@obtrace/browser 1.0.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/auto.js +126 -0
  4. package/dist/browser/breadcrumbs.js +41 -0
  5. package/dist/browser/clicks.js +68 -0
  6. package/dist/browser/console.js +58 -0
  7. package/dist/browser/errors.js +51 -0
  8. package/dist/browser/index.js +352 -0
  9. package/dist/browser/longtasks.js +24 -0
  10. package/dist/browser/memory.js +19 -0
  11. package/dist/browser/offline.js +115 -0
  12. package/dist/browser/replay.js +127 -0
  13. package/dist/browser/resources.js +24 -0
  14. package/dist/browser/vitals.js +81 -0
  15. package/dist/browser_entry.bundle.js +28780 -0
  16. package/dist/browser_entry.bundle.js.map +7 -0
  17. package/dist/browser_entry.js +3 -0
  18. package/dist/core/client.js +80 -0
  19. package/dist/core/otel-web-setup.js +93 -0
  20. package/dist/index.js +6 -0
  21. package/dist/shared/semantic_metrics.js +28 -0
  22. package/dist/shared/types.js +1 -0
  23. package/dist/shared/utils.js +112 -0
  24. package/dist/sourcemaps.js +32 -0
  25. package/dist/types/auto.d.ts +13 -0
  26. package/dist/types/browser/breadcrumbs.d.ts +12 -0
  27. package/dist/types/browser/clicks.d.ts +2 -0
  28. package/dist/types/browser/console.d.ts +2 -0
  29. package/dist/types/browser/errors.d.ts +2 -0
  30. package/dist/types/browser/index.d.ts +25 -0
  31. package/dist/types/browser/longtasks.d.ts +2 -0
  32. package/dist/types/browser/memory.d.ts +2 -0
  33. package/dist/types/browser/offline.d.ts +3 -0
  34. package/dist/types/browser/replay.d.ts +24 -0
  35. package/dist/types/browser/resources.d.ts +2 -0
  36. package/dist/types/browser/vitals.d.ts +2 -0
  37. package/dist/types/browser_entry.d.ts +6 -0
  38. package/dist/types/core/client.d.ts +19 -0
  39. package/dist/types/core/otel-web-setup.d.ts +8 -0
  40. package/dist/types/index.d.ts +9 -0
  41. package/dist/types/shared/semantic_metrics.d.ts +26 -0
  42. package/dist/types/shared/types.d.ts +104 -0
  43. package/dist/types/shared/utils.d.ts +21 -0
  44. package/dist/types/sourcemaps.d.ts +14 -0
  45. package/dist/types/wrappers/frontend/next.d.ts +4 -0
  46. package/dist/types/wrappers/frontend/react.d.ts +8 -0
  47. package/dist/types/wrappers/frontend/vite.d.ts +5 -0
  48. package/dist/wrappers/frontend/next.js +12 -0
  49. package/dist/wrappers/frontend/react.js +20 -0
  50. package/dist/wrappers/frontend/vite.js +19 -0
  51. package/package.json +82 -0
@@ -0,0 +1,3 @@
1
+ export { initBrowserSDK } from "./browser/index";
2
+ export { SemanticMetrics } from "./shared/semantic_metrics";
3
+ export { isSemanticMetricName } from "./shared/semantic_metrics";
@@ -0,0 +1,80 @@
1
+ import { enqueueOffline } from "../browser/offline";
2
+ export class ObtraceClient {
3
+ config;
4
+ active = true;
5
+ replayTimer = null;
6
+ constructor(config) {
7
+ if (!config.apiKey || !config.ingestBaseUrl || !config.serviceName) {
8
+ throw new Error("apiKey, ingestBaseUrl and serviceName are required");
9
+ }
10
+ this.config = {
11
+ requestTimeoutMs: 5000,
12
+ defaultHeaders: {},
13
+ ...config,
14
+ apiKey: config.apiKey,
15
+ ingestBaseUrl: config.ingestBaseUrl.replace(/\/$/, ""),
16
+ serviceName: config.serviceName,
17
+ };
18
+ }
19
+ stop() {
20
+ this.active = false;
21
+ if (this.replayTimer) {
22
+ clearInterval(this.replayTimer);
23
+ this.replayTimer = null;
24
+ }
25
+ }
26
+ async shutdown() {
27
+ this.stop();
28
+ }
29
+ replayChunk(chunk) {
30
+ if (!this.active)
31
+ return;
32
+ this.sendReplay("/ingest/replay/chunk", JSON.stringify(chunk));
33
+ }
34
+ replayRecipes(steps) {
35
+ if (!this.active)
36
+ return;
37
+ this.sendReplay("/ingest/replay/recipes", JSON.stringify({ steps }));
38
+ }
39
+ injectPropagation(headers, context) {
40
+ const h = new Headers(headers);
41
+ if (this.config.propagation?.enabled === false) {
42
+ return h;
43
+ }
44
+ const sessionHeader = this.config.propagation?.sessionHeaderName ?? "x-obtrace-session-id";
45
+ if (context?.sessionId && !h.has(sessionHeader)) {
46
+ h.set(sessionHeader, context.sessionId);
47
+ }
48
+ return h;
49
+ }
50
+ sendReplay(endpoint, body) {
51
+ const ctrl = new AbortController();
52
+ const t = setTimeout(() => ctrl.abort(), this.config.requestTimeoutMs);
53
+ const hdrs = {
54
+ Authorization: `Bearer ${this.config.apiKey}`,
55
+ "Content-Type": "application/json",
56
+ ...this.config.defaultHeaders,
57
+ };
58
+ if (this.config.appId)
59
+ hdrs["X-Obtrace-App-ID"] = this.config.appId;
60
+ if (this.config.env)
61
+ hdrs["X-Obtrace-Env"] = this.config.env;
62
+ if (this.config.serviceName)
63
+ hdrs["X-Obtrace-Service-Name"] = this.config.serviceName;
64
+ const url = `${this.config.ingestBaseUrl}${endpoint}`;
65
+ fetch(url, {
66
+ method: "POST",
67
+ headers: hdrs,
68
+ body,
69
+ signal: ctrl.signal,
70
+ })
71
+ .then((res) => {
72
+ if (!res.ok)
73
+ throw new Error(`${res.status}`);
74
+ })
75
+ .catch(() => {
76
+ enqueueOffline(url, body, hdrs).catch(() => { });
77
+ })
78
+ .finally(() => clearTimeout(t));
79
+ }
80
+ }
@@ -0,0 +1,93 @@
1
+ import { trace, metrics } from "@opentelemetry/api";
2
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
3
+ import { BatchSpanProcessor, TraceIdRatioBasedSampler, ParentBasedSampler } from "@opentelemetry/sdk-trace-web";
4
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
5
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
6
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
7
+ import { Resource } from "@opentelemetry/resources";
8
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
9
+ import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
10
+ import { XMLHttpRequestInstrumentation } from "@opentelemetry/instrumentation-xml-http-request";
11
+ import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
12
+ import { UserInteractionInstrumentation } from "@opentelemetry/instrumentation-user-interaction";
13
+ import { ZoneContextManager } from "@opentelemetry/context-zone";
14
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
15
+ export function setupOtelWeb(config) {
16
+ const baseUrl = config.ingestBaseUrl.replace(/\/$/, "");
17
+ const authHeaders = {
18
+ Authorization: `Bearer ${config.apiKey}`,
19
+ ...(config.appId ? { "X-Obtrace-App-ID": config.appId } : {}),
20
+ ...(config.env ? { "X-Obtrace-Env": config.env } : {}),
21
+ ...(config.serviceName ? { "X-Obtrace-Service-Name": config.serviceName } : {}),
22
+ ...config.defaultHeaders,
23
+ };
24
+ const resource = new Resource({
25
+ [ATTR_SERVICE_NAME]: config.serviceName,
26
+ [ATTR_SERVICE_VERSION]: config.serviceVersion ?? "0.0.0",
27
+ "deployment.environment": config.env ?? "dev",
28
+ ...(config.tenantId ? { "obtrace.tenant_id": config.tenantId } : {}),
29
+ ...(config.projectId ? { "obtrace.project_id": config.projectId } : {}),
30
+ ...(config.appId ? { "obtrace.app_id": config.appId } : {}),
31
+ ...(config.env ? { "obtrace.env": config.env } : {}),
32
+ "runtime.name": "browser",
33
+ });
34
+ const traceExporter = new OTLPTraceExporter({
35
+ url: `${baseUrl}/otlp/v1/traces`,
36
+ headers: authHeaders,
37
+ });
38
+ const sampleRate = config.tracesSampleRate ?? 1;
39
+ const sampler = sampleRate < 1
40
+ ? new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(sampleRate) })
41
+ : undefined;
42
+ const tracerProvider = new WebTracerProvider({
43
+ resource,
44
+ spanProcessors: [new BatchSpanProcessor(traceExporter)],
45
+ ...(sampler ? { sampler } : {}),
46
+ });
47
+ tracerProvider.register({
48
+ contextManager: new ZoneContextManager(),
49
+ });
50
+ const metricExporter = new OTLPMetricExporter({
51
+ url: `${baseUrl}/otlp/v1/metrics`,
52
+ headers: authHeaders,
53
+ });
54
+ const meterProvider = new MeterProvider({
55
+ resource,
56
+ readers: [
57
+ new PeriodicExportingMetricReader({
58
+ exporter: metricExporter,
59
+ exportIntervalMillis: config.flushIntervalMs ?? 2000,
60
+ }),
61
+ ],
62
+ });
63
+ metrics.setGlobalMeterProvider(meterProvider);
64
+ const ingestPattern = new RegExp(`^${baseUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
65
+ const instrumentations = [];
66
+ if (config.instrumentGlobalFetch !== false) {
67
+ instrumentations.push(new FetchInstrumentation({
68
+ propagateTraceHeaderCorsUrls: /.*/,
69
+ ignoreUrls: [ingestPattern],
70
+ clearTimingResources: true,
71
+ }));
72
+ }
73
+ if (config.instrumentXHR !== false) {
74
+ instrumentations.push(new XMLHttpRequestInstrumentation({
75
+ propagateTraceHeaderCorsUrls: /.*/,
76
+ ignoreUrls: [ingestPattern],
77
+ clearTimingResources: true,
78
+ }));
79
+ }
80
+ instrumentations.push(new DocumentLoadInstrumentation());
81
+ instrumentations.push(new UserInteractionInstrumentation());
82
+ registerInstrumentations({
83
+ tracerProvider,
84
+ instrumentations,
85
+ });
86
+ const tracer = trace.getTracer("@obtrace/sdk-browser", "1.1.0");
87
+ const meter = metrics.getMeter("@obtrace/sdk-browser", "1.1.0");
88
+ const shutdown = async () => {
89
+ await tracerProvider.shutdown();
90
+ await meterProvider.shutdown();
91
+ };
92
+ return { tracer, meter, shutdown };
93
+ }
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { initBrowserSDK } from "./browser/index";
2
+ export { SemanticMetrics } from "./shared/semantic_metrics";
3
+ export { isSemanticMetricName } from "./shared/semantic_metrics";
4
+ export { initNextBrowserSDK, withNextFetchInstrumentation } from "./wrappers/frontend/next";
5
+ export { initViteBrowserSDK, createViteConfigFromImportMetaEnv } from "./wrappers/frontend/vite";
6
+ export { obtrace, getObtrace, obtraceLog, obtraceMetric, obtraceError } from "./wrappers/frontend/react";
@@ -0,0 +1,28 @@
1
+ export const SemanticMetrics = {
2
+ throughput: "http_requests_total",
3
+ errorRate: "http_5xx_total",
4
+ latencyP95: "latency_p95",
5
+ runtimeCpuUtilization: "runtime.cpu.utilization",
6
+ runtimeMemoryUsage: "runtime.memory.usage",
7
+ runtimeThreadCount: "runtime.thread.count",
8
+ runtimeGcPause: "runtime.gc.pause",
9
+ runtimeEventloopLag: "runtime.eventloop.lag",
10
+ clusterCpuUtilization: "cluster.cpu.utilization",
11
+ clusterMemoryUsage: "cluster.memory.usage",
12
+ clusterNodeCount: "cluster.node.count",
13
+ clusterPodCount: "cluster.pod.count",
14
+ dbOperationLatency: "db.operation.latency",
15
+ dbClientErrors: "db.client.errors",
16
+ dbConnectionsUsage: "db.connections.usage",
17
+ messagingConsumerLag: "messaging.consumer.lag",
18
+ webVitalLcp: "web.vital.lcp",
19
+ webVitalFcp: "web.vital.fcp",
20
+ webVitalInp: "web.vital.inp",
21
+ webVitalCls: "web.vital.cls",
22
+ webVitalTtfb: "web.vital.ttfb",
23
+ userActions: "obtrace.sim.web.react.actions",
24
+ };
25
+ const semanticMetricSet = new Set(Object.values(SemanticMetrics));
26
+ export function isSemanticMetricName(name) {
27
+ return semanticMetricSet.has(name);
28
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,112 @@
1
+ export function nowUnixNano() {
2
+ const ms = BigInt(Date.now());
3
+ return (ms * 1000000n).toString();
4
+ }
5
+ export function hex(bytes) {
6
+ return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
7
+ }
8
+ export function randomHex(lenBytes) {
9
+ const out = new Uint8Array(lenBytes);
10
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
11
+ crypto.getRandomValues(out);
12
+ }
13
+ else {
14
+ for (let i = 0; i < lenBytes; i += 1) {
15
+ out[i] = Math.floor(Math.random() * 256);
16
+ }
17
+ }
18
+ return hex(out);
19
+ }
20
+ export function toBase64(input) {
21
+ if (typeof Buffer !== "undefined") {
22
+ return Buffer.from(input, "utf8").toString("base64");
23
+ }
24
+ if (typeof btoa !== "undefined") {
25
+ return btoa(unescape(encodeURIComponent(input)));
26
+ }
27
+ throw new Error("base64 encoder unavailable");
28
+ }
29
+ export function truncate(input, max) {
30
+ if (input.length <= max) {
31
+ return input;
32
+ }
33
+ return input.slice(0, max);
34
+ }
35
+ export function safeJson(value) {
36
+ try {
37
+ return JSON.stringify(value);
38
+ }
39
+ catch {
40
+ return '"[unserializable]"';
41
+ }
42
+ }
43
+ export function sanitizeHeaders(headers) {
44
+ const out = {};
45
+ const deny = new Set(["authorization", "cookie", "set-cookie", "x-api-key", "api-key"]);
46
+ if (!headers) {
47
+ return out;
48
+ }
49
+ if (headers instanceof Headers) {
50
+ headers.forEach((v, k) => {
51
+ if (!deny.has(k.toLowerCase())) {
52
+ out[k] = v;
53
+ }
54
+ });
55
+ return out;
56
+ }
57
+ for (const [k, v] of Object.entries(headers)) {
58
+ if (!deny.has(k.toLowerCase())) {
59
+ out[k] = v;
60
+ }
61
+ }
62
+ return out;
63
+ }
64
+ export function stripQuery(url) {
65
+ try {
66
+ const u = new URL(url, "http://local");
67
+ u.search = "";
68
+ if (u.origin === "http://local") {
69
+ return `${u.pathname}${u.hash}`;
70
+ }
71
+ return u.toString();
72
+ }
73
+ catch {
74
+ return url;
75
+ }
76
+ }
77
+ export function createTraceparent(traceId, spanId) {
78
+ const t = traceId && traceId.length === 32 ? traceId : randomHex(16);
79
+ const s = spanId && spanId.length === 16 ? spanId : randomHex(8);
80
+ return `00-${t}-${s}-01`;
81
+ }
82
+ export function parseTraceparent(value) {
83
+ if (!value) {
84
+ return null;
85
+ }
86
+ const m = value.trim().match(/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i);
87
+ if (!m) {
88
+ return null;
89
+ }
90
+ return {
91
+ traceId: m[2].toLowerCase(),
92
+ parentSpanId: m[3].toLowerCase(),
93
+ flags: m[4].toLowerCase()
94
+ };
95
+ }
96
+ function readHeader(headers, key) {
97
+ const v = headers.get(key);
98
+ if (!v) {
99
+ return undefined;
100
+ }
101
+ return v;
102
+ }
103
+ export function extractPropagation(headers) {
104
+ const h = new Headers(headers);
105
+ const tp = parseTraceparent(readHeader(h, "traceparent"));
106
+ return {
107
+ traceId: tp?.traceId,
108
+ parentSpanId: tp?.parentSpanId,
109
+ traceState: readHeader(h, "tracestate"),
110
+ baggage: readHeader(h, "baggage")
111
+ };
112
+ }
@@ -0,0 +1,32 @@
1
+ export async function uploadSourceMaps(options) {
2
+ const baseUrl = options.ingestBaseUrl.replace(/\/$/, "");
3
+ const errors = [];
4
+ let uploaded = 0;
5
+ for (const file of options.files) {
6
+ try {
7
+ const res = await fetch(`${baseUrl}/ingest/sourcemaps`, {
8
+ method: "POST",
9
+ headers: {
10
+ Authorization: `Bearer ${options.apiKey}`,
11
+ "Content-Type": "application/json",
12
+ },
13
+ body: JSON.stringify({
14
+ release: options.release,
15
+ url: file.url,
16
+ sourcemap: file.sourcemap,
17
+ source: file.source,
18
+ }),
19
+ });
20
+ if (res.ok) {
21
+ uploaded++;
22
+ }
23
+ else {
24
+ errors.push(`${file.url}: ${res.status} ${res.statusText}`);
25
+ }
26
+ }
27
+ catch (err) {
28
+ errors.push(`${file.url}: ${err instanceof Error ? err.message : String(err)}`);
29
+ }
30
+ }
31
+ return { uploaded, errors };
32
+ }
@@ -0,0 +1,13 @@
1
+ import { type BrowserSDK } from "./browser/index";
2
+ declare global {
3
+ interface Window {
4
+ __OBTRACE_CONFIG__?: {
5
+ apiKey?: string;
6
+ ingestBaseUrl?: string;
7
+ serviceName?: string;
8
+ appId?: string;
9
+ env?: string;
10
+ };
11
+ }
12
+ }
13
+ export declare function getObtrace(): BrowserSDK | null;
@@ -0,0 +1,12 @@
1
+ export interface Breadcrumb {
2
+ timestamp: number;
3
+ category: string;
4
+ message: string;
5
+ level: "debug" | "info" | "warn" | "error" | "fatal";
6
+ data?: Record<string, unknown>;
7
+ }
8
+ export declare function addBreadcrumb(crumb: Breadcrumb): void;
9
+ export declare function getBreadcrumbs(): Breadcrumb[];
10
+ export declare function clearBreadcrumbs(): void;
11
+ export declare function getElementSelector(el: Element | null): string;
12
+ export declare function installClickBreadcrumbs(): () => void;
@@ -0,0 +1,2 @@
1
+ import type { Tracer } from "@opentelemetry/api";
2
+ export declare function installClickTracking(tracer: Tracer, sessionId: string): () => void;
@@ -0,0 +1,2 @@
1
+ import type { Tracer } from "@opentelemetry/api";
2
+ export declare function installConsoleCapture(tracer: Tracer, sessionId: string): () => void;
@@ -0,0 +1,2 @@
1
+ import { type Tracer } from "@opentelemetry/api";
2
+ export declare function installBrowserErrorHooks(tracer: Tracer, sessionId?: string): () => void;
@@ -0,0 +1,25 @@
1
+ import { ObtraceClient } from "../core/client";
2
+ import type { ObtraceSDKConfig, ReplayStep, SDKContext } from "../shared/types";
3
+ import { type Breadcrumb } from "./breadcrumbs";
4
+ export type UserContext = {
5
+ id?: string;
6
+ email?: string;
7
+ name?: string;
8
+ [key: string]: unknown;
9
+ };
10
+ export interface BrowserSDK {
11
+ client: ObtraceClient;
12
+ sessionId: string;
13
+ log: (level: "debug" | "info" | "warn" | "error" | "fatal", message: string, context?: SDKContext) => void;
14
+ metric: (name: string, value: number, unit?: string, context?: SDKContext) => void;
15
+ captureException: (error: unknown, context?: SDKContext) => void;
16
+ captureError: (error: unknown, context?: SDKContext) => void;
17
+ captureReplayEvent: (type: string, payload: Record<string, unknown>) => void;
18
+ flushReplay: () => void;
19
+ captureRecipe: (step: ReplayStep) => void;
20
+ instrumentFetch: () => (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
21
+ shutdown: () => Promise<void>;
22
+ setUser: (user: UserContext) => void;
23
+ addBreadcrumb: (crumb: Breadcrumb) => void;
24
+ }
25
+ export declare function initBrowserSDK(config: ObtraceSDKConfig): BrowserSDK;
@@ -0,0 +1,2 @@
1
+ import type { Tracer } from "@opentelemetry/api";
2
+ export declare function installLongTaskDetection(tracer: Tracer): () => void;
@@ -0,0 +1,2 @@
1
+ import type { Meter } from "@opentelemetry/api";
2
+ export declare function installMemoryTracking(meter: Meter): () => void;
@@ -0,0 +1,3 @@
1
+ export declare function enqueueOffline(url: string, payload: string, headers: Record<string, string>): Promise<boolean>;
2
+ export declare function drainOfflineQueue(): Promise<void>;
3
+ export declare function installOfflineSupport(): () => void;
@@ -0,0 +1,24 @@
1
+ import type { eventWithTime } from "@rrweb/types";
2
+ import type { HTTPRecord, ReplayChunk, ReplayStep } from "../shared/types";
3
+ export interface ReplayBufferConfig {
4
+ maxChunkBytes: number;
5
+ flushIntervalMs: number;
6
+ sessionStorageKey: string;
7
+ }
8
+ export declare class BrowserReplayBuffer {
9
+ private readonly cfg;
10
+ private readonly replayId;
11
+ private seq;
12
+ private events;
13
+ private bytesEstimate;
14
+ private chunkStartedAt;
15
+ constructor(cfg: ReplayBufferConfig);
16
+ get sessionId(): string;
17
+ pushRrwebEvent(event: eventWithTime): ReplayChunk | null;
18
+ pushCustomEvent(tag: string, payload: Record<string, unknown>): ReplayChunk | null;
19
+ flush(): ReplayChunk | null;
20
+ toRecipeStep(index: number, record: HTTPRecord): ReplayStep;
21
+ asNetworkEvent(record: HTTPRecord): Record<string, unknown>;
22
+ encodeBody(body: unknown): string | undefined;
23
+ private resolveReplayId;
24
+ }
@@ -0,0 +1,2 @@
1
+ import type { Meter } from "@opentelemetry/api";
2
+ export declare function installResourceTiming(meter: Meter): () => void;
@@ -0,0 +1,2 @@
1
+ import type { Meter } from "@opentelemetry/api";
2
+ export declare function installWebVitals(meter: Meter, reportAllChanges: boolean): () => void;
@@ -0,0 +1,6 @@
1
+ export { initBrowserSDK } from "./browser/index";
2
+ export type { BrowserSDK } from "./browser/index";
3
+ export { SemanticMetrics } from "./shared/semantic_metrics";
4
+ export { isSemanticMetricName } from "./shared/semantic_metrics";
5
+ export type { SemanticMetricName } from "./shared/semantic_metrics";
6
+ export type { ObtraceSDKConfig, SDKContext, ReplayStep } from "./shared/types";
@@ -0,0 +1,19 @@
1
+ import type { ObtraceSDKConfig, ReplayChunk, ReplayStep } from "../shared/types";
2
+ export declare class ObtraceClient {
3
+ private readonly config;
4
+ private active;
5
+ replayTimer: ReturnType<typeof setInterval> | null;
6
+ constructor(config: ObtraceSDKConfig);
7
+ stop(): void;
8
+ shutdown(): Promise<void>;
9
+ replayChunk(chunk: ReplayChunk): void;
10
+ replayRecipes(steps: ReplayStep[]): void;
11
+ injectPropagation(headers?: HeadersInit, context?: {
12
+ traceId?: string;
13
+ spanId?: string;
14
+ traceState?: string;
15
+ baggage?: string;
16
+ sessionId?: string;
17
+ }): Headers;
18
+ private sendReplay;
19
+ }
@@ -0,0 +1,8 @@
1
+ import { type Tracer, type Meter } from "@opentelemetry/api";
2
+ import type { ObtraceSDKConfig } from "../shared/types";
3
+ export interface OtelHandles {
4
+ tracer: Tracer;
5
+ meter: Meter;
6
+ shutdown: () => Promise<void>;
7
+ }
8
+ export declare function setupOtelWeb(config: ObtraceSDKConfig): OtelHandles;
@@ -0,0 +1,9 @@
1
+ export { initBrowserSDK } from "./browser/index";
2
+ export type { BrowserSDK } from "./browser/index";
3
+ export { SemanticMetrics } from "./shared/semantic_metrics";
4
+ export { isSemanticMetricName } from "./shared/semantic_metrics";
5
+ export type { SemanticMetricName } from "./shared/semantic_metrics";
6
+ export { initNextBrowserSDK, withNextFetchInstrumentation } from "./wrappers/frontend/next";
7
+ export { initViteBrowserSDK, createViteConfigFromImportMetaEnv } from "./wrappers/frontend/vite";
8
+ export { obtrace, getObtrace, obtraceLog, obtraceMetric, obtraceError } from "./wrappers/frontend/react";
9
+ export type { ObtraceSDKConfig, SDKContext, ReplayStep } from "./shared/types";
@@ -0,0 +1,26 @@
1
+ export declare const SemanticMetrics: {
2
+ readonly throughput: "http_requests_total";
3
+ readonly errorRate: "http_5xx_total";
4
+ readonly latencyP95: "latency_p95";
5
+ readonly runtimeCpuUtilization: "runtime.cpu.utilization";
6
+ readonly runtimeMemoryUsage: "runtime.memory.usage";
7
+ readonly runtimeThreadCount: "runtime.thread.count";
8
+ readonly runtimeGcPause: "runtime.gc.pause";
9
+ readonly runtimeEventloopLag: "runtime.eventloop.lag";
10
+ readonly clusterCpuUtilization: "cluster.cpu.utilization";
11
+ readonly clusterMemoryUsage: "cluster.memory.usage";
12
+ readonly clusterNodeCount: "cluster.node.count";
13
+ readonly clusterPodCount: "cluster.pod.count";
14
+ readonly dbOperationLatency: "db.operation.latency";
15
+ readonly dbClientErrors: "db.client.errors";
16
+ readonly dbConnectionsUsage: "db.connections.usage";
17
+ readonly messagingConsumerLag: "messaging.consumer.lag";
18
+ readonly webVitalLcp: "web.vital.lcp";
19
+ readonly webVitalFcp: "web.vital.fcp";
20
+ readonly webVitalInp: "web.vital.inp";
21
+ readonly webVitalCls: "web.vital.cls";
22
+ readonly webVitalTtfb: "web.vital.ttfb";
23
+ readonly userActions: "obtrace.sim.web.react.actions";
24
+ };
25
+ export type SemanticMetricName = (typeof SemanticMetrics)[keyof typeof SemanticMetrics];
26
+ export declare function isSemanticMetricName(name: string): name is SemanticMetricName;
@@ -0,0 +1,104 @@
1
+ export type Signal = "traces" | "logs" | "metrics";
2
+ export type LogLevel = "debug" | "info" | "warn" | "error" | "fatal";
3
+ export interface ObtraceResource {
4
+ serviceName?: string;
5
+ serviceVersion?: string;
6
+ deploymentEnv?: string;
7
+ runtimeName?: string;
8
+ runtimeVersion?: string;
9
+ appNamespace?: string;
10
+ appName?: string;
11
+ appInstanceId?: string;
12
+ [k: string]: string | number | boolean | undefined;
13
+ }
14
+ export interface ObtraceSDKConfig {
15
+ apiKey: string;
16
+ ingestBaseUrl: string;
17
+ tenantId?: string;
18
+ projectId?: string;
19
+ appId?: string;
20
+ env?: string;
21
+ serviceName: string;
22
+ serviceVersion?: string;
23
+ defaultHeaders?: Record<string, string>;
24
+ requestTimeoutMs?: number;
25
+ flushIntervalMs?: number;
26
+ maxQueueSize?: number;
27
+ maxQueueBytes?: number;
28
+ replay?: {
29
+ enabled: boolean;
30
+ flushIntervalMs?: number;
31
+ maxChunkBytes?: number;
32
+ captureNetworkRecipes?: boolean;
33
+ sessionStorageKey?: string;
34
+ blockClass?: string;
35
+ maskTextClass?: string;
36
+ maskAllInputs?: boolean;
37
+ sampling?: ReplaySamplingConfig;
38
+ };
39
+ vitals?: {
40
+ enabled: boolean;
41
+ reportAllChanges?: boolean;
42
+ };
43
+ propagation?: {
44
+ enabled: boolean;
45
+ headerName?: string;
46
+ sessionHeaderName?: string;
47
+ };
48
+ validateSemanticMetrics?: boolean;
49
+ debug?: boolean;
50
+ instrumentGlobalFetch?: boolean;
51
+ instrumentXHR?: boolean;
52
+ patchConsole?: boolean;
53
+ captureConsole?: boolean;
54
+ tracesSampleRate?: number;
55
+ replaySampleRate?: number;
56
+ }
57
+ export interface SDKContext {
58
+ traceId?: string;
59
+ spanId?: string;
60
+ traceState?: string;
61
+ baggage?: string;
62
+ sessionId?: string;
63
+ routeTemplate?: string;
64
+ endpoint?: string;
65
+ method?: string;
66
+ statusCode?: number;
67
+ attrs?: Record<string, string | number | boolean>;
68
+ }
69
+ export interface ReplayStep {
70
+ step_id: number;
71
+ method: string;
72
+ url_template: string;
73
+ headers?: Record<string, string>;
74
+ body_b64?: string;
75
+ }
76
+ export interface ReplayChunk {
77
+ replay_id: string;
78
+ seq: number;
79
+ started_at_ms: number;
80
+ ended_at_ms: number;
81
+ events: import("@rrweb/types").eventWithTime[];
82
+ metadata?: Record<string, unknown>;
83
+ }
84
+ export interface ReplaySamplingConfig {
85
+ mousemove?: boolean | number;
86
+ scroll?: number;
87
+ input?: "all" | "last";
88
+ }
89
+ export interface HTTPRecord {
90
+ ts: number;
91
+ method: string;
92
+ url: string;
93
+ status?: number;
94
+ dur_ms?: number;
95
+ req_headers?: Record<string, string>;
96
+ res_headers?: Record<string, string>;
97
+ req_body_b64?: string;
98
+ res_body_b64?: string;
99
+ }
100
+ export interface QueuedPayload {
101
+ endpoint: string;
102
+ contentType: string;
103
+ body: string;
104
+ }