@jeffjassky/telemetry 0.2.0 → 0.4.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/README.md +3 -1
- package/dist/cli.cjs +12 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +12 -1
- package/dist/cli.js.map +1 -1
- package/dist/core.cjs +12 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +12 -1
- package/dist/core.js.map +1 -1
- package/dist/electron.cjs +12 -1
- package/dist/electron.cjs.map +1 -1
- package/dist/electron.js +12 -1
- package/dist/electron.js.map +1 -1
- package/dist/index.cjs +1437 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1423 -64
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +1468 -78
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +1468 -78
- package/dist/mcp.js.map +1 -1
- package/dist/react.cjs +12 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +12 -1
- package/dist/react.js.map +1 -1
- package/dist/ui/_assets/{index-CXiDp_v6.css → index-COswHpSX.css} +1 -1
- package/dist/ui/_assets/index-Deqhu-hT.js +41 -0
- package/dist/ui/_assets/index-Deqhu-hT.js.map +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/vue.cjs +12 -1
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +12 -1
- package/dist/vue.js.map +1 -1
- package/dist/web.cjs +34 -2
- package/dist/web.cjs.map +1 -1
- package/dist/web.js +34 -3
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/types/core.d.ts +6 -0
- package/types/index.d.ts +484 -11
- package/types/test-d.ts +173 -2
- package/types/web.d.ts +8 -0
- package/dist/ui/_assets/index-3jcToTuP.js +0 -41
- package/dist/ui/_assets/index-3jcToTuP.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -63,6 +63,7 @@ function createClient(options) {
|
|
|
63
63
|
storage = memoryStorage(),
|
|
64
64
|
consent = () => true,
|
|
65
65
|
errorName = "error.unhandled",
|
|
66
|
+
beforeSend,
|
|
66
67
|
onError = () => {
|
|
67
68
|
}
|
|
68
69
|
} = options;
|
|
@@ -92,7 +93,17 @@ function createClient(options) {
|
|
|
92
93
|
let flushing = null;
|
|
93
94
|
let timer;
|
|
94
95
|
const enqueue = (rec) => {
|
|
95
|
-
|
|
96
|
+
let out = rec;
|
|
97
|
+
if (beforeSend) {
|
|
98
|
+
try {
|
|
99
|
+
out = beforeSend(rec);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
onError(e);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!out) return;
|
|
106
|
+
queue.push(out);
|
|
96
107
|
while (queue.length > maxQueueSize) {
|
|
97
108
|
queue.shift();
|
|
98
109
|
dropped++;
|
package/dist/react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/core.ts","../src/client/react.ts"],"names":["uuidv7","React"],"mappings":"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","file":"react.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 * as React from 'react';\nimport type { TelemetryClient } from './core.js';\n\n/**\n * React wiring (instrumentation §7): provider, hook, error boundary. Router\n * integration stays userland — page.view semantics belong to the app.\n */\n\nconst TelemetryContext = React.createContext<TelemetryClient | null>(null);\n\nexport function TelemetryProvider(props: { client: TelemetryClient; children?: React.ReactNode }) {\n return React.createElement(TelemetryContext.Provider, { value: props.client }, props.children);\n}\n\nexport function useTelemetry(): TelemetryClient {\n const client = React.useContext(TelemetryContext);\n if (!client) {\n throw new Error('useTelemetry: no <TelemetryProvider> above this component');\n }\n return client;\n}\n\ninterface BoundaryProps {\n /** falls back to the provider's client when omitted */\n client?: TelemetryClient;\n fallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n children?: React.ReactNode;\n}\n\n/**\n * Catches render errors, reports them handled:true (the boundary DID handle\n * it — unhandled is the crash path), and renders the fallback.\n */\nexport class TelemetryErrorBoundary extends React.Component<BoundaryProps, { error: Error | null }> {\n static override contextType = TelemetryContext;\n declare context: TelemetryClient | null;\n\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo) {\n const client = this.props.client ?? this.context;\n client?.captureError(error, {\n handled: true,\n attrs: info.componentStack ? { component_stack_head: info.componentStack.split('\\n')[1]?.trim() ?? '' } : undefined,\n });\n }\n\n override render() {\n const { error } = this.state;\n if (error) {\n const { fallback } = this.props;\n return typeof fallback === 'function' ? fallback(error) : fallback ?? null;\n }\n return this.props.children ?? null;\n }\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/core.ts","../src/client/react.ts"],"names":["uuidv7","React"],"mappings":"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","file":"react.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 * as React from 'react';\nimport type { TelemetryClient } from './core.js';\n\n/**\n * React wiring (instrumentation §7): provider, hook, error boundary. Router\n * integration stays userland — page.view semantics belong to the app.\n */\n\nconst TelemetryContext = React.createContext<TelemetryClient | null>(null);\n\nexport function TelemetryProvider(props: { client: TelemetryClient; children?: React.ReactNode }) {\n return React.createElement(TelemetryContext.Provider, { value: props.client }, props.children);\n}\n\nexport function useTelemetry(): TelemetryClient {\n const client = React.useContext(TelemetryContext);\n if (!client) {\n throw new Error('useTelemetry: no <TelemetryProvider> above this component');\n }\n return client;\n}\n\ninterface BoundaryProps {\n /** falls back to the provider's client when omitted */\n client?: TelemetryClient;\n fallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n children?: React.ReactNode;\n}\n\n/**\n * Catches render errors, reports them handled:true (the boundary DID handle\n * it — unhandled is the crash path), and renders the fallback.\n */\nexport class TelemetryErrorBoundary extends React.Component<BoundaryProps, { error: Error | null }> {\n static override contextType = TelemetryContext;\n declare context: TelemetryClient | null;\n\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo) {\n const client = this.props.client ?? this.context;\n client?.captureError(error, {\n handled: true,\n attrs: info.componentStack ? { component_stack_head: info.componentStack.split('\\n')[1]?.trim() ?? '' } : undefined,\n });\n }\n\n override render() {\n const { error } = this.state;\n if (error) {\n const { fallback } = this.props;\n return typeof fallback === 'function' ? fallback(error) : fallback ?? null;\n }\n return this.props.children ?? null;\n }\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
|
package/dist/react.js
CHANGED
|
@@ -41,6 +41,7 @@ function createClient(options) {
|
|
|
41
41
|
storage = memoryStorage(),
|
|
42
42
|
consent = () => true,
|
|
43
43
|
errorName = "error.unhandled",
|
|
44
|
+
beforeSend,
|
|
44
45
|
onError = () => {
|
|
45
46
|
}
|
|
46
47
|
} = options;
|
|
@@ -70,7 +71,17 @@ function createClient(options) {
|
|
|
70
71
|
let flushing = null;
|
|
71
72
|
let timer;
|
|
72
73
|
const enqueue = (rec) => {
|
|
73
|
-
|
|
74
|
+
let out = rec;
|
|
75
|
+
if (beforeSend) {
|
|
76
|
+
try {
|
|
77
|
+
out = beforeSend(rec);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
onError(e);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!out) return;
|
|
84
|
+
queue.push(out);
|
|
74
85
|
while (queue.length > maxQueueSize) {
|
|
75
86
|
queue.shift();
|
|
76
87
|
dropped++;
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/core.ts","../src/client/react.ts"],"names":["uuidv7"],"mappings":"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","file":"react.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 * as React from 'react';\nimport type { TelemetryClient } from './core.js';\n\n/**\n * React wiring (instrumentation §7): provider, hook, error boundary. Router\n * integration stays userland — page.view semantics belong to the app.\n */\n\nconst TelemetryContext = React.createContext<TelemetryClient | null>(null);\n\nexport function TelemetryProvider(props: { client: TelemetryClient; children?: React.ReactNode }) {\n return React.createElement(TelemetryContext.Provider, { value: props.client }, props.children);\n}\n\nexport function useTelemetry(): TelemetryClient {\n const client = React.useContext(TelemetryContext);\n if (!client) {\n throw new Error('useTelemetry: no <TelemetryProvider> above this component');\n }\n return client;\n}\n\ninterface BoundaryProps {\n /** falls back to the provider's client when omitted */\n client?: TelemetryClient;\n fallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n children?: React.ReactNode;\n}\n\n/**\n * Catches render errors, reports them handled:true (the boundary DID handle\n * it — unhandled is the crash path), and renders the fallback.\n */\nexport class TelemetryErrorBoundary extends React.Component<BoundaryProps, { error: Error | null }> {\n static override contextType = TelemetryContext;\n declare context: TelemetryClient | null;\n\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo) {\n const client = this.props.client ?? this.context;\n client?.captureError(error, {\n handled: true,\n attrs: info.componentStack ? { component_stack_head: info.componentStack.split('\\n')[1]?.trim() ?? '' } : undefined,\n });\n }\n\n override render() {\n const { error } = this.state;\n if (error) {\n const { fallback } = this.props;\n return typeof fallback === 'function' ? fallback(error) : fallback ?? null;\n }\n return this.props.children ?? null;\n }\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/core.ts","../src/client/react.ts"],"names":["uuidv7"],"mappings":";;;;;;;AAiBO,IAAM,KAAA,GAAQA,MAAAA;AAuGrB,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,UAAA;AAAA,IACA,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,IAAI,GAAA,GAA4C,GAAA;AAChD,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,IAAI;AACF,QAAA,GAAA,GAAM,WAAW,GAAG,CAAA;AAAA,MACtB,SAAS,CAAA,EAAG;AACV,QAAA,OAAA,CAAQ,CAAC,CAAA;AACT,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,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;;;AChZA,IAAM,gBAAA,GAAyB,oBAAsC,IAAI,CAAA;AAElE,SAAS,kBAAkB,KAAA,EAAgE;AAChG,EAAA,OAAa,KAAA,CAAA,aAAA,CAAc,iBAAiB,QAAA,EAAU,EAAE,OAAO,KAAA,CAAM,MAAA,EAAO,EAAG,KAAA,CAAM,QAAQ,CAAA;AAC/F;AAEO,SAAS,YAAA,GAAgC;AAC9C,EAAA,MAAM,MAAA,GAAe,iBAAW,gBAAgB,CAAA;AAChD,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,MAAM,2DAA2D,CAAA;AAAA,EAC7E;AACA,EAAA,OAAO,MAAA;AACT;AAaO,IAAM,sBAAA,GAAN,cAA2C,KAAA,CAAA,SAAA,CAAkD;AAAA,EAClG,OAAgB,WAAA,GAAc,gBAAA;AAAA,EAGrB,KAAA,GAAQ,EAAE,KAAA,EAAO,IAAA,EAAqB;AAAA,EAE/C,OAAO,yBAAyB,KAAA,EAAc;AAC5C,IAAA,OAAO,EAAE,KAAA,EAAM;AAAA,EACjB;AAAA,EAES,iBAAA,CAAkB,OAAc,IAAA,EAAuB;AAC9D,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAA,IAAU,IAAA,CAAK,OAAA;AACzC,IAAA,MAAA,EAAQ,aAAa,KAAA,EAAO;AAAA,MAC1B,OAAA,EAAS,IAAA;AAAA,MACT,KAAA,EAAO,IAAA,CAAK,cAAA,GAAiB,EAAE,sBAAsB,IAAA,CAAK,cAAA,CAAe,KAAA,CAAM,IAAI,EAAE,CAAC,CAAA,EAAG,IAAA,EAAK,IAAK,IAAG,GAAI;AAAA,KAC3G,CAAA;AAAA,EACH;AAAA,EAES,MAAA,GAAS;AAChB,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,IAAA,CAAK,KAAA;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,KAAA;AAC1B,MAAA,OAAO,OAAO,QAAA,KAAa,UAAA,GAAa,QAAA,CAAS,KAAK,IAAI,QAAA,IAAY,IAAA;AAAA,IACxE;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,IAAY,IAAA;AAAA,EAChC;AACF","file":"react.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 * as React from 'react';\nimport type { TelemetryClient } from './core.js';\n\n/**\n * React wiring (instrumentation §7): provider, hook, error boundary. Router\n * integration stays userland — page.view semantics belong to the app.\n */\n\nconst TelemetryContext = React.createContext<TelemetryClient | null>(null);\n\nexport function TelemetryProvider(props: { client: TelemetryClient; children?: React.ReactNode }) {\n return React.createElement(TelemetryContext.Provider, { value: props.client }, props.children);\n}\n\nexport function useTelemetry(): TelemetryClient {\n const client = React.useContext(TelemetryContext);\n if (!client) {\n throw new Error('useTelemetry: no <TelemetryProvider> above this component');\n }\n return client;\n}\n\ninterface BoundaryProps {\n /** falls back to the provider's client when omitted */\n client?: TelemetryClient;\n fallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n children?: React.ReactNode;\n}\n\n/**\n * Catches render errors, reports them handled:true (the boundary DID handle\n * it — unhandled is the crash path), and renders the fallback.\n */\nexport class TelemetryErrorBoundary extends React.Component<BoundaryProps, { error: Error | null }> {\n static override contextType = TelemetryContext;\n declare context: TelemetryClient | null;\n\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo) {\n const client = this.props.client ?? this.context;\n client?.captureError(error, {\n handled: true,\n attrs: info.componentStack ? { component_stack_head: info.componentStack.split('\\n')[1]?.trim() ?? '' } : undefined,\n });\n }\n\n override render() {\n const { error } = this.state;\n if (error) {\n const { fallback } = this.props;\n return typeof fallback === 'function' ? fallback(error) : fallback ?? null;\n }\n return this.props.children ?? null;\n }\n}\n\nexport { createClient } from './core.js';\nexport type { TelemetryClient } from './core.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--accent: #7c3aed;--accent-soft: #ede9fe;--accent-press: #6d28d9;--accent-fg: #ffffff;--accent-border: #ddd6fe;--bg: #fafaf9;--bg-elev: #ffffff;--bg-sunken: #f5f5f4;--bg-hover: #f5f5f4;--bg-active: #ececea;--border: #ebe9e4;--border-strong: #d6d3ce;--border-input: #d6d3ce;--fg: #1c1917;--fg-muted: #57534e;--fg-subtle: #8a857d;--fg-faint: #b8b4ac;--fg-inverse: #ffffff;--green: #16a34a;--green-soft: #dcfce7;--green-fg: #15803d;--red: #dc2626;--red-soft: #fee2e2;--red-fg: #b91c1c;--amber: #d97706;--amber-soft: #fef3c7;--amber-fg: #b45309;--blue: #2563eb;--blue-soft: #dbeafe;--blue-fg: #1d4ed8;--violet: #7c3aed;--violet-soft: #ede9fe;--violet-fg: #6d28d9;--r-xs: 4px;--r-sm: 6px;--r-md: 8px;--r-lg: 12px;--r-xl: 16px;--r-pill: 999px;--shadow-sm: 0 1px 2px rgba(20, 17, 14, .04);--shadow-md: 0 2px 4px rgba(20, 17, 14, .04), 0 4px 12px rgba(20, 17, 14, .06);--shadow-lg: 0 4px 12px rgba(20, 17, 14, .06), 0 12px 32px rgba(20, 17, 14, .08);--shadow-pop: 0 8px 28px rgba(20, 17, 14, .12);--sidebar-w: 232px;--topbar-h: 56px;--content-max: 1240px;--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace}[data-theme=dark]{--accent: #a78bfa;--accent-soft: #1c1330;--accent-press: #c4b5fd;--accent-fg: #1c1917;--accent-border: #4c3a80;--bg: #0f0e0d;--bg-elev: #161514;--bg-sunken: #0a0a09;--bg-hover: #1f1d1b;--bg-active: #28251f;--border: #2a2724;--border-strong: #3a3633;--border-input: #3a3633;--fg: #f5f4f1;--fg-muted: #b8b3aa;--fg-subtle: #8a857d;--fg-faint: #5a544c;--fg-inverse: #1c1917;--green: #4ade80;--green-soft: #0f2a18;--green-fg: #86efac;--red: #f87171;--red-soft: #2a1416;--red-fg: #fca5a5;--amber: #fbbf24;--amber-soft: #2a1f0c;--amber-fg: #fcd34d;--blue: #60a5fa;--blue-soft: #0d1d33;--blue-fg: #93c5fd;--violet: #a78bfa;--violet-soft: #1c1330;--violet-fg: #c4b5fd;--shadow-sm: 0 1px 2px rgba(0, 0, 0, .35);--shadow-md: 0 2px 4px rgba(0, 0, 0, .3), 0 4px 12px rgba(0, 0, 0, .35);--shadow-lg: 0 4px 12px rgba(0, 0, 0, .35), 0 12px 32px rgba(0, 0, 0, .45);--shadow-pop: 0 8px 28px rgba(0, 0, 0, .55)}*{box-sizing:border-box}html,body,#telemetry-root{height:100%}body{margin:0;font-family:var(--font-sans);font-size:14px;line-height:1.5;color:var(--fg);background:var(--bg);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03"}button{font:inherit;color:inherit;cursor:pointer}input,textarea,select{font:inherit;color:inherit}a{color:inherit;text-decoration:none}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:999px;border:2px solid var(--bg)}::-webkit-scrollbar-thumb:hover{background:var(--fg-faint)}.app{display:grid;grid-template-columns:var(--sidebar-w) 1fr;height:100vh;background:var(--bg)}.main{display:flex;flex-direction:column;min-width:0;background:var(--bg)}.content{flex:1;overflow-y:auto;padding:24px 32px 64px}.content-inner{max-width:var(--content-max);margin:0 auto}.sidebar{background:var(--bg-elev);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0;overflow-y:auto}.sidebar-brand{display:flex;align-items:center;gap:10px;padding:18px 20px 14px;font-weight:600;letter-spacing:-.01em}.sidebar-brand-logo{width:26px;height:26px;border-radius:7px;background:linear-gradient(135deg,var(--accent) 0%,#8b5cf6 60%,#c4b5fd 100%);display:grid;place-items:center;color:#fff;font-weight:700;font-size:14px;box-shadow:inset 0 0 0 1px #ffffff2e,0 1px 2px #0000001f}.sidebar-brand-name{font-size:14.5px}.sidebar-brand-name span{color:var(--fg-muted);font-weight:400}.sidebar-section{padding:4px 14px 0}.sidebar-section-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-subtle);padding:12px 8px 6px;font-weight:600}.sidebar-nav{display:flex;flex-direction:column;gap:1px}.sidebar-link{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--r-sm);font-size:13px;color:var(--fg-muted);cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sidebar-link:hover{background:var(--bg-hover);color:var(--fg)}.sidebar-link.active{background:var(--bg-active);color:var(--fg);font-weight:500}.sidebar-link .icon{width:16px;flex:0 0 auto;opacity:.8;text-align:center}.sidebar-link.active .icon{opacity:1;color:var(--accent)}.sidebar-link-badge{margin-left:auto;font-size:11px;background:var(--bg-sunken);color:var(--fg-muted);padding:1px 6px;border-radius:var(--r-pill);font-variant-numeric:tabular-nums}.sidebar-footer{margin-top:auto;padding:12px 14px;border-top:1px solid var(--border);font-size:12px;color:var(--fg-subtle);display:flex;align-items:center;gap:8px}.sidebar-footer .dot{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 0 3px color-mix(in oklab,var(--green) 20%,transparent)}.topbar{height:var(--topbar-h);display:flex;align-items:center;gap:12px;padding:0 24px;border-bottom:1px solid var(--border);background:var(--bg-elev);flex:0 0 auto}.crumb{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--fg-muted)}.crumb-current{color:var(--fg);font-weight:500}.topbar-spacer{flex:1}.icon-btn{width:32px;height:32px;display:grid;place-items:center;border:1px solid var(--border);background:var(--bg);border-radius:var(--r-md);color:var(--fg-muted)}.icon-btn:hover{background:var(--bg-hover);color:var(--fg)}.btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;border-radius:var(--r-md);border:1px solid var(--border-strong);background:var(--bg-elev);color:var(--fg);font-size:13px;font-weight:500;white-space:nowrap;transition:background .1s ease}.btn:hover{background:var(--bg-hover)}.btn-primary{background:var(--accent);border-color:color-mix(in oklab,var(--accent) 80%,black);color:var(--accent-fg);box-shadow:0 1px #0000000f,inset 0 1px #ffffff2e}.btn-primary:hover{background:var(--accent-press)}.btn-ghost{border-color:transparent;background:transparent;color:var(--fg-muted)}.btn-ghost:hover{background:var(--bg-hover);color:var(--fg)}.btn-danger{color:var(--red-fg);border-color:var(--border);background:var(--bg-elev)}.btn-danger:hover{background:var(--red-soft)}.btn-sm{height:28px;padding:0 9px;font-size:12.5px}.page-head{display:flex;align-items:flex-start;gap:16px;margin-bottom:24px}.page-head h1{margin:0 0 4px;font-size:22px;font-weight:600;letter-spacing:-.01em}.page-head .page-desc{color:var(--fg-muted);font-size:13.5px}.page-head .right{margin-left:auto;display:flex;gap:8px;align-items:center}.card{background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden}.card-head{padding:14px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}.card-title{font-weight:600;font-size:13.5px}.card-sub{color:var(--fg-muted);font-size:12.5px}.card-actions{margin-left:auto;display:flex;gap:6px}.card-body{padding:16px}.card-pad-0 .card-body{padding:0}.card+.card{margin-top:16px}.table{width:100%;border-collapse:collapse;font-size:13px}.table th,.table td{text-align:left;padding:10px 14px;border-bottom:1px solid var(--border);vertical-align:middle}.table th{font-weight:500;color:var(--fg-subtle);font-size:12px;letter-spacing:.01em;background:var(--bg-sunken)}.table tbody tr{background:var(--bg-elev)}.table tbody tr:hover{background:var(--bg-hover);cursor:pointer}.table tbody tr:last-child td{border-bottom:0}.table td.num,.table th.num{text-align:right;font-variant-numeric:tabular-nums}.table .mono{font-family:var(--font-mono);font-size:12px}.pill{display:inline-flex;align-items:center;gap:6px;padding:2px 8px;border-radius:var(--r-pill);font-size:11.5px;font-weight:500;background:var(--bg-sunken);color:var(--fg-muted);border:1px solid var(--border);white-space:nowrap}.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor}.pill.green{background:var(--green-soft);color:var(--green-fg);border-color:transparent}.pill.red{background:var(--red-soft);color:var(--red-fg);border-color:transparent}.pill.amber{background:var(--amber-soft);color:var(--amber-fg);border-color:transparent}.pill.blue{background:var(--blue-soft);color:var(--blue-fg);border-color:transparent}.pill.violet{background:var(--violet-soft);color:var(--violet-fg);border-color:transparent}.pill.accent{background:var(--accent-soft);color:var(--accent-press);border-color:transparent}.tag{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:var(--r-xs);font-size:11.5px;background:var(--bg-sunken);border:1px solid var(--border);color:var(--fg-muted);font-family:var(--font-mono)}.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.kpi{background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--r-lg);padding:14px 16px;position:relative;overflow:hidden}.kpi-label{color:var(--fg-subtle);font-size:12px;font-weight:500}.kpi-value{font-size:24px;font-weight:600;letter-spacing:-.01em;margin:4px 0 2px;font-variant-numeric:tabular-nums}.kpi-meta{font-size:12px;color:var(--fg-muted);display:flex;align-items:center;gap:6px}.kpi-delta.up{color:var(--green-fg)}.kpi-delta.down{color:var(--red-fg)}.spark{width:100%;height:32px;display:block}.chart-card{padding:16px}.chart-axis text{font-size:10.5px;fill:var(--fg-subtle);font-family:var(--font-sans)}.chart-grid line{stroke:var(--border);stroke-dasharray:2 3}.chart-line{fill:none;stroke-width:1.5}.chart-area{opacity:.12}.chart-bar:hover{opacity:.8}.input,.select{width:100%;height:32px;padding:0 10px;border-radius:var(--r-md);border:1px solid var(--border-input);background:var(--bg-elev);font-size:13px;outline:none;transition:border .1s,box-shadow .1s}.input:focus,.select:focus{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in oklab,var(--accent) 22%,transparent)}.select{width:auto}.tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:16px}.tab{padding:8px 14px;font-size:13px;color:var(--fg-muted);border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;font-weight:500;background:none;border-top:0;border-left:0;border-right:0}.tab:hover{color:var(--fg)}.tab.active{color:var(--fg);border-bottom-color:var(--accent)}.seg{display:inline-flex;background:var(--bg-sunken);padding:2px;border-radius:var(--r-md);border:1px solid var(--border)}.seg-item{font-size:12px;padding:4px 10px;border-radius:var(--r-sm);color:var(--fg-muted);cursor:pointer;border:0;background:transparent}.seg-item.active{background:var(--bg-elev);color:var(--fg);box-shadow:var(--shadow-sm)}.split{display:grid;gap:16px}.split-2{grid-template-columns:1fr 1fr}.split-asym{grid-template-columns:minmax(0,1fr) 380px}.hstack{display:flex;align-items:center;gap:8px}.vstack{display:flex;flex-direction:column;gap:8px}.muted{color:var(--fg-muted)}.subtle{color:var(--fg-subtle)}.mono{font-family:var(--font-mono)}.tabular{font-variant-numeric:tabular-nums}.divider{height:1px;background:var(--border);margin:16px 0}.grow{flex:1}.text-xs{font-size:12px}.text-sm{font-size:13px}.f500{font-weight:500}.f600{font-weight:600}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.status-dot.green{background:var(--green);box-shadow:0 0 0 3px color-mix(in oklab,var(--green) 22%,transparent)}.status-dot.red{background:var(--red)}.status-dot.amber{background:var(--amber)}.status-dot.gray{background:var(--fg-faint)}.empty{padding:48px 24px;text-align:center;color:var(--fg-muted)}.empty h3{color:var(--fg);margin:0 0 4px;font-weight:600}.filter-bar{display:flex;gap:8px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:1px solid var(--border);border-radius:var(--r-md);background:var(--bg-elev);font-size:12.5px;color:var(--fg-muted)}.filter-chip:hover{background:var(--bg-hover);color:var(--fg)}.filter-chip.active{background:var(--accent-soft);color:var(--accent-press);border-color:var(--accent-border)}.filter-chip .scan-badge{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;background:var(--bg-sunken);border-radius:var(--r-xs);padding:0 4px;color:var(--fg-subtle)}pre.code{font-family:var(--font-mono);font-size:12px;background:var(--bg-sunken);border:1px solid var(--border);border-radius:var(--r-md);padding:12px 14px;overflow-x:auto;color:var(--fg);line-height:1.55;margin:0}.drawer-veil{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000040;z-index:40}.drawer{position:fixed;top:0;right:0;bottom:0;width:520px;max-width:90vw;background:var(--bg-elev);border-left:1px solid var(--border);box-shadow:var(--shadow-pop);z-index:41;overflow-y:auto;padding:20px}.drawer h2{margin:0 0 2px;font-size:16px}.kv{display:grid;grid-template-columns:140px 1fr;gap:4px 12px;font-size:12.5px}.kv dt{color:var(--fg-subtle)}.kv dd{margin:0;font-family:var(--font-mono);font-size:12px;word-break:break-all}.stream{display:flex;flex-direction:column}.stream-item{display:flex;gap:12px;padding:8px 4px;border-bottom:1px solid var(--border);font-size:12.5px;align-items:baseline;cursor:pointer}.stream-item:hover{background:var(--bg-hover)}.stream-time{color:var(--fg-subtle);font-family:var(--font-mono);font-size:11px;flex:0 0 128px}.stream-kind{flex:0 0 8px}.stream-name{font-weight:500}.stream-meta{color:var(--fg-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.stream-marker{background:var(--accent-soft);color:var(--accent-press);border-radius:var(--r-sm);padding:4px 10px;font-size:12px;font-weight:500;margin:8px 0}.wf-row{display:grid;grid-template-columns:220px 1fr;gap:8px;align-items:center;font-size:12px;padding:3px 0}.wf-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);font-size:11.5px;cursor:pointer}.wf-track{position:relative;height:16px;background:var(--bg-sunken);border-radius:var(--r-xs)}.wf-bar{position:absolute;top:2px;bottom:2px;border-radius:3px;min-width:2px}.wf-dot{position:absolute;top:3px;width:10px;height:10px;border-radius:50%}.wf-dur{position:absolute;right:6px;top:0;font-size:10.5px;color:var(--fg-subtle);line-height:16px;font-variant-numeric:tabular-nums}.funnel-step{display:grid;grid-template-columns:160px 1fr 90px;gap:10px;align-items:center;padding:6px 0;font-size:12.5px}.funnel-bar{height:20px;background:var(--accent);border-radius:var(--r-xs);min-width:2px;opacity:.85}.funnel-conv{color:var(--fg-subtle);font-size:11.5px;text-align:right;font-variant-numeric:tabular-nums}.matrix{border-collapse:collapse;font-size:12px}.matrix th,.matrix td{border:1px solid var(--border);padding:6px 10px;text-align:center;min-width:72px}.matrix th{background:var(--bg-sunken);color:var(--fg-subtle);font-weight:500}.matrix td{font-variant-numeric:tabular-nums}.matrix td .dwell{display:block;font-size:10px;color:var(--fg-subtle)}.frames{font-family:var(--font-mono);font-size:12px}.frame{padding:4px 10px;border-left:2px solid var(--border);color:var(--fg-subtle)}.frame.in-app{border-left-color:var(--red);color:var(--fg)}.frame .fn{color:var(--violet-fg)}.frame .loc{color:var(--fg-muted)}
|
|
1
|
+
:root{--accent: #7c3aed;--accent-soft: #ede9fe;--accent-press: #6d28d9;--accent-fg: #ffffff;--accent-border: #ddd6fe;--bg: #fafaf9;--bg-elev: #ffffff;--bg-sunken: #f5f5f4;--bg-hover: #f5f5f4;--bg-active: #ececea;--border: #ebe9e4;--border-strong: #d6d3ce;--border-input: #d6d3ce;--fg: #1c1917;--fg-muted: #57534e;--fg-subtle: #8a857d;--fg-faint: #b8b4ac;--fg-inverse: #ffffff;--green: #16a34a;--green-soft: #dcfce7;--green-fg: #15803d;--red: #dc2626;--red-soft: #fee2e2;--red-fg: #b91c1c;--amber: #d97706;--amber-soft: #fef3c7;--amber-fg: #b45309;--blue: #2563eb;--blue-soft: #dbeafe;--blue-fg: #1d4ed8;--violet: #7c3aed;--violet-soft: #ede9fe;--violet-fg: #6d28d9;--r-xs: 4px;--r-sm: 6px;--r-md: 8px;--r-lg: 12px;--r-xl: 16px;--r-pill: 999px;--shadow-sm: 0 1px 2px rgba(20, 17, 14, .04);--shadow-md: 0 2px 4px rgba(20, 17, 14, .04), 0 4px 12px rgba(20, 17, 14, .06);--shadow-lg: 0 4px 12px rgba(20, 17, 14, .06), 0 12px 32px rgba(20, 17, 14, .08);--shadow-pop: 0 8px 28px rgba(20, 17, 14, .12);--sidebar-w: 232px;--topbar-h: 56px;--content-max: 1240px;--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace}[data-theme=dark]{--accent: #a78bfa;--accent-soft: #1c1330;--accent-press: #c4b5fd;--accent-fg: #1c1917;--accent-border: #4c3a80;--bg: #0f0e0d;--bg-elev: #161514;--bg-sunken: #0a0a09;--bg-hover: #1f1d1b;--bg-active: #28251f;--border: #2a2724;--border-strong: #3a3633;--border-input: #3a3633;--fg: #f5f4f1;--fg-muted: #b8b3aa;--fg-subtle: #8a857d;--fg-faint: #5a544c;--fg-inverse: #1c1917;--green: #4ade80;--green-soft: #0f2a18;--green-fg: #86efac;--red: #f87171;--red-soft: #2a1416;--red-fg: #fca5a5;--amber: #fbbf24;--amber-soft: #2a1f0c;--amber-fg: #fcd34d;--blue: #60a5fa;--blue-soft: #0d1d33;--blue-fg: #93c5fd;--violet: #a78bfa;--violet-soft: #1c1330;--violet-fg: #c4b5fd;--shadow-sm: 0 1px 2px rgba(0, 0, 0, .35);--shadow-md: 0 2px 4px rgba(0, 0, 0, .3), 0 4px 12px rgba(0, 0, 0, .35);--shadow-lg: 0 4px 12px rgba(0, 0, 0, .35), 0 12px 32px rgba(0, 0, 0, .45);--shadow-pop: 0 8px 28px rgba(0, 0, 0, .55)}*{box-sizing:border-box}html,body,#telemetry-root{height:100%}body{margin:0;font-family:var(--font-sans);font-size:14px;line-height:1.5;color:var(--fg);background:var(--bg);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03"}button{font:inherit;color:inherit;cursor:pointer}input,textarea,select{font:inherit;color:inherit}a{color:inherit;text-decoration:none}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:999px;border:2px solid var(--bg)}::-webkit-scrollbar-thumb:hover{background:var(--fg-faint)}.app{display:grid;grid-template-columns:var(--sidebar-w) 1fr;height:100vh;background:var(--bg)}.main{display:flex;flex-direction:column;min-width:0;background:var(--bg)}.content{flex:1;overflow-y:auto;padding:24px 32px 64px}.content-inner{max-width:var(--content-max);margin:0 auto}.sidebar{background:var(--bg-elev);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0;overflow-y:auto}.sidebar-brand{display:flex;align-items:center;gap:10px;padding:18px 20px 14px;font-weight:600;letter-spacing:-.01em}.sidebar-brand-logo{width:26px;height:26px;border-radius:7px;background:linear-gradient(135deg,var(--accent) 0%,#8b5cf6 60%,#c4b5fd 100%);display:grid;place-items:center;color:#fff;font-weight:700;font-size:14px;box-shadow:inset 0 0 0 1px #ffffff2e,0 1px 2px #0000001f}.sidebar-brand-name{font-size:14.5px}.sidebar-brand-name span{color:var(--fg-muted);font-weight:400}.sidebar-section{padding:4px 14px 0}.sidebar-section-title{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-subtle);padding:12px 8px 6px;font-weight:600}.sidebar-nav{display:flex;flex-direction:column;gap:1px}.sidebar-link{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:var(--r-sm);font-size:13px;color:var(--fg-muted);cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sidebar-link:hover{background:var(--bg-hover);color:var(--fg)}.sidebar-link.active{background:var(--bg-active);color:var(--fg);font-weight:500}.sidebar-link .icon{width:16px;flex:0 0 auto;opacity:.8;text-align:center}.sidebar-link.active .icon{opacity:1;color:var(--accent)}.sidebar-link-badge{margin-left:auto;font-size:11px;background:var(--bg-sunken);color:var(--fg-muted);padding:1px 6px;border-radius:var(--r-pill);font-variant-numeric:tabular-nums}.sidebar-footer{margin-top:auto;padding:12px 14px;border-top:1px solid var(--border);font-size:12px;color:var(--fg-subtle);display:flex;align-items:center;gap:8px}.sidebar-footer .dot{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 0 3px color-mix(in oklab,var(--green) 20%,transparent)}.topbar{height:var(--topbar-h);display:flex;align-items:center;gap:12px;padding:0 24px;border-bottom:1px solid var(--border);background:var(--bg-elev);flex:0 0 auto}.crumb{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--fg-muted)}.crumb-current{color:var(--fg);font-weight:500}.topbar-spacer{flex:1}.icon-btn{width:32px;height:32px;display:grid;place-items:center;border:1px solid var(--border);background:var(--bg);border-radius:var(--r-md);color:var(--fg-muted)}.icon-btn:hover{background:var(--bg-hover);color:var(--fg)}.btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;border-radius:var(--r-md);border:1px solid var(--border-strong);background:var(--bg-elev);color:var(--fg);font-size:13px;font-weight:500;white-space:nowrap;transition:background .1s ease}.btn:hover{background:var(--bg-hover)}.btn-primary{background:var(--accent);border-color:color-mix(in oklab,var(--accent) 80%,black);color:var(--accent-fg);box-shadow:0 1px #0000000f,inset 0 1px #ffffff2e}.btn-primary:hover{background:var(--accent-press)}.btn-ghost{border-color:transparent;background:transparent;color:var(--fg-muted)}.btn-ghost:hover{background:var(--bg-hover);color:var(--fg)}.btn-danger{color:var(--red-fg);border-color:var(--border);background:var(--bg-elev)}.btn-danger:hover{background:var(--red-soft)}.btn-sm{height:28px;padding:0 9px;font-size:12.5px}.page-head{display:flex;align-items:flex-start;gap:16px;margin-bottom:24px}.page-head h1{margin:0 0 4px;font-size:22px;font-weight:600;letter-spacing:-.01em}.page-head .page-desc{color:var(--fg-muted);font-size:13.5px}.page-head .right{margin-left:auto;display:flex;gap:8px;align-items:center}.card{background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden}.card-head{padding:14px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}.card-title{font-weight:600;font-size:13.5px}.card-sub{color:var(--fg-muted);font-size:12.5px}.card-actions{margin-left:auto;display:flex;gap:6px}.card-body{padding:16px}.card-pad-0 .card-body{padding:0}.card+.card{margin-top:16px}.table{width:100%;border-collapse:collapse;font-size:13px}.table th,.table td{text-align:left;padding:10px 14px;border-bottom:1px solid var(--border);vertical-align:middle}.table th{font-weight:500;color:var(--fg-subtle);font-size:12px;letter-spacing:.01em;background:var(--bg-sunken)}.table tbody tr{background:var(--bg-elev)}.table tbody tr:hover{background:var(--bg-hover);cursor:pointer}.table tbody tr:last-child td{border-bottom:0}.table td.num,.table th.num{text-align:right;font-variant-numeric:tabular-nums}.table .mono{font-family:var(--font-mono);font-size:12px}.pill{display:inline-flex;align-items:center;gap:6px;padding:2px 8px;border-radius:var(--r-pill);font-size:11.5px;font-weight:500;background:var(--bg-sunken);color:var(--fg-muted);border:1px solid var(--border);white-space:nowrap}.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor}.pill.green{background:var(--green-soft);color:var(--green-fg);border-color:transparent}.pill.red{background:var(--red-soft);color:var(--red-fg);border-color:transparent}.pill.amber{background:var(--amber-soft);color:var(--amber-fg);border-color:transparent}.pill.blue{background:var(--blue-soft);color:var(--blue-fg);border-color:transparent}.pill.violet{background:var(--violet-soft);color:var(--violet-fg);border-color:transparent}.pill.accent{background:var(--accent-soft);color:var(--accent-press);border-color:transparent}.tag{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:var(--r-xs);font-size:11.5px;background:var(--bg-sunken);border:1px solid var(--border);color:var(--fg-muted);font-family:var(--font-mono)}.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.kpi{background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--r-lg);padding:14px 16px;position:relative;overflow:hidden}.kpi-label{color:var(--fg-subtle);font-size:12px;font-weight:500}.kpi-value{font-size:24px;font-weight:600;letter-spacing:-.01em;margin:4px 0 2px;font-variant-numeric:tabular-nums}.kpi-meta{font-size:12px;color:var(--fg-muted);display:flex;align-items:center;gap:6px}.kpi-delta.up{color:var(--green-fg)}.kpi-delta.down{color:var(--red-fg)}.spark{width:100%;height:32px;display:block}.chart-svg{width:100%;display:block}.chart-card{padding:16px}.chart-axis text{font-size:10.5px;fill:var(--fg-subtle);font-family:var(--font-sans)}.chart-grid line{stroke:var(--border);stroke-dasharray:2 3}.chart-line{fill:none;stroke-width:1.5}.chart-area{opacity:.12}.chart-bar:hover{opacity:.8}.input,.select{width:100%;height:32px;padding:0 10px;border-radius:var(--r-md);border:1px solid var(--border-input);background:var(--bg-elev);font-size:13px;outline:none;transition:border .1s,box-shadow .1s}.input:focus,.select:focus{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in oklab,var(--accent) 22%,transparent)}.select{width:auto}.tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:16px}.tab{padding:8px 14px;font-size:13px;color:var(--fg-muted);border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;font-weight:500;background:none;border-top:0;border-left:0;border-right:0}.tab:hover{color:var(--fg)}.tab.active{color:var(--fg);border-bottom-color:var(--accent)}.seg{display:inline-flex;background:var(--bg-sunken);padding:2px;border-radius:var(--r-md);border:1px solid var(--border)}.seg-item{font-size:12px;padding:4px 10px;border-radius:var(--r-sm);color:var(--fg-muted);cursor:pointer;border:0;background:transparent}.seg-item.active{background:var(--bg-elev);color:var(--fg);box-shadow:var(--shadow-sm)}.split{display:grid;gap:16px}.split-2{grid-template-columns:1fr 1fr}.split-asym{grid-template-columns:minmax(0,1fr) 380px}.hstack{display:flex;align-items:center;gap:8px}.vstack{display:flex;flex-direction:column;gap:8px}.muted{color:var(--fg-muted)}.subtle{color:var(--fg-subtle)}.mono{font-family:var(--font-mono)}.tabular{font-variant-numeric:tabular-nums}.divider{height:1px;background:var(--border);margin:16px 0}.grow{flex:1}.text-xs{font-size:12px}.text-sm{font-size:13px}.f500{font-weight:500}.f600{font-weight:600}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.status-dot.green{background:var(--green);box-shadow:0 0 0 3px color-mix(in oklab,var(--green) 22%,transparent)}.status-dot.red{background:var(--red)}.status-dot.amber{background:var(--amber)}.status-dot.gray{background:var(--fg-faint)}.empty{padding:48px 24px;text-align:center;color:var(--fg-muted)}.empty h3{color:var(--fg);margin:0 0 4px;font-weight:600}.filter-bar{display:flex;gap:8px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:1px solid var(--border);border-radius:var(--r-md);background:var(--bg-elev);font-size:12.5px;color:var(--fg-muted)}.filter-chip:hover{background:var(--bg-hover);color:var(--fg)}.filter-chip.active{background:var(--accent-soft);color:var(--accent-press);border-color:var(--accent-border)}.filter-chip .scan-badge{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;background:var(--bg-sunken);border-radius:var(--r-xs);padding:0 4px;color:var(--fg-subtle)}pre.code{font-family:var(--font-mono);font-size:12px;background:var(--bg-sunken);border:1px solid var(--border);border-radius:var(--r-md);padding:12px 14px;overflow-x:auto;color:var(--fg);line-height:1.55;margin:0}.drawer-veil{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000040;z-index:40}.drawer{position:fixed;top:0;right:0;bottom:0;width:520px;max-width:90vw;background:var(--bg-elev);border-left:1px solid var(--border);box-shadow:var(--shadow-pop);z-index:41;overflow-y:auto;padding:20px}.drawer h2{margin:0 0 2px;font-size:16px}.kv{display:grid;grid-template-columns:140px 1fr;gap:4px 12px;font-size:12.5px}.kv dt{color:var(--fg-subtle)}.kv dd{margin:0;font-family:var(--font-mono);font-size:12px;word-break:break-all}.stream{display:flex;flex-direction:column}.stream-item{display:flex;gap:12px;padding:8px 4px;border-bottom:1px solid var(--border);font-size:12.5px;align-items:baseline;cursor:pointer}.stream-item:hover{background:var(--bg-hover)}.stream-time{color:var(--fg-subtle);font-family:var(--font-mono);font-size:11px;flex:0 0 128px}.stream-kind{flex:0 0 8px}.stream-name{font-weight:500}.stream-meta{color:var(--fg-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.stream-marker{background:var(--accent-soft);color:var(--accent-press);border-radius:var(--r-sm);padding:4px 10px;font-size:12px;font-weight:500;margin:8px 0}.wf-row{display:grid;grid-template-columns:220px 1fr;gap:8px;align-items:center;font-size:12px;padding:3px 0}.wf-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);font-size:11.5px;cursor:pointer}.wf-track{position:relative;height:16px;background:var(--bg-sunken);border-radius:var(--r-xs)}.wf-bar{position:absolute;top:2px;bottom:2px;border-radius:3px;min-width:2px}.wf-dot{position:absolute;top:3px;width:10px;height:10px;border-radius:50%}.wf-dur{position:absolute;right:6px;top:0;font-size:10.5px;color:var(--fg-subtle);line-height:16px;font-variant-numeric:tabular-nums}.funnel-step{display:grid;grid-template-columns:160px 1fr 90px;gap:10px;align-items:center;padding:6px 0;font-size:12.5px}.funnel-bar{height:20px;background:var(--accent);border-radius:var(--r-xs);min-width:2px;opacity:.85}.funnel-conv{color:var(--fg-subtle);font-size:11.5px;text-align:right;font-variant-numeric:tabular-nums}.matrix{border-collapse:collapse;font-size:12px}.matrix th,.matrix td{border:1px solid var(--border);padding:6px 10px;text-align:center;min-width:72px}.matrix th{background:var(--bg-sunken);color:var(--fg-subtle);font-weight:500}.matrix td{font-variant-numeric:tabular-nums}.matrix td .dwell{display:block;font-size:10px;color:var(--fg-subtle)}.frames{font-family:var(--font-mono);font-size:12px}.frame{padding:4px 10px;border-left:2px solid var(--border);color:var(--fg-subtle)}.frame.in-app{border-left-color:var(--red);color:var(--fg)}.frame .fn{color:var(--violet-fg)}.frame .loc{color:var(--fg-muted)}
|