@jeffjassky/telemetry 0.2.0 → 0.3.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/web.cjs CHANGED
@@ -42,6 +42,7 @@ function createClient(options) {
42
42
  storage = memoryStorage(),
43
43
  consent = () => true,
44
44
  errorName = "error.unhandled",
45
+ beforeSend,
45
46
  onError = () => {
46
47
  }
47
48
  } = options;
@@ -71,7 +72,17 @@ function createClient(options) {
71
72
  let flushing = null;
72
73
  let timer;
73
74
  const enqueue = (rec) => {
74
- queue.push(rec);
75
+ let out = rec;
76
+ if (beforeSend) {
77
+ try {
78
+ out = beforeSend(rec);
79
+ } catch (e) {
80
+ onError(e);
81
+ return;
82
+ }
83
+ }
84
+ if (!out) return;
85
+ queue.push(out);
75
86
  while (queue.length > maxQueueSize) {
76
87
  queue.shift();
77
88
  dropped++;
@@ -271,8 +282,27 @@ var privacySignalsAllow = () => {
271
282
  if (typeof window !== "undefined" && window.doNotTrack === "1") return false;
272
283
  return true;
273
284
  };
285
+ var BENIGN_BROWSER_ERRORS = [
286
+ /ResizeObserver loop (?:completed with undelivered notifications|limit exceeded)/
287
+ ];
274
288
  function createWebTelemetry(opts) {
275
- const { consent = () => true, captureGlobalErrors = true, clientContext, ...rest } = opts;
289
+ const {
290
+ consent = () => true,
291
+ captureGlobalErrors = true,
292
+ ignoreErrors,
293
+ captureBenignErrors = false,
294
+ clientContext,
295
+ ...rest
296
+ } = opts;
297
+ const patterns = [...captureBenignErrors ? [] : BENIGN_BROWSER_ERRORS, ...ignoreErrors ?? []];
298
+ const hostBeforeSend = rest.beforeSend;
299
+ const beforeSend = (rec) => {
300
+ const message = rec.error?.message;
301
+ if (message && patterns.some((p) => typeof p === "string" ? message.includes(p) : p.test(message))) {
302
+ return null;
303
+ }
304
+ return hostBeforeSend ? hostBeforeSend(rec) : rec;
305
+ };
276
306
  const storage = typeof localStorage === "undefined" ? void 0 : {
277
307
  get: (k) => {
278
308
  try {
@@ -290,6 +320,7 @@ function createWebTelemetry(opts) {
290
320
  };
291
321
  const client = createClient({
292
322
  ...rest,
323
+ beforeSend,
293
324
  storage,
294
325
  clientContext: { ...browserContext(), appVersion: opts.release ?? "unknown", ...clientContext },
295
326
  consent: () => privacySignalsAllow() && consent()
@@ -329,6 +360,7 @@ function createWebTelemetry(opts) {
329
360
  return client;
330
361
  }
331
362
 
363
+ exports.BENIGN_BROWSER_ERRORS = BENIGN_BROWSER_ERRORS;
332
364
  exports.createClient = createClient;
333
365
  exports.createWebTelemetry = createWebTelemetry;
334
366
  //# sourceMappingURL=web.cjs.map
package/dist/web.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/core.ts","../src/client/web.ts"],"names":["uuidv7"],"mappings":"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","file":"web.cjs","sourcesContent":["import { uuidv7 } from 'uuidv7';\n\ndeclare const window: unknown;\n\n/**\n * The isomorphic SDK core (instrumentation §6) — ~80% of every client by\n * value. Zero heavy dependencies; platform entries are thin wiring over this.\n *\n * The wire contract it upholds:\n * - every record carries a client-generated UUIDv7 `_id` (the idempotency\n * contract — transports are at-least-once)\n * - trace ids are UUIDv7 too: schema §2.6 samples on the random hex tail\n * - batch context carries subjects/actor once, not per record\n * - `identify()` swaps subjects AND posts one `$identify` control record —\n * the input to the server-side stitching job\n */\n\nexport const newId = uuidv7;\n\nexport interface ClientContextInput {\n platform?: string;\n appVersion?: string;\n userAgent?: string;\n os?: string;\n osVersion?: string;\n browser?: string;\n browserVersion?: string;\n deviceType?: string;\n locale?: string;\n timezone?: string;\n screenW?: number;\n screenH?: number;\n viewportW?: number;\n viewportH?: number;\n connection?: string;\n online?: boolean;\n}\n\nexport interface TransportResult {\n ok: boolean;\n status?: number;\n}\n\nexport type Transport = (url: string, body: string, headers: Record<string, string>) => Promise<TransportResult>;\n\nexport interface ClientStorage {\n get(key: string): string | null | undefined;\n set(key: string, value: string): void;\n}\n\nexport interface WireRecord {\n _id: string;\n name: string;\n occurredAt: string;\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n body?: string;\n severity?: string;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n actor?: string;\n traceId?: string;\n spanId?: string;\n parentId?: string;\n durationMs?: number;\n error?: unknown;\n state?: unknown;\n usage?: unknown;\n anonRef?: string;\n userRef?: string;\n}\n\nexport interface CreateClientOptions {\n /** pk_ for anything shipped to users; sk_ only in trusted processes */\n key: string;\n /** the mounted ingest endpoint, e.g. https://app.example.com/telemetry/ingest */\n url: string;\n /** injected at build; the key supplies service/env */\n release?: string;\n flushIntervalMs?: number;\n /** records per POST */\n maxBatchSize?: number;\n /** ring buffer cap — drop-OLDEST beyond it; new telemetry outranks stale */\n maxQueueSize?: number;\n /** failed flushes retry with backoff; a batch is abandoned after this many */\n maxRetries?: number;\n transport?: Transport;\n /** persists the anon id (and lets platforms persist queues); memory default */\n storage?: ClientStorage;\n clientContext?: ClientContextInput;\n /** false = drop instead of send. Web adapter wires DNT/GPC/consent here. */\n consent?: () => boolean;\n /** registry name used by captureError. Convention: 'error.unhandled'. */\n errorName?: string;\n /** SDK-internal failures — never thrown at the app */\n onError?: (e: unknown) => void;\n}\n\nexport interface Span {\n readonly traceId: string;\n readonly spanId: string;\n end(extra?: { attrs?: Record<string, string>; metrics?: Record<string, number> }): void;\n}\n\nconst SDK = { name: '@jeffjassky/telemetry', version: '0' };\n\nconst defaultTransport: Transport = async (url, body, headers) => {\n const res = await fetch(url, { method: 'POST', headers, body, keepalive: true });\n return { ok: res.ok, status: res.status };\n};\n\nconst memoryStorage = (): ClientStorage => {\n const m = new Map<string, string>();\n return { get: (k) => m.get(k), set: (k, v) => void m.set(k, v) };\n};\n\n/** stable, cheap error grouping — server-side fingerprinting can refine later */\nconst fingerprint = (type: string, message: string, frame: string): string => {\n const s = `${type}|${message.replace(/\\d+/g, 'N').slice(0, 200)}|${frame}`;\n let h = 5381;\n for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;\n return h.toString(16);\n};\n\nconst FRAME_RE = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+))\\)?\\s*$/;\n\nconst parseFrames = (stack: string | undefined) =>\n (stack ?? '')\n .split('\\n')\n .slice(1, 21)\n .map((line) => FRAME_RE.exec(line))\n .filter((m): m is RegExpExecArray => !!m)\n .map((m) => ({\n fn: m[1],\n filename: m[2],\n lineno: Number(m[3]),\n colno: Number(m[4]),\n }));\n\nexport function createClient(options: CreateClientOptions) {\n const {\n key,\n url,\n release,\n flushIntervalMs = 5000,\n maxBatchSize = 50,\n maxQueueSize = 1000,\n maxRetries = 5,\n transport = defaultTransport,\n storage = memoryStorage(),\n consent = () => true,\n errorName = 'error.unhandled',\n onError = () => {},\n } = options;\n\n // ClientContext must always be storable — platform/appVersion are required\n // by the envelope, so the core guarantees them and adapters refine them\n const clientContext = {\n platform: typeof window === 'undefined' ? 'server' : 'web',\n appVersion: release ?? 'unknown',\n ...options.clientContext,\n };\n\n // ── identity context ──\n let anonId = storage.get('telemetry_anon') ?? null;\n if (!anonId) {\n anonId = `anon_${newId()}`;\n try {\n storage.set('telemetry_anon', anonId);\n } catch (e) {\n onError(e);\n }\n }\n const sessionId = `ses_${newId()}`;\n /** current subjects, by type. anon + session ride every batch (schema §2.3). */\n const subjects = new Map<string, string>([\n ['anon', anonId],\n ['session', sessionId],\n ]);\n let actor: string | undefined;\n\n // ── queue ──\n const queue: WireRecord[] = [];\n let dropped = 0;\n let attempts = 0;\n let flushing: Promise<void> | null = null;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const enqueue = (rec: WireRecord) => {\n queue.push(rec);\n while (queue.length > maxQueueSize) {\n queue.shift(); // drop-oldest: fresh telemetry outranks stale\n dropped++;\n }\n };\n\n // ── tracing ──\n const spanStack: Array<{ traceId: string; spanId: string }> = [];\n const activeTrace = () => spanStack[spanStack.length - 1];\n\n const doFlush = async (): Promise<void> => {\n if (!queue.length) return;\n if (!consent()) {\n queue.length = 0; // no consent = no send AND no hoard\n return;\n }\n while (queue.length) {\n const batch = queue.slice(0, maxBatchSize);\n const body = JSON.stringify({\n sdk: SDK,\n sentAt: new Date().toISOString(),\n release,\n client: clientContext,\n context: {\n subjects: [...subjects].map(([type, id]) => ({ type, id })),\n ...(actor ? { actor } : {}),\n },\n records: batch,\n });\n try {\n const res = await transport(url, body, {\n 'content-type': 'application/json',\n authorization: `Bearer ${key}`,\n });\n if (!res.ok) throw new Error(`ingest ${res.status}`);\n queue.splice(0, batch.length);\n attempts = 0;\n } catch (e) {\n // records stay queued; the SAME _ids retry, so the server dedupes\n attempts++;\n if (attempts >= maxRetries) {\n queue.splice(0, batch.length);\n dropped += batch.length;\n attempts = 0;\n }\n onError(e);\n return; // back off until the next interval\n }\n }\n };\n\n const flush = () => (flushing ??= doFlush().finally(() => (flushing = null)));\n\n if (flushIntervalMs > 0) {\n timer = setInterval(() => void flush(), flushIntervalMs);\n (timer as { unref?: () => void }).unref?.(); // never hold a node process open\n }\n\n const client = {\n track(\n name: string,\n opts: {\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n occurredAt?: Date;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n severity?: string;\n } = {},\n ) {\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name,\n occurredAt: (opts.occurredAt ?? new Date()).toISOString(),\n ...(opts.attrs ? { attrs: opts.attrs } : {}),\n ...(opts.metrics ? { metrics: opts.metrics } : {}),\n ...(opts.data ? { data: opts.data } : {}),\n ...(opts.subjects ? { subjects: opts.subjects } : {}),\n ...(opts.severity ? { severity: opts.severity } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n });\n },\n\n captureError(err: unknown, ctx: { handled?: boolean; name?: string; attrs?: Record<string, string> } = {}) {\n const e = err instanceof Error ? err : new Error(String(err));\n const frames = parseFrames(e.stack);\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name: ctx.name ?? errorName,\n occurredAt: new Date().toISOString(),\n severity: 'error',\n ...(ctx.attrs ? { attrs: ctx.attrs } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n error: {\n type: e.name || 'Error',\n message: e.message,\n handled: ctx.handled ?? true,\n fingerprint: fingerprint(e.name, e.message, frames[0]?.filename ?? ''),\n frames,\n },\n });\n },\n\n startSpan(name: string, opts: { attrs?: Record<string, string> } = {}): Span {\n const parent = activeTrace();\n const traceId = parent?.traceId ?? newId();\n const spanId = newId();\n const startedAt = Date.now();\n const frame = { traceId, spanId };\n spanStack.push(frame);\n let ended = false;\n return {\n traceId,\n spanId,\n end: (extra = {}) => {\n if (ended) return;\n ended = true;\n const i = spanStack.indexOf(frame);\n if (i >= 0) spanStack.splice(i, 1);\n enqueue({\n _id: newId(),\n name,\n occurredAt: new Date(startedAt).toISOString(),\n traceId,\n spanId,\n ...(parent ? { parentId: parent.spanId } : {}),\n durationMs: Date.now() - startedAt,\n ...(opts.attrs || extra.attrs ? { attrs: { ...opts.attrs, ...extra.attrs } } : {}),\n ...(extra.metrics ? { metrics: extra.metrics } : {}),\n });\n },\n };\n },\n\n state(name: string, st: { key: string; from?: string; to: string; previousSinceMs?: number }) {\n enqueue({ _id: newId(), name, occurredAt: new Date().toISOString(), state: st });\n },\n\n /**\n * Swap in the real identity. Future batches carry the new subjects; one\n * `$identify` control record links anon → user for the stitching job.\n * The anon subject stays on the context — stitching consumes the alias.\n */\n identify(ids: Record<string, string | null | undefined>) {\n const hadUser = subjects.get('user');\n for (const [type, id] of Object.entries(ids)) {\n if (id == null) subjects.delete(type);\n else subjects.set(type, String(id));\n }\n const user = subjects.get('user');\n if (user && user !== hadUser && anonId) {\n enqueue({\n _id: newId(),\n name: '$identify',\n occurredAt: new Date().toISOString(),\n anonRef: `anon:${anonId}`,\n userRef: `user:${user}`,\n });\n }\n if (subjects.has('user')) actor = `user:${subjects.get('user')}`;\n },\n\n setActor(ref: string | undefined) {\n actor = ref;\n },\n\n flush,\n\n async shutdown() {\n if (timer) clearInterval(timer);\n await flush();\n },\n\n /** introspection for adapters and tests — not a public contract */\n _internal: {\n queue,\n subjects,\n get anonId() {\n return anonId!;\n },\n sessionId,\n get dropped() {\n return dropped;\n },\n enqueue,\n },\n };\n\n return client;\n}\n\nexport type TelemetryClient = ReturnType<typeof createClient>;\n\n// The isomorphic registry surface (instrumentation §8): the host's registry\n// file imports defineRegistry from '/core' so server AND clients can import\n// it — server at runtime, clients `import type` only, so zod never reaches a\n// bundle. This chain must stay mongoose/express-free.\nexport { defineRegistry, boundedMeta } from '../server/registry.js';\nexport type { Registry, EventSpec, RollupSpec, DimSource } from '../server/registry.js';\n","import { createClient, type CreateClientOptions, type TelemetryClient } from './core.js';\n\n/**\n * Browser wiring (instrumentation §7): ClientContext capture, global error\n * hooks, consent/DNT/GPC gate, unload flush via sendBeacon. ~wiring, not\n * behavior — anything smarter belongs in core.\n */\n\ndeclare const window: any;\ndeclare const navigator: any;\ndeclare const document: any;\ndeclare const screen: any;\ndeclare const localStorage: any;\n\nconst browserContext = () => {\n const nav = typeof navigator === 'undefined' ? {} : navigator;\n const scr = typeof screen === 'undefined' ? {} : screen;\n const win = typeof window === 'undefined' ? {} : window;\n return {\n platform: 'web',\n appVersion: 'unknown', // overridden by opts.release / clientContext\n userAgent: nav.userAgent,\n locale: nav.language,\n timezone: (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {\n return undefined;\n }\n })(),\n screenW: scr.width,\n screenH: scr.height,\n viewportW: win.innerWidth,\n viewportH: win.innerHeight,\n connection: nav.connection?.effectiveType,\n online: nav.onLine,\n };\n};\n\n/** DNT/GPC are hard signals — a host consent callback can only narrow further */\nconst privacySignalsAllow = () => {\n if (typeof navigator === 'undefined') return true;\n if (navigator.doNotTrack === '1' || navigator.globalPrivacyControl === true) return false;\n if (typeof window !== 'undefined' && window.doNotTrack === '1') return false;\n return true;\n};\n\nexport interface WebTelemetryOptions extends Omit<CreateClientOptions, 'storage' | 'consent'> {\n /** host consent, e.g. a cookie-banner check. ANDed with DNT/GPC. */\n consent?: () => boolean;\n /** auto-capture window.onerror / unhandledrejection (default true) */\n captureGlobalErrors?: boolean;\n}\n\nexport function createWebTelemetry(opts: WebTelemetryOptions): TelemetryClient {\n const { consent = () => true, captureGlobalErrors = true, clientContext, ...rest } = opts;\n\n const storage =\n typeof localStorage === 'undefined'\n ? undefined\n : {\n get: (k: string) => {\n try {\n return localStorage.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k: string, v: string) => {\n try {\n localStorage.setItem(k, v);\n } catch {}\n },\n };\n\n const client = createClient({\n ...rest,\n storage,\n clientContext: { ...browserContext(), appVersion: opts.release ?? 'unknown', ...clientContext },\n consent: () => privacySignalsAllow() && consent(),\n });\n\n if (typeof window !== 'undefined') {\n if (captureGlobalErrors) {\n window.addEventListener('error', (ev: any) => {\n client.captureError(ev.error ?? ev.message, { handled: false });\n });\n window.addEventListener('unhandledrejection', (ev: any) => {\n client.captureError(ev.reason, { handled: false });\n });\n }\n\n // Unload flush: sendBeacon carries the batch (no headers possible, so the\n // pk_ key rides the query string — the server accepts that for pk_ only).\n const beaconFlush = () => {\n const q = client._internal.queue;\n if (!q.length || typeof navigator === 'undefined' || !navigator.sendBeacon) return;\n const body = JSON.stringify({\n sdk: { name: '@jeffjassky/telemetry', version: '0' },\n sentAt: new Date().toISOString(),\n release: opts.release,\n client: { ...browserContext(), appVersion: opts.release ?? 'unknown' },\n context: {\n subjects: [...client._internal.subjects].map(([type, id]) => ({ type, id })),\n },\n records: q.slice(0, 100),\n });\n const sep = opts.url.includes('?') ? '&' : '?';\n if (navigator.sendBeacon(`${opts.url}${sep}key=${encodeURIComponent(opts.key)}`, body)) {\n q.length = 0; // retries would duplicate _ids anyway; the server dedupes\n }\n };\n window.addEventListener('pagehide', beaconFlush);\n document?.addEventListener?.('visibilitychange', () => {\n if (document.visibilityState === 'hidden') beaconFlush();\n });\n }\n\n return client;\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
1
+ {"version":3,"sources":["../src/client/core.ts","../src/client/web.ts"],"names":["uuidv7"],"mappings":"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","file":"web.cjs","sourcesContent":["import { uuidv7 } from 'uuidv7';\n\ndeclare const window: unknown;\n\n/**\n * The isomorphic SDK core (instrumentation §6) — ~80% of every client by\n * value. Zero heavy dependencies; platform entries are thin wiring over this.\n *\n * The wire contract it upholds:\n * - every record carries a client-generated UUIDv7 `_id` (the idempotency\n * contract — transports are at-least-once)\n * - trace ids are UUIDv7 too: schema §2.6 samples on the random hex tail\n * - batch context carries subjects/actor once, not per record\n * - `identify()` swaps subjects AND posts one `$identify` control record —\n * the input to the server-side stitching job\n */\n\nexport const newId = uuidv7;\n\nexport interface ClientContextInput {\n platform?: string;\n appVersion?: string;\n userAgent?: string;\n os?: string;\n osVersion?: string;\n browser?: string;\n browserVersion?: string;\n deviceType?: string;\n locale?: string;\n timezone?: string;\n screenW?: number;\n screenH?: number;\n viewportW?: number;\n viewportH?: number;\n connection?: string;\n online?: boolean;\n}\n\nexport interface TransportResult {\n ok: boolean;\n status?: number;\n}\n\nexport type Transport = (url: string, body: string, headers: Record<string, string>) => Promise<TransportResult>;\n\nexport interface ClientStorage {\n get(key: string): string | null | undefined;\n set(key: string, value: string): void;\n}\n\nexport interface WireRecord {\n _id: string;\n name: string;\n occurredAt: string;\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n body?: string;\n severity?: string;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n actor?: string;\n traceId?: string;\n spanId?: string;\n parentId?: string;\n durationMs?: number;\n error?: unknown;\n state?: unknown;\n usage?: unknown;\n anonRef?: string;\n userRef?: string;\n}\n\nexport interface CreateClientOptions {\n /** pk_ for anything shipped to users; sk_ only in trusted processes */\n key: string;\n /** the mounted ingest endpoint, e.g. https://app.example.com/telemetry/ingest */\n url: string;\n /** injected at build; the key supplies service/env */\n release?: string;\n flushIntervalMs?: number;\n /** records per POST */\n maxBatchSize?: number;\n /** ring buffer cap — drop-OLDEST beyond it; new telemetry outranks stale */\n maxQueueSize?: number;\n /** failed flushes retry with backoff; a batch is abandoned after this many */\n maxRetries?: number;\n transport?: Transport;\n /** persists the anon id (and lets platforms persist queues); memory default */\n storage?: ClientStorage;\n clientContext?: ClientContextInput;\n /** false = drop instead of send. Web adapter wires DNT/GPC/consent here. */\n consent?: () => boolean;\n /** registry name used by captureError. Convention: 'error.unhandled'. */\n errorName?: string;\n /**\n * Last gate before a record joins the queue — the one place every kind\n * (event, error, span, state, usage) passes through. Return the record to\n * keep it, a modified copy to redact it, or `null` to drop it silently.\n *\n * This is where noise dies. A host that filters at the platform level\n * instead — swallowing `window.onerror` before the SDK sees it — has to win\n * a listener-registration race to do it, which is a real footgun; the whole\n * point of putting the hook here is that the host never has to think about\n * ordering.\n *\n * A throwing hook DROPS the record and reports via `onError`. Fail-closed is\n * deliberate: the common use is redaction, and a half-applied redaction that\n * still ships is worse than a lost record.\n */\n beforeSend?: (rec: WireRecord) => WireRecord | null | undefined | void;\n /** SDK-internal failures — never thrown at the app */\n onError?: (e: unknown) => void;\n}\n\nexport interface Span {\n readonly traceId: string;\n readonly spanId: string;\n end(extra?: { attrs?: Record<string, string>; metrics?: Record<string, number> }): void;\n}\n\nconst SDK = { name: '@jeffjassky/telemetry', version: '0' };\n\nconst defaultTransport: Transport = async (url, body, headers) => {\n const res = await fetch(url, { method: 'POST', headers, body, keepalive: true });\n return { ok: res.ok, status: res.status };\n};\n\nconst memoryStorage = (): ClientStorage => {\n const m = new Map<string, string>();\n return { get: (k) => m.get(k), set: (k, v) => void m.set(k, v) };\n};\n\n/** stable, cheap error grouping — server-side fingerprinting can refine later */\nconst fingerprint = (type: string, message: string, frame: string): string => {\n const s = `${type}|${message.replace(/\\d+/g, 'N').slice(0, 200)}|${frame}`;\n let h = 5381;\n for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;\n return h.toString(16);\n};\n\nconst FRAME_RE = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+))\\)?\\s*$/;\n\nconst parseFrames = (stack: string | undefined) =>\n (stack ?? '')\n .split('\\n')\n .slice(1, 21)\n .map((line) => FRAME_RE.exec(line))\n .filter((m): m is RegExpExecArray => !!m)\n .map((m) => ({\n fn: m[1],\n filename: m[2],\n lineno: Number(m[3]),\n colno: Number(m[4]),\n }));\n\nexport function createClient(options: CreateClientOptions) {\n const {\n key,\n url,\n release,\n flushIntervalMs = 5000,\n maxBatchSize = 50,\n maxQueueSize = 1000,\n maxRetries = 5,\n transport = defaultTransport,\n storage = memoryStorage(),\n consent = () => true,\n errorName = 'error.unhandled',\n beforeSend,\n onError = () => {},\n } = options;\n\n // ClientContext must always be storable — platform/appVersion are required\n // by the envelope, so the core guarantees them and adapters refine them\n const clientContext = {\n platform: typeof window === 'undefined' ? 'server' : 'web',\n appVersion: release ?? 'unknown',\n ...options.clientContext,\n };\n\n // ── identity context ──\n let anonId = storage.get('telemetry_anon') ?? null;\n if (!anonId) {\n anonId = `anon_${newId()}`;\n try {\n storage.set('telemetry_anon', anonId);\n } catch (e) {\n onError(e);\n }\n }\n const sessionId = `ses_${newId()}`;\n /** current subjects, by type. anon + session ride every batch (schema §2.3). */\n const subjects = new Map<string, string>([\n ['anon', anonId],\n ['session', sessionId],\n ]);\n let actor: string | undefined;\n\n // ── queue ──\n const queue: WireRecord[] = [];\n let dropped = 0;\n let attempts = 0;\n let flushing: Promise<void> | null = null;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const enqueue = (rec: WireRecord) => {\n let out: WireRecord | null | undefined | void = rec;\n if (beforeSend) {\n try {\n out = beforeSend(rec);\n } catch (e) {\n onError(e); // fail-closed: a broken filter must not leak what it was filtering\n return;\n }\n }\n if (!out) return;\n queue.push(out);\n while (queue.length > maxQueueSize) {\n queue.shift(); // drop-oldest: fresh telemetry outranks stale\n dropped++;\n }\n };\n\n // ── tracing ──\n const spanStack: Array<{ traceId: string; spanId: string }> = [];\n const activeTrace = () => spanStack[spanStack.length - 1];\n\n const doFlush = async (): Promise<void> => {\n if (!queue.length) return;\n if (!consent()) {\n queue.length = 0; // no consent = no send AND no hoard\n return;\n }\n while (queue.length) {\n const batch = queue.slice(0, maxBatchSize);\n const body = JSON.stringify({\n sdk: SDK,\n sentAt: new Date().toISOString(),\n release,\n client: clientContext,\n context: {\n subjects: [...subjects].map(([type, id]) => ({ type, id })),\n ...(actor ? { actor } : {}),\n },\n records: batch,\n });\n try {\n const res = await transport(url, body, {\n 'content-type': 'application/json',\n authorization: `Bearer ${key}`,\n });\n if (!res.ok) throw new Error(`ingest ${res.status}`);\n queue.splice(0, batch.length);\n attempts = 0;\n } catch (e) {\n // records stay queued; the SAME _ids retry, so the server dedupes\n attempts++;\n if (attempts >= maxRetries) {\n queue.splice(0, batch.length);\n dropped += batch.length;\n attempts = 0;\n }\n onError(e);\n return; // back off until the next interval\n }\n }\n };\n\n const flush = () => (flushing ??= doFlush().finally(() => (flushing = null)));\n\n if (flushIntervalMs > 0) {\n timer = setInterval(() => void flush(), flushIntervalMs);\n (timer as { unref?: () => void }).unref?.(); // never hold a node process open\n }\n\n const client = {\n track(\n name: string,\n opts: {\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n occurredAt?: Date;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n severity?: string;\n } = {},\n ) {\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name,\n occurredAt: (opts.occurredAt ?? new Date()).toISOString(),\n ...(opts.attrs ? { attrs: opts.attrs } : {}),\n ...(opts.metrics ? { metrics: opts.metrics } : {}),\n ...(opts.data ? { data: opts.data } : {}),\n ...(opts.subjects ? { subjects: opts.subjects } : {}),\n ...(opts.severity ? { severity: opts.severity } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n });\n },\n\n captureError(err: unknown, ctx: { handled?: boolean; name?: string; attrs?: Record<string, string> } = {}) {\n const e = err instanceof Error ? err : new Error(String(err));\n const frames = parseFrames(e.stack);\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name: ctx.name ?? errorName,\n occurredAt: new Date().toISOString(),\n severity: 'error',\n ...(ctx.attrs ? { attrs: ctx.attrs } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n error: {\n type: e.name || 'Error',\n message: e.message,\n handled: ctx.handled ?? true,\n fingerprint: fingerprint(e.name, e.message, frames[0]?.filename ?? ''),\n frames,\n },\n });\n },\n\n startSpan(name: string, opts: { attrs?: Record<string, string> } = {}): Span {\n const parent = activeTrace();\n const traceId = parent?.traceId ?? newId();\n const spanId = newId();\n const startedAt = Date.now();\n const frame = { traceId, spanId };\n spanStack.push(frame);\n let ended = false;\n return {\n traceId,\n spanId,\n end: (extra = {}) => {\n if (ended) return;\n ended = true;\n const i = spanStack.indexOf(frame);\n if (i >= 0) spanStack.splice(i, 1);\n enqueue({\n _id: newId(),\n name,\n occurredAt: new Date(startedAt).toISOString(),\n traceId,\n spanId,\n ...(parent ? { parentId: parent.spanId } : {}),\n durationMs: Date.now() - startedAt,\n ...(opts.attrs || extra.attrs ? { attrs: { ...opts.attrs, ...extra.attrs } } : {}),\n ...(extra.metrics ? { metrics: extra.metrics } : {}),\n });\n },\n };\n },\n\n state(name: string, st: { key: string; from?: string; to: string; previousSinceMs?: number }) {\n enqueue({ _id: newId(), name, occurredAt: new Date().toISOString(), state: st });\n },\n\n /**\n * Swap in the real identity. Future batches carry the new subjects; one\n * `$identify` control record links anon → user for the stitching job.\n * The anon subject stays on the context — stitching consumes the alias.\n */\n identify(ids: Record<string, string | null | undefined>) {\n const hadUser = subjects.get('user');\n for (const [type, id] of Object.entries(ids)) {\n if (id == null) subjects.delete(type);\n else subjects.set(type, String(id));\n }\n const user = subjects.get('user');\n if (user && user !== hadUser && anonId) {\n enqueue({\n _id: newId(),\n name: '$identify',\n occurredAt: new Date().toISOString(),\n anonRef: `anon:${anonId}`,\n userRef: `user:${user}`,\n });\n }\n if (subjects.has('user')) actor = `user:${subjects.get('user')}`;\n },\n\n setActor(ref: string | undefined) {\n actor = ref;\n },\n\n flush,\n\n async shutdown() {\n if (timer) clearInterval(timer);\n await flush();\n },\n\n /** introspection for adapters and tests — not a public contract */\n _internal: {\n queue,\n subjects,\n get anonId() {\n return anonId!;\n },\n sessionId,\n get dropped() {\n return dropped;\n },\n enqueue,\n },\n };\n\n return client;\n}\n\nexport type TelemetryClient = ReturnType<typeof createClient>;\n\n// The isomorphic registry surface (instrumentation §8): the host's registry\n// file imports defineRegistry from '/core' so server AND clients can import\n// it — server at runtime, clients `import type` only, so zod never reaches a\n// bundle. This chain must stay mongoose/express-free.\nexport { defineRegistry, boundedMeta } from '../server/registry.js';\nexport type { Registry, EventSpec, RollupSpec, DimSource } from '../server/registry.js';\n","import { createClient, type CreateClientOptions, type TelemetryClient, type WireRecord } from './core.js';\n\n/**\n * Browser wiring (instrumentation §7): ClientContext capture, global error\n * hooks, consent/DNT/GPC gate, unload flush via sendBeacon. ~wiring, not\n * behavior — anything smarter belongs in core.\n */\n\ndeclare const window: any;\ndeclare const navigator: any;\ndeclare const document: any;\ndeclare const screen: any;\ndeclare const localStorage: any;\n\nconst browserContext = () => {\n const nav = typeof navigator === 'undefined' ? {} : navigator;\n const scr = typeof screen === 'undefined' ? {} : screen;\n const win = typeof window === 'undefined' ? {} : window;\n return {\n platform: 'web',\n appVersion: 'unknown', // overridden by opts.release / clientContext\n userAgent: nav.userAgent,\n locale: nav.language,\n timezone: (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {\n return undefined;\n }\n })(),\n screenW: scr.width,\n screenH: scr.height,\n viewportW: win.innerWidth,\n viewportH: win.innerHeight,\n connection: nav.connection?.effectiveType,\n online: nav.onLine,\n };\n};\n\n/** DNT/GPC are hard signals — a host consent callback can only narrow further */\nconst privacySignalsAllow = () => {\n if (typeof navigator === 'undefined') return true;\n if (navigator.doNotTrack === '1' || navigator.globalPrivacyControl === true) return false;\n if (typeof window !== 'undefined' && window.doNotTrack === '1') return false;\n return true;\n};\n\n/**\n * Errors the browser raises that are not failures.\n *\n * `ResizeObserver loop completed with undelivered notifications` is the whole\n * list for now, and it earns its place: Chrome raises it as an *uncaught\n * error* whenever an observer callback dirties layout and the next batch\n * lands a frame later. Nothing is broken, nothing is actionable, and it is\n * emitted often enough to bury real errors — one production host saw 49 of\n * its last 50 error records come from this one message.\n *\n * Filtered by default because the alternative is every host discovering it\n * separately. Opt back in with `captureBenignErrors: true`.\n */\nexport const BENIGN_BROWSER_ERRORS: readonly RegExp[] = [\n /ResizeObserver loop (?:completed with undelivered notifications|limit exceeded)/,\n];\n\nexport interface WebTelemetryOptions extends Omit<CreateClientOptions, 'storage' | 'consent'> {\n /** host consent, e.g. a cookie-banner check. ANDed with DNT/GPC. */\n consent?: () => boolean;\n /** auto-capture window.onerror / unhandledrejection (default true) */\n captureGlobalErrors?: boolean;\n /**\n * Drop error records whose message matches. Strings match by substring,\n * RegExp by test. ADDED to `BENIGN_BROWSER_ERRORS`, not replacing it.\n */\n ignoreErrors?: Array<string | RegExp>;\n /** keep the `BENIGN_BROWSER_ERRORS` records instead of dropping them */\n captureBenignErrors?: boolean;\n}\n\nexport function createWebTelemetry(opts: WebTelemetryOptions): TelemetryClient {\n const {\n consent = () => true,\n captureGlobalErrors = true,\n ignoreErrors,\n captureBenignErrors = false,\n clientContext,\n ...rest\n } = opts;\n\n // Message filtering rides core's `beforeSend` rather than the global error\n // listeners: the listeners are not the only way a record is born\n // (`captureError()` calls from app code are), and a host-supplied\n // `beforeSend` must still get its turn on everything that survives.\n const patterns = [...(captureBenignErrors ? [] : BENIGN_BROWSER_ERRORS), ...(ignoreErrors ?? [])];\n const hostBeforeSend = rest.beforeSend;\n const beforeSend = (rec: WireRecord) => {\n const message = (rec.error as { message?: string } | undefined)?.message;\n if (\n message &&\n patterns.some((p) => (typeof p === 'string' ? message.includes(p) : p.test(message)))\n ) {\n return null;\n }\n return hostBeforeSend ? hostBeforeSend(rec) : rec;\n };\n\n const storage =\n typeof localStorage === 'undefined'\n ? undefined\n : {\n get: (k: string) => {\n try {\n return localStorage.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k: string, v: string) => {\n try {\n localStorage.setItem(k, v);\n } catch {}\n },\n };\n\n const client = createClient({\n ...rest,\n beforeSend,\n storage,\n clientContext: { ...browserContext(), appVersion: opts.release ?? 'unknown', ...clientContext },\n consent: () => privacySignalsAllow() && consent(),\n });\n\n if (typeof window !== 'undefined') {\n if (captureGlobalErrors) {\n window.addEventListener('error', (ev: any) => {\n client.captureError(ev.error ?? ev.message, { handled: false });\n });\n window.addEventListener('unhandledrejection', (ev: any) => {\n client.captureError(ev.reason, { handled: false });\n });\n }\n\n // Unload flush: sendBeacon carries the batch (no headers possible, so the\n // pk_ key rides the query string — the server accepts that for pk_ only).\n const beaconFlush = () => {\n const q = client._internal.queue;\n if (!q.length || typeof navigator === 'undefined' || !navigator.sendBeacon) return;\n const body = JSON.stringify({\n sdk: { name: '@jeffjassky/telemetry', version: '0' },\n sentAt: new Date().toISOString(),\n release: opts.release,\n client: { ...browserContext(), appVersion: opts.release ?? 'unknown' },\n context: {\n subjects: [...client._internal.subjects].map(([type, id]) => ({ type, id })),\n },\n records: q.slice(0, 100),\n });\n const sep = opts.url.includes('?') ? '&' : '?';\n if (navigator.sendBeacon(`${opts.url}${sep}key=${encodeURIComponent(opts.key)}`, body)) {\n q.length = 0; // retries would duplicate _ids anyway; the server dedupes\n }\n };\n window.addEventListener('pagehide', beaconFlush);\n document?.addEventListener?.('visibilitychange', () => {\n if (document.visibilityState === 'hidden') beaconFlush();\n });\n }\n\n return client;\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
package/dist/web.js CHANGED
@@ -40,6 +40,7 @@ function createClient(options) {
40
40
  storage = memoryStorage(),
41
41
  consent = () => true,
42
42
  errorName = "error.unhandled",
43
+ beforeSend,
43
44
  onError = () => {
44
45
  }
45
46
  } = options;
@@ -69,7 +70,17 @@ function createClient(options) {
69
70
  let flushing = null;
70
71
  let timer;
71
72
  const enqueue = (rec) => {
72
- queue.push(rec);
73
+ let out = rec;
74
+ if (beforeSend) {
75
+ try {
76
+ out = beforeSend(rec);
77
+ } catch (e) {
78
+ onError(e);
79
+ return;
80
+ }
81
+ }
82
+ if (!out) return;
83
+ queue.push(out);
73
84
  while (queue.length > maxQueueSize) {
74
85
  queue.shift();
75
86
  dropped++;
@@ -269,8 +280,27 @@ var privacySignalsAllow = () => {
269
280
  if (typeof window !== "undefined" && window.doNotTrack === "1") return false;
270
281
  return true;
271
282
  };
283
+ var BENIGN_BROWSER_ERRORS = [
284
+ /ResizeObserver loop (?:completed with undelivered notifications|limit exceeded)/
285
+ ];
272
286
  function createWebTelemetry(opts) {
273
- const { consent = () => true, captureGlobalErrors = true, clientContext, ...rest } = opts;
287
+ const {
288
+ consent = () => true,
289
+ captureGlobalErrors = true,
290
+ ignoreErrors,
291
+ captureBenignErrors = false,
292
+ clientContext,
293
+ ...rest
294
+ } = opts;
295
+ const patterns = [...captureBenignErrors ? [] : BENIGN_BROWSER_ERRORS, ...ignoreErrors ?? []];
296
+ const hostBeforeSend = rest.beforeSend;
297
+ const beforeSend = (rec) => {
298
+ const message = rec.error?.message;
299
+ if (message && patterns.some((p) => typeof p === "string" ? message.includes(p) : p.test(message))) {
300
+ return null;
301
+ }
302
+ return hostBeforeSend ? hostBeforeSend(rec) : rec;
303
+ };
274
304
  const storage = typeof localStorage === "undefined" ? void 0 : {
275
305
  get: (k) => {
276
306
  try {
@@ -288,6 +318,7 @@ function createWebTelemetry(opts) {
288
318
  };
289
319
  const client = createClient({
290
320
  ...rest,
321
+ beforeSend,
291
322
  storage,
292
323
  clientContext: { ...browserContext(), appVersion: opts.release ?? "unknown", ...clientContext },
293
324
  consent: () => privacySignalsAllow() && consent()
@@ -327,6 +358,6 @@ function createWebTelemetry(opts) {
327
358
  return client;
328
359
  }
329
360
 
330
- export { createClient, createWebTelemetry };
361
+ export { BENIGN_BROWSER_ERRORS, createClient, createWebTelemetry };
331
362
  //# sourceMappingURL=web.js.map
332
363
  //# sourceMappingURL=web.js.map
package/dist/web.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/core.ts","../src/client/web.ts"],"names":["uuidv7"],"mappings":";;;;;;AAiBO,IAAM,KAAA,GAAQA,MAAAA;AAuFrB,IAAM,GAAA,GAAM,EAAE,IAAA,EAAM,uBAAA,EAAyB,SAAS,GAAA,EAAI;AAE1D,IAAM,gBAAA,GAA8B,OAAO,GAAA,EAAK,IAAA,EAAM,OAAA,KAAY;AAChE,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,IAAA,EAAM,CAAA;AAC/E,EAAA,OAAO,EAAE,EAAA,EAAI,GAAA,CAAI,EAAA,EAAI,MAAA,EAAQ,IAAI,MAAA,EAAO;AAC1C,CAAA;AAEA,IAAM,gBAAgB,MAAqB;AACzC,EAAA,MAAM,CAAA,uBAAQ,GAAA,EAAoB;AAClC,EAAA,OAAO,EAAE,GAAA,EAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAC,CAAA,EAAG,GAAA,EAAK,CAAC,GAAG,CAAA,KAAM,KAAK,EAAE,GAAA,CAAI,CAAA,EAAG,CAAC,CAAA,EAAE;AACjE,CAAA;AAGA,IAAM,WAAA,GAAc,CAAC,IAAA,EAAc,OAAA,EAAiB,KAAA,KAA0B;AAC5E,EAAA,MAAM,CAAA,GAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,QAAQ,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,IAAI,KAAK,CAAA,CAAA;AACxE,EAAA,IAAI,CAAA,GAAI,IAAA;AACR,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,MAAA,EAAQ,CAAA,EAAA,EAAK,CAAA,GAAA,CAAM,CAAA,IAAK,CAAA,IAAK,CAAA,GAAI,CAAA,CAAE,UAAA,CAAW,CAAC,CAAA,KAAO,CAAA;AAC5E,EAAA,OAAO,CAAA,CAAE,SAAS,EAAE,CAAA;AACtB,CAAA;AAEA,IAAM,QAAA,GAAW,sDAAA;AAEjB,IAAM,WAAA,GAAc,CAAC,KAAA,KAAA,CAClB,KAAA,IAAS,EAAA,EACP,KAAA,CAAM,IAAI,CAAA,CACV,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CACX,GAAA,CAAI,CAAC,IAAA,KAAS,QAAA,CAAS,IAAA,CAAK,IAAI,CAAC,CAAA,CACjC,MAAA,CAAO,CAAC,CAAA,KAA4B,CAAC,CAAC,CAAC,CAAA,CACvC,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,EACX,EAAA,EAAI,EAAE,CAAC,CAAA;AAAA,EACP,QAAA,EAAU,EAAE,CAAC,CAAA;AAAA,EACb,MAAA,EAAQ,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,EACnB,KAAA,EAAO,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC;AACpB,CAAA,CAAE,CAAA;AAEC,SAAS,aAAa,OAAA,EAA8B;AACzD,EAAA,MAAM;AAAA,IACJ,GAAA;AAAA,IACA,GAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA,GAAkB,GAAA;AAAA,IAClB,YAAA,GAAe,EAAA;AAAA,IACf,YAAA,GAAe,GAAA;AAAA,IACf,UAAA,GAAa,CAAA;AAAA,IACb,SAAA,GAAY,gBAAA;AAAA,IACZ,UAAU,aAAA,EAAc;AAAA,IACxB,UAAU,MAAM,IAAA;AAAA,IAChB,SAAA,GAAY,iBAAA;AAAA,IACZ,UAAU,MAAM;AAAA,IAAC;AAAA,GACnB,GAAI,OAAA;AAIJ,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,QAAA,EAAU,OAAO,MAAA,KAAW,WAAA,GAAc,QAAA,GAAW,KAAA;AAAA,IACrD,YAAY,OAAA,IAAW,SAAA;AAAA,IACvB,GAAG,OAAA,CAAQ;AAAA,GACb;AAGA,EAAA,IAAI,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,IAAK,IAAA;AAC9C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAA,GAAS,CAAA,KAAA,EAAQ,OAAO,CAAA,CAAA;AACxB,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,GAAA,CAAI,kBAAkB,MAAM,CAAA;AAAA,IACtC,SAAS,CAAA,EAAG;AACV,MAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACX;AAAA,EACF;AACA,EAAA,MAAM,SAAA,GAAY,CAAA,IAAA,EAAO,KAAA,EAAO,CAAA,CAAA;AAEhC,EAAA,MAAM,QAAA,uBAAe,GAAA,CAAoB;AAAA,IACvC,CAAC,QAAQ,MAAM,CAAA;AAAA,IACf,CAAC,WAAW,SAAS;AAAA,GACtB,CAAA;AACD,EAAA,IAAI,KAAA;AAGJ,EAAA,MAAM,QAAsB,EAAC;AAC7B,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,QAAA,GAAiC,IAAA;AACrC,EAAA,IAAI,KAAA;AAEJ,EAAA,MAAM,OAAA,GAAU,CAAC,GAAA,KAAoB;AACnC,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,IAAA,OAAO,KAAA,CAAM,SAAS,YAAA,EAAc;AAClC,MAAA,KAAA,CAAM,KAAA,EAAM;AACZ,MAAA,OAAA,EAAA;AAAA,IACF;AAAA,EACF,CAAA;AAGA,EAAA,MAAM,YAAwD,EAAC;AAC/D,EAAA,MAAM,WAAA,GAAc,MAAM,SAAA,CAAU,SAAA,CAAU,SAAS,CAAC,CAAA;AAExD,EAAA,MAAM,UAAU,YAA2B;AACzC,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACnB,IAAA,IAAI,CAAC,SAAQ,EAAG;AACd,MAAA,KAAA,CAAM,MAAA,GAAS,CAAA;AACf,MAAA;AAAA,IACF;AACA,IAAA,OAAO,MAAM,MAAA,EAAQ;AACnB,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,YAAY,CAAA;AACzC,MAAA,MAAM,IAAA,GAAO,KAAK,SAAA,CAAU;AAAA,QAC1B,GAAA,EAAK,GAAA;AAAA,QACL,MAAA,EAAA,iBAAQ,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QAC/B,OAAA;AAAA,QACA,MAAA,EAAQ,aAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,QAAA,EAAU,CAAC,GAAG,QAAQ,EAAE,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,EAAE,CAAA,MAAO,EAAE,IAAA,EAAM,IAAG,CAAE,CAAA;AAAA,UAC1D,GAAI,KAAA,GAAQ,EAAE,KAAA,KAAU;AAAC,SAC3B;AAAA,QACA,OAAA,EAAS;AAAA,OACV,CAAA;AACD,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,GAAA,EAAK,IAAA,EAAM;AAAA,UACrC,cAAA,EAAgB,kBAAA;AAAA,UAChB,aAAA,EAAe,UAAU,GAAG,CAAA;AAAA,SAC7B,CAAA;AACD,QAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,OAAA,EAAU,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACnD,QAAA,KAAA,CAAM,MAAA,CAAO,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA;AAC5B,QAAA,QAAA,GAAW,CAAA;AAAA,MACb,SAAS,CAAA,EAAG;AAEV,QAAA,QAAA,EAAA;AACA,QAAA,IAAI,YAAY,UAAA,EAAY;AAC1B,UAAA,KAAA,CAAM,MAAA,CAAO,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA;AAC5B,UAAA,OAAA,IAAW,KAAA,CAAM,MAAA;AACjB,UAAA,QAAA,GAAW,CAAA;AAAA,QACb;AACA,QAAA,OAAA,CAAQ,CAAC,CAAA;AACT,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAO,QAAA,KAAa,OAAA,GAAU,OAAA,CAAQ,MAAO,WAAW,IAAK,CAAA;AAE3E,EAAA,IAAI,kBAAkB,CAAA,EAAG;AACvB,IAAA,KAAA,GAAQ,WAAA,CAAY,MAAM,KAAK,KAAA,IAAS,eAAe,CAAA;AACvD,IAAC,MAAiC,KAAA,IAAQ;AAAA,EAC5C;AAEA,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,KAAA,CACE,IAAA,EACA,IAAA,GAOI,EAAC,EACL;AACA,MAAA,MAAM,QAAQ,WAAA,EAAY;AAC1B,MAAA,OAAA,CAAQ;AAAA,QACN,KAAK,KAAA,EAAM;AAAA,QACX,IAAA;AAAA,QACA,aAAa,IAAA,CAAK,UAAA,oBAAc,IAAI,IAAA,IAAQ,WAAA,EAAY;AAAA,QACxD,GAAI,KAAK,KAAA,GAAQ,EAAE,OAAO,IAAA,CAAK,KAAA,KAAU,EAAC;AAAA,QAC1C,GAAI,KAAK,OAAA,GAAU,EAAE,SAAS,IAAA,CAAK,OAAA,KAAY,EAAC;AAAA,QAChD,GAAI,KAAK,IAAA,GAAO,EAAE,MAAM,IAAA,CAAK,IAAA,KAAS,EAAC;AAAA,QACvC,GAAI,KAAK,QAAA,GAAW,EAAE,UAAU,IAAA,CAAK,QAAA,KAAa,EAAC;AAAA,QACnD,GAAI,KAAK,QAAA,GAAW,EAAE,UAAU,IAAA,CAAK,QAAA,KAAa,EAAC;AAAA,QACnD,GAAI,KAAA,GAAQ,EAAE,OAAA,EAAS,KAAA,CAAM,SAAS,QAAA,EAAU,KAAA,CAAM,MAAA,EAAO,GAAI;AAAC,OACnE,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,YAAA,CAAa,GAAA,EAAc,GAAA,GAA4E,EAAC,EAAG;AACzG,MAAA,MAAM,CAAA,GAAI,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAC5D,MAAA,MAAM,MAAA,GAAS,WAAA,CAAY,CAAA,CAAE,KAAK,CAAA;AAClC,MAAA,MAAM,QAAQ,WAAA,EAAY;AAC1B,MAAA,OAAA,CAAQ;AAAA,QACN,KAAK,KAAA,EAAM;AAAA,QACX,IAAA,EAAM,IAAI,IAAA,IAAQ,SAAA;AAAA,QAClB,UAAA,EAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QACnC,QAAA,EAAU,OAAA;AAAA,QACV,GAAI,IAAI,KAAA,GAAQ,EAAE,OAAO,GAAA,CAAI,KAAA,KAAU,EAAC;AAAA,QACxC,GAAI,KAAA,GAAQ,EAAE,OAAA,EAAS,KAAA,CAAM,SAAS,QAAA,EAAU,KAAA,CAAM,MAAA,EAAO,GAAI,EAAC;AAAA,QAClE,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,EAAE,IAAA,IAAQ,OAAA;AAAA,UAChB,SAAS,CAAA,CAAE,OAAA;AAAA,UACX,OAAA,EAAS,IAAI,OAAA,IAAW,IAAA;AAAA,UACxB,WAAA,EAAa,WAAA,CAAY,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,SAAS,MAAA,CAAO,CAAC,CAAA,EAAG,QAAA,IAAY,EAAE,CAAA;AAAA,UACrE;AAAA;AACF,OACD,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,SAAA,CAAU,IAAA,EAAc,IAAA,GAA2C,EAAC,EAAS;AAC3E,MAAA,MAAM,SAAS,WAAA,EAAY;AAC3B,MAAA,MAAM,OAAA,GAAU,MAAA,EAAQ,OAAA,IAAW,KAAA,EAAM;AACzC,MAAA,MAAM,SAAS,KAAA,EAAM;AACrB,MAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,MAAA,MAAM,KAAA,GAAQ,EAAE,OAAA,EAAS,MAAA,EAAO;AAChC,MAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AACpB,MAAA,IAAI,KAAA,GAAQ,KAAA;AACZ,MAAA,OAAO;AAAA,QACL,OAAA;AAAA,QACA,MAAA;AAAA,QACA,GAAA,EAAK,CAAC,KAAA,GAAQ,EAAC,KAAM;AACnB,UAAA,IAAI,KAAA,EAAO;AACX,UAAA,KAAA,GAAQ,IAAA;AACR,UAAA,MAAM,CAAA,GAAI,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA;AACjC,UAAA,IAAI,CAAA,IAAK,CAAA,EAAG,SAAA,CAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AACjC,UAAA,OAAA,CAAQ;AAAA,YACN,KAAK,KAAA,EAAM;AAAA,YACX,IAAA;AAAA,YACA,UAAA,EAAY,IAAI,IAAA,CAAK,SAAS,EAAE,WAAA,EAAY;AAAA,YAC5C,OAAA;AAAA,YACA,MAAA;AAAA,YACA,GAAI,MAAA,GAAS,EAAE,UAAU,MAAA,CAAO,MAAA,KAAW,EAAC;AAAA,YAC5C,UAAA,EAAY,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACzB,GAAI,IAAA,CAAK,KAAA,IAAS,KAAA,CAAM,KAAA,GAAQ,EAAE,KAAA,EAAO,EAAE,GAAG,IAAA,CAAK,OAAO,GAAG,KAAA,CAAM,KAAA,EAAM,KAAM,EAAC;AAAA,YAChF,GAAI,MAAM,OAAA,GAAU,EAAE,SAAS,KAAA,CAAM,OAAA,KAAY;AAAC,WACnD,CAAA;AAAA,QACH;AAAA,OACF;AAAA,IACF,CAAA;AAAA,IAEA,KAAA,CAAM,MAAc,EAAA,EAA0E;AAC5F,MAAA,OAAA,CAAQ,EAAE,GAAA,EAAK,KAAA,EAAM,EAAG,IAAA,EAAM,UAAA,EAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAG,KAAA,EAAO,IAAI,CAAA;AAAA,IACjF,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,SAAS,GAAA,EAAgD;AACvD,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AACnC,MAAA,KAAA,MAAW,CAAC,IAAA,EAAM,EAAE,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC5C,QAAA,IAAI,EAAA,IAAM,IAAA,EAAM,QAAA,CAAS,MAAA,CAAO,IAAI,CAAA;AAAA,aAC/B,QAAA,CAAS,GAAA,CAAI,IAAA,EAAM,MAAA,CAAO,EAAE,CAAC,CAAA;AAAA,MACpC;AACA,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AAChC,MAAA,IAAI,IAAA,IAAQ,IAAA,KAAS,OAAA,IAAW,MAAA,EAAQ;AACtC,QAAA,OAAA,CAAQ;AAAA,UACN,KAAK,KAAA,EAAM;AAAA,UACX,IAAA,EAAM,WAAA;AAAA,UACN,UAAA,EAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,UACnC,OAAA,EAAS,QAAQ,MAAM,CAAA,CAAA;AAAA,UACvB,OAAA,EAAS,QAAQ,IAAI,CAAA;AAAA,SACtB,CAAA;AAAA,MACH;AACA,MAAA,IAAI,QAAA,CAAS,IAAI,MAAM,CAAA,UAAW,CAAA,KAAA,EAAQ,QAAA,CAAS,GAAA,CAAI,MAAM,CAAC,CAAA,CAAA;AAAA,IAChE,CAAA;AAAA,IAEA,SAAS,GAAA,EAAyB;AAChC,MAAA,KAAA,GAAQ,GAAA;AAAA,IACV,CAAA;AAAA,IAEA,KAAA;AAAA,IAEA,MAAM,QAAA,GAAW;AACf,MAAA,IAAI,KAAA,gBAAqB,KAAK,CAAA;AAC9B,MAAA,MAAM,KAAA,EAAM;AAAA,IACd,CAAA;AAAA;AAAA,IAGA,SAAA,EAAW;AAAA,MACT,KAAA;AAAA,MACA,QAAA;AAAA,MACA,IAAI,MAAA,GAAS;AACX,QAAA,OAAO,MAAA;AAAA,MACT,CAAA;AAAA,MACA,SAAA;AAAA,MACA,IAAI,OAAA,GAAU;AACZ,QAAA,OAAO,OAAA;AAAA,MACT,CAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC/WA,IAAM,iBAAiB,MAAM;AAC3B,EAAA,MAAM,GAAA,GAAM,OAAO,SAAA,KAAc,WAAA,GAAc,EAAC,GAAI,SAAA;AACpD,EAAA,MAAM,GAAA,GAAM,OAAO,MAAA,KAAW,WAAA,GAAc,EAAC,GAAI,MAAA;AACjD,EAAA,MAAM,GAAA,GAAM,OAAO,MAAA,KAAW,WAAA,GAAc,EAAC,GAAI,MAAA;AACjD,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,KAAA;AAAA,IACV,UAAA,EAAY,SAAA;AAAA;AAAA,IACZ,WAAW,GAAA,CAAI,SAAA;AAAA,IACf,QAAQ,GAAA,CAAI,QAAA;AAAA,IACZ,WAAW,MAAM;AACf,MAAA,IAAI;AACF,QAAA,OAAO,IAAA,CAAK,cAAA,EAAe,CAAE,eAAA,EAAgB,CAAE,QAAA;AAAA,MACjD,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,MAAA;AAAA,MACT;AAAA,IACF,CAAA,GAAG;AAAA,IACH,SAAS,GAAA,CAAI,KAAA;AAAA,IACb,SAAS,GAAA,CAAI,MAAA;AAAA,IACb,WAAW,GAAA,CAAI,UAAA;AAAA,IACf,WAAW,GAAA,CAAI,WAAA;AAAA,IACf,UAAA,EAAY,IAAI,UAAA,EAAY,aAAA;AAAA,IAC5B,QAAQ,GAAA,CAAI;AAAA,GACd;AACF,CAAA;AAGA,IAAM,sBAAsB,MAAM;AAChC,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,EAAa,OAAO,IAAA;AAC7C,EAAA,IAAI,UAAU,UAAA,KAAe,GAAA,IAAO,SAAA,CAAU,oBAAA,KAAyB,MAAM,OAAO,KAAA;AACpF,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,UAAA,KAAe,KAAK,OAAO,KAAA;AACvE,EAAA,OAAO,IAAA;AACT,CAAA;AASO,SAAS,mBAAmB,IAAA,EAA4C;AAC7E,EAAA,MAAM,EAAE,UAAU,MAAM,IAAA,EAAM,sBAAsB,IAAA,EAAM,aAAA,EAAe,GAAG,IAAA,EAAK,GAAI,IAAA;AAErF,EAAA,MAAM,OAAA,GACJ,OAAO,YAAA,KAAiB,WAAA,GACpB,MAAA,GACA;AAAA,IACE,GAAA,EAAK,CAAC,CAAA,KAAc;AAClB,MAAA,IAAI;AACF,QAAA,OAAO,YAAA,CAAa,QAAQ,CAAC,CAAA;AAAA,MAC/B,CAAA,CAAA,MAAQ;AACN,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA;AAAA,IACA,GAAA,EAAK,CAAC,CAAA,EAAW,CAAA,KAAc;AAC7B,MAAA,IAAI;AACF,QAAA,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,MAC3B,CAAA,CAAA,MAAQ;AAAA,MAAC;AAAA,IACX;AAAA,GACF;AAEN,EAAA,MAAM,SAAS,YAAA,CAAa;AAAA,IAC1B,GAAG,IAAA;AAAA,IACH,OAAA;AAAA,IACA,aAAA,EAAe,EAAE,GAAG,cAAA,EAAe,EAAG,YAAY,IAAA,CAAK,OAAA,IAAW,SAAA,EAAW,GAAG,aAAA,EAAc;AAAA,IAC9F,OAAA,EAAS,MAAM,mBAAA,EAAoB,IAAK,OAAA;AAAQ,GACjD,CAAA;AAED,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,IAAI,mBAAA,EAAqB;AACvB,MAAA,MAAA,CAAO,gBAAA,CAAiB,OAAA,EAAS,CAAC,EAAA,KAAY;AAC5C,QAAA,MAAA,CAAO,YAAA,CAAa,GAAG,KAAA,IAAS,EAAA,CAAG,SAAS,EAAE,OAAA,EAAS,OAAO,CAAA;AAAA,MAChE,CAAC,CAAA;AACD,MAAA,MAAA,CAAO,gBAAA,CAAiB,oBAAA,EAAsB,CAAC,EAAA,KAAY;AACzD,QAAA,MAAA,CAAO,aAAa,EAAA,CAAG,MAAA,EAAQ,EAAE,OAAA,EAAS,OAAO,CAAA;AAAA,MACnD,CAAC,CAAA;AAAA,IACH;AAIA,IAAA,MAAM,cAAc,MAAM;AACxB,MAAA,MAAM,CAAA,GAAI,OAAO,SAAA,CAAU,KAAA;AAC3B,MAAA,IAAI,CAAC,EAAE,MAAA,IAAU,OAAO,cAAc,WAAA,IAAe,CAAC,UAAU,UAAA,EAAY;AAC5E,MAAA,MAAM,IAAA,GAAO,KAAK,SAAA,CAAU;AAAA,QAC1B,GAAA,EAAK,EAAE,IAAA,EAAM,uBAAA,EAAyB,SAAS,GAAA,EAAI;AAAA,QACnD,MAAA,EAAA,iBAAQ,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,QAC/B,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,MAAA,EAAQ,EAAE,GAAG,cAAA,IAAkB,UAAA,EAAY,IAAA,CAAK,WAAW,SAAA,EAAU;AAAA,QACrE,OAAA,EAAS;AAAA,UACP,UAAU,CAAC,GAAG,MAAA,CAAO,SAAA,CAAU,QAAQ,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,MAAM,EAAE,CAAA,MAAO,EAAE,IAAA,EAAM,IAAG,CAAE;AAAA,SAC7E;AAAA,QACA,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG;AAAA,OACxB,CAAA;AACD,MAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,GAAG,IAAI,GAAA,GAAM,GAAA;AAC3C,MAAA,IAAI,SAAA,CAAU,UAAA,CAAW,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA,EAAG,GAAG,CAAA,IAAA,EAAO,kBAAA,CAAmB,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,EAAG;AACtF,QAAA,CAAA,CAAE,MAAA,GAAS,CAAA;AAAA,MACb;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,WAAW,CAAA;AAC/C,IAAA,QAAA,EAAU,gBAAA,GAAmB,oBAAoB,MAAM;AACrD,MAAA,IAAI,QAAA,CAAS,eAAA,KAAoB,QAAA,EAAU,WAAA,EAAY;AAAA,IACzD,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,MAAA;AACT","file":"web.js","sourcesContent":["import { uuidv7 } from 'uuidv7';\n\ndeclare const window: unknown;\n\n/**\n * The isomorphic SDK core (instrumentation §6) — ~80% of every client by\n * value. Zero heavy dependencies; platform entries are thin wiring over this.\n *\n * The wire contract it upholds:\n * - every record carries a client-generated UUIDv7 `_id` (the idempotency\n * contract — transports are at-least-once)\n * - trace ids are UUIDv7 too: schema §2.6 samples on the random hex tail\n * - batch context carries subjects/actor once, not per record\n * - `identify()` swaps subjects AND posts one `$identify` control record —\n * the input to the server-side stitching job\n */\n\nexport const newId = uuidv7;\n\nexport interface ClientContextInput {\n platform?: string;\n appVersion?: string;\n userAgent?: string;\n os?: string;\n osVersion?: string;\n browser?: string;\n browserVersion?: string;\n deviceType?: string;\n locale?: string;\n timezone?: string;\n screenW?: number;\n screenH?: number;\n viewportW?: number;\n viewportH?: number;\n connection?: string;\n online?: boolean;\n}\n\nexport interface TransportResult {\n ok: boolean;\n status?: number;\n}\n\nexport type Transport = (url: string, body: string, headers: Record<string, string>) => Promise<TransportResult>;\n\nexport interface ClientStorage {\n get(key: string): string | null | undefined;\n set(key: string, value: string): void;\n}\n\nexport interface WireRecord {\n _id: string;\n name: string;\n occurredAt: string;\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n body?: string;\n severity?: string;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n actor?: string;\n traceId?: string;\n spanId?: string;\n parentId?: string;\n durationMs?: number;\n error?: unknown;\n state?: unknown;\n usage?: unknown;\n anonRef?: string;\n userRef?: string;\n}\n\nexport interface CreateClientOptions {\n /** pk_ for anything shipped to users; sk_ only in trusted processes */\n key: string;\n /** the mounted ingest endpoint, e.g. https://app.example.com/telemetry/ingest */\n url: string;\n /** injected at build; the key supplies service/env */\n release?: string;\n flushIntervalMs?: number;\n /** records per POST */\n maxBatchSize?: number;\n /** ring buffer cap — drop-OLDEST beyond it; new telemetry outranks stale */\n maxQueueSize?: number;\n /** failed flushes retry with backoff; a batch is abandoned after this many */\n maxRetries?: number;\n transport?: Transport;\n /** persists the anon id (and lets platforms persist queues); memory default */\n storage?: ClientStorage;\n clientContext?: ClientContextInput;\n /** false = drop instead of send. Web adapter wires DNT/GPC/consent here. */\n consent?: () => boolean;\n /** registry name used by captureError. Convention: 'error.unhandled'. */\n errorName?: string;\n /** SDK-internal failures — never thrown at the app */\n onError?: (e: unknown) => void;\n}\n\nexport interface Span {\n readonly traceId: string;\n readonly spanId: string;\n end(extra?: { attrs?: Record<string, string>; metrics?: Record<string, number> }): void;\n}\n\nconst SDK = { name: '@jeffjassky/telemetry', version: '0' };\n\nconst defaultTransport: Transport = async (url, body, headers) => {\n const res = await fetch(url, { method: 'POST', headers, body, keepalive: true });\n return { ok: res.ok, status: res.status };\n};\n\nconst memoryStorage = (): ClientStorage => {\n const m = new Map<string, string>();\n return { get: (k) => m.get(k), set: (k, v) => void m.set(k, v) };\n};\n\n/** stable, cheap error grouping — server-side fingerprinting can refine later */\nconst fingerprint = (type: string, message: string, frame: string): string => {\n const s = `${type}|${message.replace(/\\d+/g, 'N').slice(0, 200)}|${frame}`;\n let h = 5381;\n for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;\n return h.toString(16);\n};\n\nconst FRAME_RE = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+))\\)?\\s*$/;\n\nconst parseFrames = (stack: string | undefined) =>\n (stack ?? '')\n .split('\\n')\n .slice(1, 21)\n .map((line) => FRAME_RE.exec(line))\n .filter((m): m is RegExpExecArray => !!m)\n .map((m) => ({\n fn: m[1],\n filename: m[2],\n lineno: Number(m[3]),\n colno: Number(m[4]),\n }));\n\nexport function createClient(options: CreateClientOptions) {\n const {\n key,\n url,\n release,\n flushIntervalMs = 5000,\n maxBatchSize = 50,\n maxQueueSize = 1000,\n maxRetries = 5,\n transport = defaultTransport,\n storage = memoryStorage(),\n consent = () => true,\n errorName = 'error.unhandled',\n onError = () => {},\n } = options;\n\n // ClientContext must always be storable — platform/appVersion are required\n // by the envelope, so the core guarantees them and adapters refine them\n const clientContext = {\n platform: typeof window === 'undefined' ? 'server' : 'web',\n appVersion: release ?? 'unknown',\n ...options.clientContext,\n };\n\n // ── identity context ──\n let anonId = storage.get('telemetry_anon') ?? null;\n if (!anonId) {\n anonId = `anon_${newId()}`;\n try {\n storage.set('telemetry_anon', anonId);\n } catch (e) {\n onError(e);\n }\n }\n const sessionId = `ses_${newId()}`;\n /** current subjects, by type. anon + session ride every batch (schema §2.3). */\n const subjects = new Map<string, string>([\n ['anon', anonId],\n ['session', sessionId],\n ]);\n let actor: string | undefined;\n\n // ── queue ──\n const queue: WireRecord[] = [];\n let dropped = 0;\n let attempts = 0;\n let flushing: Promise<void> | null = null;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const enqueue = (rec: WireRecord) => {\n queue.push(rec);\n while (queue.length > maxQueueSize) {\n queue.shift(); // drop-oldest: fresh telemetry outranks stale\n dropped++;\n }\n };\n\n // ── tracing ──\n const spanStack: Array<{ traceId: string; spanId: string }> = [];\n const activeTrace = () => spanStack[spanStack.length - 1];\n\n const doFlush = async (): Promise<void> => {\n if (!queue.length) return;\n if (!consent()) {\n queue.length = 0; // no consent = no send AND no hoard\n return;\n }\n while (queue.length) {\n const batch = queue.slice(0, maxBatchSize);\n const body = JSON.stringify({\n sdk: SDK,\n sentAt: new Date().toISOString(),\n release,\n client: clientContext,\n context: {\n subjects: [...subjects].map(([type, id]) => ({ type, id })),\n ...(actor ? { actor } : {}),\n },\n records: batch,\n });\n try {\n const res = await transport(url, body, {\n 'content-type': 'application/json',\n authorization: `Bearer ${key}`,\n });\n if (!res.ok) throw new Error(`ingest ${res.status}`);\n queue.splice(0, batch.length);\n attempts = 0;\n } catch (e) {\n // records stay queued; the SAME _ids retry, so the server dedupes\n attempts++;\n if (attempts >= maxRetries) {\n queue.splice(0, batch.length);\n dropped += batch.length;\n attempts = 0;\n }\n onError(e);\n return; // back off until the next interval\n }\n }\n };\n\n const flush = () => (flushing ??= doFlush().finally(() => (flushing = null)));\n\n if (flushIntervalMs > 0) {\n timer = setInterval(() => void flush(), flushIntervalMs);\n (timer as { unref?: () => void }).unref?.(); // never hold a node process open\n }\n\n const client = {\n track(\n name: string,\n opts: {\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n occurredAt?: Date;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n severity?: string;\n } = {},\n ) {\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name,\n occurredAt: (opts.occurredAt ?? new Date()).toISOString(),\n ...(opts.attrs ? { attrs: opts.attrs } : {}),\n ...(opts.metrics ? { metrics: opts.metrics } : {}),\n ...(opts.data ? { data: opts.data } : {}),\n ...(opts.subjects ? { subjects: opts.subjects } : {}),\n ...(opts.severity ? { severity: opts.severity } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n });\n },\n\n captureError(err: unknown, ctx: { handled?: boolean; name?: string; attrs?: Record<string, string> } = {}) {\n const e = err instanceof Error ? err : new Error(String(err));\n const frames = parseFrames(e.stack);\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name: ctx.name ?? errorName,\n occurredAt: new Date().toISOString(),\n severity: 'error',\n ...(ctx.attrs ? { attrs: ctx.attrs } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n error: {\n type: e.name || 'Error',\n message: e.message,\n handled: ctx.handled ?? true,\n fingerprint: fingerprint(e.name, e.message, frames[0]?.filename ?? ''),\n frames,\n },\n });\n },\n\n startSpan(name: string, opts: { attrs?: Record<string, string> } = {}): Span {\n const parent = activeTrace();\n const traceId = parent?.traceId ?? newId();\n const spanId = newId();\n const startedAt = Date.now();\n const frame = { traceId, spanId };\n spanStack.push(frame);\n let ended = false;\n return {\n traceId,\n spanId,\n end: (extra = {}) => {\n if (ended) return;\n ended = true;\n const i = spanStack.indexOf(frame);\n if (i >= 0) spanStack.splice(i, 1);\n enqueue({\n _id: newId(),\n name,\n occurredAt: new Date(startedAt).toISOString(),\n traceId,\n spanId,\n ...(parent ? { parentId: parent.spanId } : {}),\n durationMs: Date.now() - startedAt,\n ...(opts.attrs || extra.attrs ? { attrs: { ...opts.attrs, ...extra.attrs } } : {}),\n ...(extra.metrics ? { metrics: extra.metrics } : {}),\n });\n },\n };\n },\n\n state(name: string, st: { key: string; from?: string; to: string; previousSinceMs?: number }) {\n enqueue({ _id: newId(), name, occurredAt: new Date().toISOString(), state: st });\n },\n\n /**\n * Swap in the real identity. Future batches carry the new subjects; one\n * `$identify` control record links anon → user for the stitching job.\n * The anon subject stays on the context — stitching consumes the alias.\n */\n identify(ids: Record<string, string | null | undefined>) {\n const hadUser = subjects.get('user');\n for (const [type, id] of Object.entries(ids)) {\n if (id == null) subjects.delete(type);\n else subjects.set(type, String(id));\n }\n const user = subjects.get('user');\n if (user && user !== hadUser && anonId) {\n enqueue({\n _id: newId(),\n name: '$identify',\n occurredAt: new Date().toISOString(),\n anonRef: `anon:${anonId}`,\n userRef: `user:${user}`,\n });\n }\n if (subjects.has('user')) actor = `user:${subjects.get('user')}`;\n },\n\n setActor(ref: string | undefined) {\n actor = ref;\n },\n\n flush,\n\n async shutdown() {\n if (timer) clearInterval(timer);\n await flush();\n },\n\n /** introspection for adapters and tests — not a public contract */\n _internal: {\n queue,\n subjects,\n get anonId() {\n return anonId!;\n },\n sessionId,\n get dropped() {\n return dropped;\n },\n enqueue,\n },\n };\n\n return client;\n}\n\nexport type TelemetryClient = ReturnType<typeof createClient>;\n\n// The isomorphic registry surface (instrumentation §8): the host's registry\n// file imports defineRegistry from '/core' so server AND clients can import\n// it — server at runtime, clients `import type` only, so zod never reaches a\n// bundle. This chain must stay mongoose/express-free.\nexport { defineRegistry, boundedMeta } from '../server/registry.js';\nexport type { Registry, EventSpec, RollupSpec, DimSource } from '../server/registry.js';\n","import { createClient, type CreateClientOptions, type TelemetryClient } from './core.js';\n\n/**\n * Browser wiring (instrumentation §7): ClientContext capture, global error\n * hooks, consent/DNT/GPC gate, unload flush via sendBeacon. ~wiring, not\n * behavior — anything smarter belongs in core.\n */\n\ndeclare const window: any;\ndeclare const navigator: any;\ndeclare const document: any;\ndeclare const screen: any;\ndeclare const localStorage: any;\n\nconst browserContext = () => {\n const nav = typeof navigator === 'undefined' ? {} : navigator;\n const scr = typeof screen === 'undefined' ? {} : screen;\n const win = typeof window === 'undefined' ? {} : window;\n return {\n platform: 'web',\n appVersion: 'unknown', // overridden by opts.release / clientContext\n userAgent: nav.userAgent,\n locale: nav.language,\n timezone: (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {\n return undefined;\n }\n })(),\n screenW: scr.width,\n screenH: scr.height,\n viewportW: win.innerWidth,\n viewportH: win.innerHeight,\n connection: nav.connection?.effectiveType,\n online: nav.onLine,\n };\n};\n\n/** DNT/GPC are hard signals — a host consent callback can only narrow further */\nconst privacySignalsAllow = () => {\n if (typeof navigator === 'undefined') return true;\n if (navigator.doNotTrack === '1' || navigator.globalPrivacyControl === true) return false;\n if (typeof window !== 'undefined' && window.doNotTrack === '1') return false;\n return true;\n};\n\nexport interface WebTelemetryOptions extends Omit<CreateClientOptions, 'storage' | 'consent'> {\n /** host consent, e.g. a cookie-banner check. ANDed with DNT/GPC. */\n consent?: () => boolean;\n /** auto-capture window.onerror / unhandledrejection (default true) */\n captureGlobalErrors?: boolean;\n}\n\nexport function createWebTelemetry(opts: WebTelemetryOptions): TelemetryClient {\n const { consent = () => true, captureGlobalErrors = true, clientContext, ...rest } = opts;\n\n const storage =\n typeof localStorage === 'undefined'\n ? undefined\n : {\n get: (k: string) => {\n try {\n return localStorage.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k: string, v: string) => {\n try {\n localStorage.setItem(k, v);\n } catch {}\n },\n };\n\n const client = createClient({\n ...rest,\n storage,\n clientContext: { ...browserContext(), appVersion: opts.release ?? 'unknown', ...clientContext },\n consent: () => privacySignalsAllow() && consent(),\n });\n\n if (typeof window !== 'undefined') {\n if (captureGlobalErrors) {\n window.addEventListener('error', (ev: any) => {\n client.captureError(ev.error ?? ev.message, { handled: false });\n });\n window.addEventListener('unhandledrejection', (ev: any) => {\n client.captureError(ev.reason, { handled: false });\n });\n }\n\n // Unload flush: sendBeacon carries the batch (no headers possible, so the\n // pk_ key rides the query string — the server accepts that for pk_ only).\n const beaconFlush = () => {\n const q = client._internal.queue;\n if (!q.length || typeof navigator === 'undefined' || !navigator.sendBeacon) return;\n const body = JSON.stringify({\n sdk: { name: '@jeffjassky/telemetry', version: '0' },\n sentAt: new Date().toISOString(),\n release: opts.release,\n client: { ...browserContext(), appVersion: opts.release ?? 'unknown' },\n context: {\n subjects: [...client._internal.subjects].map(([type, id]) => ({ type, id })),\n },\n records: q.slice(0, 100),\n });\n const sep = opts.url.includes('?') ? '&' : '?';\n if (navigator.sendBeacon(`${opts.url}${sep}key=${encodeURIComponent(opts.key)}`, body)) {\n q.length = 0; // retries would duplicate _ids anyway; the server dedupes\n }\n };\n window.addEventListener('pagehide', beaconFlush);\n document?.addEventListener?.('visibilitychange', () => {\n if (document.visibilityState === 'hidden') beaconFlush();\n });\n }\n\n return client;\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
1
+ {"version":3,"sources":["../src/client/core.ts","../src/client/web.ts"],"names":["uuidv7"],"mappings":"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","file":"web.js","sourcesContent":["import { uuidv7 } from 'uuidv7';\n\ndeclare const window: unknown;\n\n/**\n * The isomorphic SDK core (instrumentation §6) — ~80% of every client by\n * value. Zero heavy dependencies; platform entries are thin wiring over this.\n *\n * The wire contract it upholds:\n * - every record carries a client-generated UUIDv7 `_id` (the idempotency\n * contract — transports are at-least-once)\n * - trace ids are UUIDv7 too: schema §2.6 samples on the random hex tail\n * - batch context carries subjects/actor once, not per record\n * - `identify()` swaps subjects AND posts one `$identify` control record —\n * the input to the server-side stitching job\n */\n\nexport const newId = uuidv7;\n\nexport interface ClientContextInput {\n platform?: string;\n appVersion?: string;\n userAgent?: string;\n os?: string;\n osVersion?: string;\n browser?: string;\n browserVersion?: string;\n deviceType?: string;\n locale?: string;\n timezone?: string;\n screenW?: number;\n screenH?: number;\n viewportW?: number;\n viewportH?: number;\n connection?: string;\n online?: boolean;\n}\n\nexport interface TransportResult {\n ok: boolean;\n status?: number;\n}\n\nexport type Transport = (url: string, body: string, headers: Record<string, string>) => Promise<TransportResult>;\n\nexport interface ClientStorage {\n get(key: string): string | null | undefined;\n set(key: string, value: string): void;\n}\n\nexport interface WireRecord {\n _id: string;\n name: string;\n occurredAt: string;\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n body?: string;\n severity?: string;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n actor?: string;\n traceId?: string;\n spanId?: string;\n parentId?: string;\n durationMs?: number;\n error?: unknown;\n state?: unknown;\n usage?: unknown;\n anonRef?: string;\n userRef?: string;\n}\n\nexport interface CreateClientOptions {\n /** pk_ for anything shipped to users; sk_ only in trusted processes */\n key: string;\n /** the mounted ingest endpoint, e.g. https://app.example.com/telemetry/ingest */\n url: string;\n /** injected at build; the key supplies service/env */\n release?: string;\n flushIntervalMs?: number;\n /** records per POST */\n maxBatchSize?: number;\n /** ring buffer cap — drop-OLDEST beyond it; new telemetry outranks stale */\n maxQueueSize?: number;\n /** failed flushes retry with backoff; a batch is abandoned after this many */\n maxRetries?: number;\n transport?: Transport;\n /** persists the anon id (and lets platforms persist queues); memory default */\n storage?: ClientStorage;\n clientContext?: ClientContextInput;\n /** false = drop instead of send. Web adapter wires DNT/GPC/consent here. */\n consent?: () => boolean;\n /** registry name used by captureError. Convention: 'error.unhandled'. */\n errorName?: string;\n /**\n * Last gate before a record joins the queue — the one place every kind\n * (event, error, span, state, usage) passes through. Return the record to\n * keep it, a modified copy to redact it, or `null` to drop it silently.\n *\n * This is where noise dies. A host that filters at the platform level\n * instead — swallowing `window.onerror` before the SDK sees it — has to win\n * a listener-registration race to do it, which is a real footgun; the whole\n * point of putting the hook here is that the host never has to think about\n * ordering.\n *\n * A throwing hook DROPS the record and reports via `onError`. Fail-closed is\n * deliberate: the common use is redaction, and a half-applied redaction that\n * still ships is worse than a lost record.\n */\n beforeSend?: (rec: WireRecord) => WireRecord | null | undefined | void;\n /** SDK-internal failures — never thrown at the app */\n onError?: (e: unknown) => void;\n}\n\nexport interface Span {\n readonly traceId: string;\n readonly spanId: string;\n end(extra?: { attrs?: Record<string, string>; metrics?: Record<string, number> }): void;\n}\n\nconst SDK = { name: '@jeffjassky/telemetry', version: '0' };\n\nconst defaultTransport: Transport = async (url, body, headers) => {\n const res = await fetch(url, { method: 'POST', headers, body, keepalive: true });\n return { ok: res.ok, status: res.status };\n};\n\nconst memoryStorage = (): ClientStorage => {\n const m = new Map<string, string>();\n return { get: (k) => m.get(k), set: (k, v) => void m.set(k, v) };\n};\n\n/** stable, cheap error grouping — server-side fingerprinting can refine later */\nconst fingerprint = (type: string, message: string, frame: string): string => {\n const s = `${type}|${message.replace(/\\d+/g, 'N').slice(0, 200)}|${frame}`;\n let h = 5381;\n for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;\n return h.toString(16);\n};\n\nconst FRAME_RE = /^\\s*at\\s+(?:(.+?)\\s+\\()?(?:(.+?):(\\d+):(\\d+))\\)?\\s*$/;\n\nconst parseFrames = (stack: string | undefined) =>\n (stack ?? '')\n .split('\\n')\n .slice(1, 21)\n .map((line) => FRAME_RE.exec(line))\n .filter((m): m is RegExpExecArray => !!m)\n .map((m) => ({\n fn: m[1],\n filename: m[2],\n lineno: Number(m[3]),\n colno: Number(m[4]),\n }));\n\nexport function createClient(options: CreateClientOptions) {\n const {\n key,\n url,\n release,\n flushIntervalMs = 5000,\n maxBatchSize = 50,\n maxQueueSize = 1000,\n maxRetries = 5,\n transport = defaultTransport,\n storage = memoryStorage(),\n consent = () => true,\n errorName = 'error.unhandled',\n beforeSend,\n onError = () => {},\n } = options;\n\n // ClientContext must always be storable — platform/appVersion are required\n // by the envelope, so the core guarantees them and adapters refine them\n const clientContext = {\n platform: typeof window === 'undefined' ? 'server' : 'web',\n appVersion: release ?? 'unknown',\n ...options.clientContext,\n };\n\n // ── identity context ──\n let anonId = storage.get('telemetry_anon') ?? null;\n if (!anonId) {\n anonId = `anon_${newId()}`;\n try {\n storage.set('telemetry_anon', anonId);\n } catch (e) {\n onError(e);\n }\n }\n const sessionId = `ses_${newId()}`;\n /** current subjects, by type. anon + session ride every batch (schema §2.3). */\n const subjects = new Map<string, string>([\n ['anon', anonId],\n ['session', sessionId],\n ]);\n let actor: string | undefined;\n\n // ── queue ──\n const queue: WireRecord[] = [];\n let dropped = 0;\n let attempts = 0;\n let flushing: Promise<void> | null = null;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const enqueue = (rec: WireRecord) => {\n let out: WireRecord | null | undefined | void = rec;\n if (beforeSend) {\n try {\n out = beforeSend(rec);\n } catch (e) {\n onError(e); // fail-closed: a broken filter must not leak what it was filtering\n return;\n }\n }\n if (!out) return;\n queue.push(out);\n while (queue.length > maxQueueSize) {\n queue.shift(); // drop-oldest: fresh telemetry outranks stale\n dropped++;\n }\n };\n\n // ── tracing ──\n const spanStack: Array<{ traceId: string; spanId: string }> = [];\n const activeTrace = () => spanStack[spanStack.length - 1];\n\n const doFlush = async (): Promise<void> => {\n if (!queue.length) return;\n if (!consent()) {\n queue.length = 0; // no consent = no send AND no hoard\n return;\n }\n while (queue.length) {\n const batch = queue.slice(0, maxBatchSize);\n const body = JSON.stringify({\n sdk: SDK,\n sentAt: new Date().toISOString(),\n release,\n client: clientContext,\n context: {\n subjects: [...subjects].map(([type, id]) => ({ type, id })),\n ...(actor ? { actor } : {}),\n },\n records: batch,\n });\n try {\n const res = await transport(url, body, {\n 'content-type': 'application/json',\n authorization: `Bearer ${key}`,\n });\n if (!res.ok) throw new Error(`ingest ${res.status}`);\n queue.splice(0, batch.length);\n attempts = 0;\n } catch (e) {\n // records stay queued; the SAME _ids retry, so the server dedupes\n attempts++;\n if (attempts >= maxRetries) {\n queue.splice(0, batch.length);\n dropped += batch.length;\n attempts = 0;\n }\n onError(e);\n return; // back off until the next interval\n }\n }\n };\n\n const flush = () => (flushing ??= doFlush().finally(() => (flushing = null)));\n\n if (flushIntervalMs > 0) {\n timer = setInterval(() => void flush(), flushIntervalMs);\n (timer as { unref?: () => void }).unref?.(); // never hold a node process open\n }\n\n const client = {\n track(\n name: string,\n opts: {\n attrs?: Record<string, string>;\n metrics?: Record<string, number>;\n data?: Record<string, unknown>;\n occurredAt?: Date;\n subjects?: Array<{ type: string; id: string; role?: string }>;\n severity?: string;\n } = {},\n ) {\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name,\n occurredAt: (opts.occurredAt ?? new Date()).toISOString(),\n ...(opts.attrs ? { attrs: opts.attrs } : {}),\n ...(opts.metrics ? { metrics: opts.metrics } : {}),\n ...(opts.data ? { data: opts.data } : {}),\n ...(opts.subjects ? { subjects: opts.subjects } : {}),\n ...(opts.severity ? { severity: opts.severity } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n });\n },\n\n captureError(err: unknown, ctx: { handled?: boolean; name?: string; attrs?: Record<string, string> } = {}) {\n const e = err instanceof Error ? err : new Error(String(err));\n const frames = parseFrames(e.stack);\n const trace = activeTrace();\n enqueue({\n _id: newId(),\n name: ctx.name ?? errorName,\n occurredAt: new Date().toISOString(),\n severity: 'error',\n ...(ctx.attrs ? { attrs: ctx.attrs } : {}),\n ...(trace ? { traceId: trace.traceId, parentId: trace.spanId } : {}),\n error: {\n type: e.name || 'Error',\n message: e.message,\n handled: ctx.handled ?? true,\n fingerprint: fingerprint(e.name, e.message, frames[0]?.filename ?? ''),\n frames,\n },\n });\n },\n\n startSpan(name: string, opts: { attrs?: Record<string, string> } = {}): Span {\n const parent = activeTrace();\n const traceId = parent?.traceId ?? newId();\n const spanId = newId();\n const startedAt = Date.now();\n const frame = { traceId, spanId };\n spanStack.push(frame);\n let ended = false;\n return {\n traceId,\n spanId,\n end: (extra = {}) => {\n if (ended) return;\n ended = true;\n const i = spanStack.indexOf(frame);\n if (i >= 0) spanStack.splice(i, 1);\n enqueue({\n _id: newId(),\n name,\n occurredAt: new Date(startedAt).toISOString(),\n traceId,\n spanId,\n ...(parent ? { parentId: parent.spanId } : {}),\n durationMs: Date.now() - startedAt,\n ...(opts.attrs || extra.attrs ? { attrs: { ...opts.attrs, ...extra.attrs } } : {}),\n ...(extra.metrics ? { metrics: extra.metrics } : {}),\n });\n },\n };\n },\n\n state(name: string, st: { key: string; from?: string; to: string; previousSinceMs?: number }) {\n enqueue({ _id: newId(), name, occurredAt: new Date().toISOString(), state: st });\n },\n\n /**\n * Swap in the real identity. Future batches carry the new subjects; one\n * `$identify` control record links anon → user for the stitching job.\n * The anon subject stays on the context — stitching consumes the alias.\n */\n identify(ids: Record<string, string | null | undefined>) {\n const hadUser = subjects.get('user');\n for (const [type, id] of Object.entries(ids)) {\n if (id == null) subjects.delete(type);\n else subjects.set(type, String(id));\n }\n const user = subjects.get('user');\n if (user && user !== hadUser && anonId) {\n enqueue({\n _id: newId(),\n name: '$identify',\n occurredAt: new Date().toISOString(),\n anonRef: `anon:${anonId}`,\n userRef: `user:${user}`,\n });\n }\n if (subjects.has('user')) actor = `user:${subjects.get('user')}`;\n },\n\n setActor(ref: string | undefined) {\n actor = ref;\n },\n\n flush,\n\n async shutdown() {\n if (timer) clearInterval(timer);\n await flush();\n },\n\n /** introspection for adapters and tests — not a public contract */\n _internal: {\n queue,\n subjects,\n get anonId() {\n return anonId!;\n },\n sessionId,\n get dropped() {\n return dropped;\n },\n enqueue,\n },\n };\n\n return client;\n}\n\nexport type TelemetryClient = ReturnType<typeof createClient>;\n\n// The isomorphic registry surface (instrumentation §8): the host's registry\n// file imports defineRegistry from '/core' so server AND clients can import\n// it — server at runtime, clients `import type` only, so zod never reaches a\n// bundle. This chain must stay mongoose/express-free.\nexport { defineRegistry, boundedMeta } from '../server/registry.js';\nexport type { Registry, EventSpec, RollupSpec, DimSource } from '../server/registry.js';\n","import { createClient, type CreateClientOptions, type TelemetryClient, type WireRecord } from './core.js';\n\n/**\n * Browser wiring (instrumentation §7): ClientContext capture, global error\n * hooks, consent/DNT/GPC gate, unload flush via sendBeacon. ~wiring, not\n * behavior — anything smarter belongs in core.\n */\n\ndeclare const window: any;\ndeclare const navigator: any;\ndeclare const document: any;\ndeclare const screen: any;\ndeclare const localStorage: any;\n\nconst browserContext = () => {\n const nav = typeof navigator === 'undefined' ? {} : navigator;\n const scr = typeof screen === 'undefined' ? {} : screen;\n const win = typeof window === 'undefined' ? {} : window;\n return {\n platform: 'web',\n appVersion: 'unknown', // overridden by opts.release / clientContext\n userAgent: nav.userAgent,\n locale: nav.language,\n timezone: (() => {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n } catch {\n return undefined;\n }\n })(),\n screenW: scr.width,\n screenH: scr.height,\n viewportW: win.innerWidth,\n viewportH: win.innerHeight,\n connection: nav.connection?.effectiveType,\n online: nav.onLine,\n };\n};\n\n/** DNT/GPC are hard signals — a host consent callback can only narrow further */\nconst privacySignalsAllow = () => {\n if (typeof navigator === 'undefined') return true;\n if (navigator.doNotTrack === '1' || navigator.globalPrivacyControl === true) return false;\n if (typeof window !== 'undefined' && window.doNotTrack === '1') return false;\n return true;\n};\n\n/**\n * Errors the browser raises that are not failures.\n *\n * `ResizeObserver loop completed with undelivered notifications` is the whole\n * list for now, and it earns its place: Chrome raises it as an *uncaught\n * error* whenever an observer callback dirties layout and the next batch\n * lands a frame later. Nothing is broken, nothing is actionable, and it is\n * emitted often enough to bury real errors — one production host saw 49 of\n * its last 50 error records come from this one message.\n *\n * Filtered by default because the alternative is every host discovering it\n * separately. Opt back in with `captureBenignErrors: true`.\n */\nexport const BENIGN_BROWSER_ERRORS: readonly RegExp[] = [\n /ResizeObserver loop (?:completed with undelivered notifications|limit exceeded)/,\n];\n\nexport interface WebTelemetryOptions extends Omit<CreateClientOptions, 'storage' | 'consent'> {\n /** host consent, e.g. a cookie-banner check. ANDed with DNT/GPC. */\n consent?: () => boolean;\n /** auto-capture window.onerror / unhandledrejection (default true) */\n captureGlobalErrors?: boolean;\n /**\n * Drop error records whose message matches. Strings match by substring,\n * RegExp by test. ADDED to `BENIGN_BROWSER_ERRORS`, not replacing it.\n */\n ignoreErrors?: Array<string | RegExp>;\n /** keep the `BENIGN_BROWSER_ERRORS` records instead of dropping them */\n captureBenignErrors?: boolean;\n}\n\nexport function createWebTelemetry(opts: WebTelemetryOptions): TelemetryClient {\n const {\n consent = () => true,\n captureGlobalErrors = true,\n ignoreErrors,\n captureBenignErrors = false,\n clientContext,\n ...rest\n } = opts;\n\n // Message filtering rides core's `beforeSend` rather than the global error\n // listeners: the listeners are not the only way a record is born\n // (`captureError()` calls from app code are), and a host-supplied\n // `beforeSend` must still get its turn on everything that survives.\n const patterns = [...(captureBenignErrors ? [] : BENIGN_BROWSER_ERRORS), ...(ignoreErrors ?? [])];\n const hostBeforeSend = rest.beforeSend;\n const beforeSend = (rec: WireRecord) => {\n const message = (rec.error as { message?: string } | undefined)?.message;\n if (\n message &&\n patterns.some((p) => (typeof p === 'string' ? message.includes(p) : p.test(message)))\n ) {\n return null;\n }\n return hostBeforeSend ? hostBeforeSend(rec) : rec;\n };\n\n const storage =\n typeof localStorage === 'undefined'\n ? undefined\n : {\n get: (k: string) => {\n try {\n return localStorage.getItem(k);\n } catch {\n return null;\n }\n },\n set: (k: string, v: string) => {\n try {\n localStorage.setItem(k, v);\n } catch {}\n },\n };\n\n const client = createClient({\n ...rest,\n beforeSend,\n storage,\n clientContext: { ...browserContext(), appVersion: opts.release ?? 'unknown', ...clientContext },\n consent: () => privacySignalsAllow() && consent(),\n });\n\n if (typeof window !== 'undefined') {\n if (captureGlobalErrors) {\n window.addEventListener('error', (ev: any) => {\n client.captureError(ev.error ?? ev.message, { handled: false });\n });\n window.addEventListener('unhandledrejection', (ev: any) => {\n client.captureError(ev.reason, { handled: false });\n });\n }\n\n // Unload flush: sendBeacon carries the batch (no headers possible, so the\n // pk_ key rides the query string — the server accepts that for pk_ only).\n const beaconFlush = () => {\n const q = client._internal.queue;\n if (!q.length || typeof navigator === 'undefined' || !navigator.sendBeacon) return;\n const body = JSON.stringify({\n sdk: { name: '@jeffjassky/telemetry', version: '0' },\n sentAt: new Date().toISOString(),\n release: opts.release,\n client: { ...browserContext(), appVersion: opts.release ?? 'unknown' },\n context: {\n subjects: [...client._internal.subjects].map(([type, id]) => ({ type, id })),\n },\n records: q.slice(0, 100),\n });\n const sep = opts.url.includes('?') ? '&' : '?';\n if (navigator.sendBeacon(`${opts.url}${sep}key=${encodeURIComponent(opts.key)}`, body)) {\n q.length = 0; // retries would duplicate _ids anyway; the server dedupes\n }\n };\n window.addEventListener('pagehide', beaconFlush);\n document?.addEventListener?.('visibilitychange', () => {\n if (document.visibilityState === 'hidden') beaconFlush();\n });\n }\n\n return client;\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffjassky/telemetry",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Unified telemetry — product events, errors, traces, state transitions, and billable usage in one Mongo envelope, with typed SDKs and a mountable dashboard.",
5
5
  "license": "MIT",
6
6
  "author": "Jeff Jassky <jeff@jeffjassky.com>",
package/types/core.d.ts CHANGED
@@ -81,6 +81,12 @@ export interface CreateClientOptions {
81
81
  consent?: () => boolean;
82
82
  /** registry name used by captureError. Convention: 'error.unhandled'. */
83
83
  errorName?: string;
84
+ /**
85
+ * Last gate before a record joins the queue — every kind passes through it.
86
+ * Return the record to keep it, a modified copy to redact it, `null` to drop
87
+ * it. A throwing hook drops the record and reports via `onError`.
88
+ */
89
+ beforeSend?: (rec: WireRecord) => WireRecord | null | undefined | void;
84
90
  onError?: (e: unknown) => void;
85
91
  }
86
92
 
package/types/web.d.ts CHANGED
@@ -3,11 +3,19 @@ import type { CreateClientOptions, Registry, TelemetryClient } from './core.js';
3
3
  export { createClient } from './core.js';
4
4
  export type { TelemetryClient } from './core.js';
5
5
 
6
+ /** browser-raised non-errors, dropped by default (see `captureBenignErrors`) */
7
+ export declare const BENIGN_BROWSER_ERRORS: readonly RegExp[];
8
+
6
9
  export interface WebTelemetryOptions extends Omit<CreateClientOptions, 'storage' | 'consent'> {
7
10
  /** host consent (cookie banner etc). ANDed with DNT/GPC — those always win. */
8
11
  consent?: () => boolean;
9
12
  /** auto-capture window.onerror / unhandledrejection (default true) */
10
13
  captureGlobalErrors?: boolean;
14
+ /** drop error records by message — strings match by substring, RegExp by test.
15
+ * ADDED to `BENIGN_BROWSER_ERRORS`, not replacing it. */
16
+ ignoreErrors?: Array<string | RegExp>;
17
+ /** keep the `BENIGN_BROWSER_ERRORS` records instead of dropping them */
18
+ captureBenignErrors?: boolean;
11
19
  }
12
20
 
13
21
  /**