@kubit-ai/sentry 0.1.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.
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ /**
3
+ * Sentry `Event` -> OTLP/HTTP+JSON (`ExportTraceServiceRequest`).
4
+ *
5
+ * Two entry points behind one dispatcher (`sentryEventToOtlp`):
6
+ * - transactions (`event.type === 'transaction'`) -> spans (root from
7
+ * `contexts.trace`, one child per `event.spans[]`).
8
+ * - errors (everything else) -> a single span carrying an `exception` span
9
+ * event per `exception.values[]`, with `status.code = ERROR`.
10
+ *
11
+ * Pure functions — no Sentry SDK calls, no I/O, no randomness. Error events
12
+ * without a trace context are anchored on the Sentry `event_id` (a 32-hex
13
+ * string, valid as an OTLP trace id) so the transform stays deterministic.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.sentryEventToOtlp = exports.sentryErrorToOtlp = exports.sentryTransactionToOtlp = void 0;
17
+ const SCOPE_NAME = "kubit.sentry";
18
+ const SCOPE_VERSION = "0.1.0";
19
+ const SPAN_KIND_INTERNAL = 1;
20
+ // OTel Status enum
21
+ const STATUS_UNSET = 0;
22
+ const STATUS_OK = 1;
23
+ const STATUS_ERROR = 2;
24
+ const STATUS_ERROR_VALUES = new Set([
25
+ "cancelled",
26
+ "unknown",
27
+ "unknown_error",
28
+ "invalid_argument",
29
+ "deadline_exceeded",
30
+ "not_found",
31
+ "already_exists",
32
+ "permission_denied",
33
+ "resource_exhausted",
34
+ "failed_precondition",
35
+ "aborted",
36
+ "out_of_range",
37
+ "unimplemented",
38
+ "internal_error",
39
+ "unavailable",
40
+ "data_loss",
41
+ "unauthenticated",
42
+ ]);
43
+ const mapStatus = (status) => {
44
+ if (status === "ok") {
45
+ return STATUS_OK;
46
+ }
47
+ if (status !== undefined && STATUS_ERROR_VALUES.has(status)) {
48
+ return STATUS_ERROR;
49
+ }
50
+ return STATUS_UNSET;
51
+ };
52
+ /**
53
+ * Seconds-float -> integer-nanosecond string. Computed as whole seconds plus
54
+ * padded fractional nanos rather than `seconds * 1e9`: the product (~1.7e18)
55
+ * exceeds Number.MAX_SAFE_INTEGER and would quantize timestamps to ~256 ns,
56
+ * distorting sub-microsecond span durations.
57
+ */
58
+ const toUnixNano = (secondsFloat) => {
59
+ if (typeof secondsFloat !== "number" ||
60
+ !Number.isFinite(secondsFloat) ||
61
+ secondsFloat < 0) {
62
+ return "0";
63
+ }
64
+ let seconds = Math.floor(secondsFloat);
65
+ let nanos = Math.round((secondsFloat - seconds) * 1e9);
66
+ if (nanos >= 1e9) {
67
+ seconds += 1;
68
+ nanos = 0;
69
+ }
70
+ return seconds === 0
71
+ ? nanos.toString()
72
+ : `${seconds}${nanos.toString().padStart(9, "0")}`;
73
+ };
74
+ const toAnyValue = (value) => {
75
+ if (typeof value === "string") {
76
+ return { stringValue: value };
77
+ }
78
+ if (typeof value === "boolean") {
79
+ return { boolValue: value };
80
+ }
81
+ if (typeof value === "number") {
82
+ return Number.isInteger(value)
83
+ ? { intValue: Math.trunc(value).toString() }
84
+ : { doubleValue: value };
85
+ }
86
+ if (Array.isArray(value)) {
87
+ const values = value
88
+ .map(toAnyValue)
89
+ .filter((v) => v !== null);
90
+ return { arrayValue: { values } };
91
+ }
92
+ if (value === null || value === undefined) {
93
+ return null;
94
+ }
95
+ // Plain objects -> OTLP kvlistValue (recursively), so nested structure reaches
96
+ // the collector as real key/values instead of a JSON string. This is what lets
97
+ // e.g. each item in a `product_items` array arrive as an object, not a string.
98
+ // Arrays and scalars are already handled above.
99
+ if (typeof value === "object") {
100
+ const values = [];
101
+ for (const [key, child] of Object.entries(value)) {
102
+ const childValue = toAnyValue(child);
103
+ if (childValue !== null) {
104
+ values.push({ key, value: childValue });
105
+ }
106
+ }
107
+ return { kvlistValue: { values } };
108
+ }
109
+ // Non-object, non-scalar leftovers (bigint, symbol, function) -> string.
110
+ try {
111
+ return { stringValue: JSON.stringify(value) };
112
+ }
113
+ catch {
114
+ return { stringValue: "[uncoercible]" };
115
+ }
116
+ };
117
+ const toAttributes = (data) => {
118
+ if (!data) {
119
+ return [];
120
+ }
121
+ const out = [];
122
+ for (const [key, raw] of Object.entries(data)) {
123
+ const value = toAnyValue(raw);
124
+ if (value !== null) {
125
+ out.push({ key, value });
126
+ }
127
+ }
128
+ return out;
129
+ };
130
+ /** Conditional single string attribute — empty array when absent/blank. */
131
+ const strAttr = (key, value) => typeof value === "string" && value.length > 0
132
+ ? [{ key, value: { stringValue: value } }]
133
+ : [];
134
+ /**
135
+ * Build the OTLP `resource.attributes` array.
136
+ *
137
+ * `service.name` / `service.version` come from config first (so a customer's
138
+ * own app identity flows through), then fall back to the Sentry event's
139
+ * `release`. Browser / OS / device contexts map to OTel resource semconv.
140
+ */
141
+ const buildResourceAttributes = (event, config) => {
142
+ const browser = event.contexts?.browser;
143
+ const os = event.contexts?.os;
144
+ const device = event.contexts?.device;
145
+ return [
146
+ ...strAttr("service.name", config.serviceName ?? "sentry-app"),
147
+ ...strAttr("service.version", config.serviceVersion ?? event.release),
148
+ ...strAttr("deployment.environment", event.environment),
149
+ // SDK identity
150
+ ...strAttr("telemetry.sdk.name", event.sdk?.name),
151
+ ...strAttr("telemetry.sdk.version", event.sdk?.version),
152
+ ...strAttr("telemetry.sdk.language", event.platform),
153
+ // Browser / OS / device context (typed `unknown` via the index signature)
154
+ ...strAttr("browser.name", browser?.name),
155
+ ...strAttr("browser.version", browser?.version),
156
+ ...strAttr("os.name", os?.name),
157
+ ...strAttr("os.version", os?.version),
158
+ ...strAttr("device.model.name", device?.model),
159
+ ...strAttr("device.manufacturer", device?.brand),
160
+ ...strAttr("device.model.identifier", device?.family),
161
+ ];
162
+ };
163
+ /** String form of the Sentry user id (`string | number`). */
164
+ const userIdToString = (id) => {
165
+ if (typeof id === "string") {
166
+ return id.length > 0 ? id : undefined;
167
+ }
168
+ if (typeof id === "number" || typeof id === "bigint") {
169
+ return String(id);
170
+ }
171
+ return undefined;
172
+ };
173
+ /**
174
+ * Event-scoped attributes stamped onto every span: the injected `session.id`
175
+ * plus the Sentry user identity (`Sentry.setUser`). `user.id` is the join key
176
+ * for per-user behavior analytics — without it, events can't be grouped by user.
177
+ */
178
+ const eventScopedAttrs = (event, sessionId) => [
179
+ ...strAttr("session.id", sessionId),
180
+ ...strAttr("user.id", userIdToString(event.user?.id)),
181
+ ...strAttr("user.name", event.user?.username),
182
+ ...strAttr("user.email", event.user?.email),
183
+ ];
184
+ /**
185
+ * Append the given attributes to every span. Single choke point so all span
186
+ * families (transaction root, children, error) get them uniformly. The transform
187
+ * stays pure — the values are explicit inputs resolved by the impure tee
188
+ * boundary, not read here.
189
+ */
190
+ const withScopedAttrs = (spans, attrs) => attrs.length === 0
191
+ ? spans
192
+ : spans.map((span) => ({
193
+ ...span,
194
+ attributes: [...span.attributes, ...attrs],
195
+ }));
196
+ const buildExportRequest = (spans, resourceEvent, config, sessionId) => ({
197
+ resourceSpans: [
198
+ {
199
+ resource: { attributes: buildResourceAttributes(resourceEvent, config) },
200
+ scopeSpans: [
201
+ {
202
+ scope: { name: SCOPE_NAME, version: SCOPE_VERSION },
203
+ spans: withScopedAttrs(spans, eventScopedAttrs(resourceEvent, sessionId)),
204
+ },
205
+ ],
206
+ },
207
+ ],
208
+ });
209
+ const sentryTransactionToOtlp = (event, config, sessionId) => {
210
+ const trace = event.contexts?.trace;
211
+ const spans = [];
212
+ if (trace?.trace_id && trace.span_id) {
213
+ spans.push({
214
+ traceId: trace.trace_id,
215
+ spanId: trace.span_id,
216
+ parentSpanId: trace.parent_span_id,
217
+ name: event.transaction ?? trace.op ?? "transaction",
218
+ kind: SPAN_KIND_INTERNAL,
219
+ startTimeUnixNano: toUnixNano(event.start_timestamp),
220
+ endTimeUnixNano: toUnixNano(event.timestamp),
221
+ attributes: [
222
+ ...toAttributes(trace.data),
223
+ ...strAttr("sentry.op", trace.op),
224
+ ...strAttr("sentry.source", event.transaction_info?.source),
225
+ ],
226
+ status: { code: mapStatus(trace.status) },
227
+ });
228
+ }
229
+ for (const child of event.spans ?? []) {
230
+ spans.push({
231
+ traceId: child.trace_id,
232
+ spanId: child.span_id,
233
+ parentSpanId: child.parent_span_id,
234
+ name: child.description ?? child.op ?? "span",
235
+ kind: SPAN_KIND_INTERNAL,
236
+ startTimeUnixNano: toUnixNano(child.start_timestamp),
237
+ endTimeUnixNano: toUnixNano(child.timestamp),
238
+ attributes: [...toAttributes(child.data), ...strAttr("sentry.op", child.op)],
239
+ status: { code: mapStatus(child.status) },
240
+ });
241
+ }
242
+ return buildExportRequest(spans, event, config, sessionId);
243
+ };
244
+ exports.sentryTransactionToOtlp = sentryTransactionToOtlp;
245
+ const renderStackFrames = (frames) => frames
246
+ .map((f) => ` at ${f.function ?? "<anonymous>"} (${f.filename ?? "?"}:${f.lineno ?? 0}:${f.colno ?? 0})`)
247
+ .join("\n");
248
+ const sentryErrorToOtlp = (event, config, sessionId) => {
249
+ const trace = event.contexts?.trace;
250
+ // Anchor on the Sentry event_id (32-hex) when there's no trace context,
251
+ // so the transform stays pure (no randomness).
252
+ const traceId = trace?.trace_id ?? event.event_id;
253
+ if (!traceId) {
254
+ return buildExportRequest([], event, config, sessionId);
255
+ }
256
+ // The error span gets its own id derived from the (unique) event_id and is
257
+ // parented under the active span. Reusing `trace.span_id` as the span id
258
+ // would collide with the live span the transaction export also ships.
259
+ const spanId = event.event_id?.slice(0, 16) ?? traceId.slice(0, 16);
260
+ const parentSpanId = trace?.span_id;
261
+ const ts = toUnixNano(event.timestamp);
262
+ const values = event.exception?.values ?? [];
263
+ // Sentry orders exceptions outermost-first; the last entry is the innermost
264
+ // (most specific) error — use it for the span name.
265
+ const primary = values.length > 0 ? values[values.length - 1] : undefined;
266
+ const events = values.map((ex) => {
267
+ const attributes = [
268
+ ...strAttr("exception.type", ex.type),
269
+ ...strAttr("exception.message", ex.value),
270
+ ];
271
+ const frames = ex.stacktrace?.frames;
272
+ if (frames && frames.length > 0) {
273
+ attributes.push({
274
+ key: "exception.stacktrace",
275
+ value: { stringValue: renderStackFrames(frames) },
276
+ });
277
+ }
278
+ if (ex.mechanism?.handled !== undefined) {
279
+ attributes.push({
280
+ key: "exception.escaped",
281
+ value: { boolValue: !ex.mechanism?.handled },
282
+ });
283
+ }
284
+ return { timeUnixNano: ts, name: "exception", attributes };
285
+ });
286
+ const span = {
287
+ traceId,
288
+ spanId,
289
+ parentSpanId,
290
+ name: event.transaction ?? primary?.type ?? "error",
291
+ kind: SPAN_KIND_INTERNAL,
292
+ startTimeUnixNano: ts,
293
+ endTimeUnixNano: ts,
294
+ attributes: [
295
+ ...toAttributes(event.tags),
296
+ ...toAttributes(event.extra),
297
+ ...strAttr("sentry.level", event.level),
298
+ ...strAttr("exception.type", primary?.type),
299
+ ...strAttr("exception.message", primary?.value),
300
+ ],
301
+ events,
302
+ status: { code: STATUS_ERROR, message: primary?.value },
303
+ };
304
+ return buildExportRequest([span], event, config, sessionId);
305
+ };
306
+ exports.sentryErrorToOtlp = sentryErrorToOtlp;
307
+ const isTransactionEvent = (event) => event.type === "transaction";
308
+ /**
309
+ * Dispatcher: route a Sentry event to the right transform by `event.type`.
310
+ * Transactions carry `type: 'transaction'`; error events leave it undefined.
311
+ */
312
+ const sentryEventToOtlp = (event, config, sessionId) => isTransactionEvent(event)
313
+ ? (0, exports.sentryTransactionToOtlp)(event, config, sessionId)
314
+ : (0, exports.sentryErrorToOtlp)(event, config, sessionId);
315
+ exports.sentryEventToOtlp = sentryEventToOtlp;
316
+ //# sourceMappingURL=sentryToOtel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentryToOtel.js","sourceRoot":"","sources":["../src/sentryToOtel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Session id management for behavior analytics.
3
+ *
4
+ * `createRollingSession` returns a provider — call it once per teed event to get
5
+ * the current session id. A session keeps its id for a fixed window (default 30
6
+ * min) measured from when it STARTED, regardless of activity: a hard clock cap,
7
+ * not an inactivity timeout. When the window elapses the next call mints a fresh
8
+ * id.
9
+ *
10
+ * The first session of a fresh visitor can be SEEDED via `initialId` (a string
11
+ * or a lazy function) — e.g. a hashed access token — so the opening session is
12
+ * tied to a known identity; every later window uses `generateId`. This is the
13
+ * "one more layer" some apps want on top of the plain rolling default:
14
+ *
15
+ * // Simple: plain 30-min rolling, random ids
16
+ * createRollingSession()
17
+ *
18
+ * // Seeded: first window = a hashed identity, then roll to generated ids
19
+ * createRollingSession({ initialId: () => hashAccessToken(token) })
20
+ *
21
+ * Persisted to `localStorage` by default so the id survives page navigations;
22
+ * pass `storage: null` for in-memory only (Node, or privacy-sensitive contexts).
23
+ */
24
+ /** Minimal storage contract — a subset of the Web Storage API. */
25
+ export interface SessionStorageLike {
26
+ getItem(key: string): string | null;
27
+ setItem(key: string, value: string): void;
28
+ }
29
+ export interface RollingSessionOptions {
30
+ /** Hard cap per session, in ms. Default 1_800_000 (30 min). */
31
+ windowMs?: number;
32
+ /** Persistence key. Default "kubit-session". */
33
+ storageKey?: string;
34
+ /**
35
+ * Id for the FIRST session of a fresh visitor (no persisted session yet). A
36
+ * string, or a function evaluated once when that session starts. Every later
37
+ * window uses `generateId` instead.
38
+ */
39
+ initialId?: string | (() => string);
40
+ /** Id generator for every window after the first. Default: `crypto.randomUUID`. */
41
+ generateId?: () => string;
42
+ /**
43
+ * Storage backend. Defaults to `localStorage` when available, else in-memory.
44
+ * Pass `null` to force in-memory (no cross-reload persistence).
45
+ */
46
+ storage?: SessionStorageLike | null;
47
+ /** Clock source — injectable for tests. Default `Date.now`. */
48
+ now?: () => number;
49
+ }
50
+ /** Returns the current session id. Call once per event. */
51
+ export type SessionIdProvider = () => string;
52
+ export declare const createRollingSession: (options?: RollingSessionOptions) => SessionIdProvider;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /**
3
+ * Session id management for behavior analytics.
4
+ *
5
+ * `createRollingSession` returns a provider — call it once per teed event to get
6
+ * the current session id. A session keeps its id for a fixed window (default 30
7
+ * min) measured from when it STARTED, regardless of activity: a hard clock cap,
8
+ * not an inactivity timeout. When the window elapses the next call mints a fresh
9
+ * id.
10
+ *
11
+ * The first session of a fresh visitor can be SEEDED via `initialId` (a string
12
+ * or a lazy function) — e.g. a hashed access token — so the opening session is
13
+ * tied to a known identity; every later window uses `generateId`. This is the
14
+ * "one more layer" some apps want on top of the plain rolling default:
15
+ *
16
+ * // Simple: plain 30-min rolling, random ids
17
+ * createRollingSession()
18
+ *
19
+ * // Seeded: first window = a hashed identity, then roll to generated ids
20
+ * createRollingSession({ initialId: () => hashAccessToken(token) })
21
+ *
22
+ * Persisted to `localStorage` by default so the id survives page navigations;
23
+ * pass `storage: null` for in-memory only (Node, or privacy-sensitive contexts).
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createRollingSession = void 0;
27
+ const DEFAULT_WINDOW_MS = 30 * 60 * 1000;
28
+ const DEFAULT_STORAGE_KEY = "kubit-session";
29
+ /** `localStorage` when present (browser); null in Node or sandboxed contexts. */
30
+ const getDefaultStorage = () => {
31
+ try {
32
+ if (typeof localStorage !== "undefined") {
33
+ return localStorage;
34
+ }
35
+ }
36
+ catch {
37
+ // accessing localStorage can throw in sandboxed iframes
38
+ }
39
+ return null;
40
+ };
41
+ const defaultGenerateId = () => {
42
+ try {
43
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
44
+ return crypto.randomUUID();
45
+ }
46
+ }
47
+ catch {
48
+ // fall through to the non-crypto id
49
+ }
50
+ return `sess-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
51
+ };
52
+ const resolveSeed = (seed) => {
53
+ try {
54
+ const value = typeof seed === "function" ? seed() : seed;
55
+ return typeof value === "string" ? value : "";
56
+ }
57
+ catch {
58
+ return "";
59
+ }
60
+ };
61
+ const isLive = (saved, now, windowMs) => saved !== null && saved.id.length > 0 && now - saved.start < windowMs;
62
+ const createRollingSession = (options = {}) => {
63
+ const windowMs = options.windowMs ?? DEFAULT_WINDOW_MS;
64
+ const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
65
+ const generateId = options.generateId ?? defaultGenerateId;
66
+ const now = options.now ?? (() => Date.now());
67
+ const storage = options.storage === undefined ? getDefaultStorage() : options.storage;
68
+ // In-memory mirror — authoritative within a page, backed by `storage` across
69
+ // reloads / navigations.
70
+ let memory = null;
71
+ const read = () => {
72
+ if (memory) {
73
+ return memory;
74
+ }
75
+ if (!storage) {
76
+ return null;
77
+ }
78
+ try {
79
+ const raw = storage.getItem(storageKey);
80
+ if (!raw) {
81
+ return null;
82
+ }
83
+ const parsed = JSON.parse(raw);
84
+ if (parsed &&
85
+ typeof parsed.id === "string" &&
86
+ typeof parsed.start === "number") {
87
+ return { id: parsed.id, start: parsed.start };
88
+ }
89
+ }
90
+ catch {
91
+ // unreadable / malformed — treat as no session
92
+ }
93
+ return null;
94
+ };
95
+ const write = (session) => {
96
+ memory = session;
97
+ if (!storage) {
98
+ return;
99
+ }
100
+ try {
101
+ storage.setItem(storageKey, JSON.stringify(session));
102
+ }
103
+ catch {
104
+ // storage full / unavailable — `memory` still holds the session
105
+ }
106
+ };
107
+ return () => {
108
+ const t = now();
109
+ const saved = read();
110
+ if (isLive(saved, t, windowMs)) {
111
+ memory = saved;
112
+ return saved.id;
113
+ }
114
+ // Mint a new session. A fresh visitor (no prior session at all) may be
115
+ // seeded with `initialId`; every later window uses `generateId`.
116
+ const seeded = saved === null && options.initialId !== undefined
117
+ ? resolveSeed(options.initialId)
118
+ : "";
119
+ const id = seeded.length > 0 ? seeded : generateId();
120
+ write({ id, start: t });
121
+ return id;
122
+ };
123
+ };
124
+ exports.createRollingSession = createRollingSession;
125
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * OTLP/HTTP+JSON encoding types (trace signal only).
3
+ *
4
+ * Reference: OpenTelemetry OTLP/HTTP JSON encoding spec
5
+ * https://opentelemetry.io/docs/specs/otlp/#otlphttp-with-json-encoding
6
+ *
7
+ * These mirror the subset of the OTLP `ExportTraceServiceRequest` shape we
8
+ * emit. The Kubit collector accepts both protobuf and JSON; this SDK sends
9
+ * JSON to keep the browser build free of a protobuf dependency.
10
+ */
11
+ export type OtlpAnyValue = {
12
+ stringValue: string;
13
+ } | {
14
+ intValue: string;
15
+ } | {
16
+ doubleValue: number;
17
+ } | {
18
+ boolValue: boolean;
19
+ } | {
20
+ arrayValue: {
21
+ values: OtlpAnyValue[];
22
+ };
23
+ } | {
24
+ kvlistValue: {
25
+ values: OtlpKeyValue[];
26
+ };
27
+ };
28
+ export interface OtlpKeyValue {
29
+ key: string;
30
+ value: OtlpAnyValue;
31
+ }
32
+ /** A span event (e.g. a recorded exception). */
33
+ export interface OtlpSpanEvent {
34
+ timeUnixNano: string;
35
+ name: string;
36
+ attributes: OtlpKeyValue[];
37
+ }
38
+ export interface OtlpSpan {
39
+ traceId: string;
40
+ spanId: string;
41
+ parentSpanId?: string;
42
+ name: string;
43
+ /** SpanKind enum. 1 = INTERNAL (default for browser-side spans). */
44
+ kind: number;
45
+ startTimeUnixNano: string;
46
+ endTimeUnixNano: string;
47
+ attributes: OtlpKeyValue[];
48
+ events?: OtlpSpanEvent[];
49
+ status: {
50
+ code: number;
51
+ message?: string;
52
+ };
53
+ }
54
+ export interface OtlpExportRequest {
55
+ resourceSpans: Array<{
56
+ resource: {
57
+ attributes: OtlpKeyValue[];
58
+ };
59
+ scopeSpans: Array<{
60
+ scope: {
61
+ name: string;
62
+ version: string;
63
+ };
64
+ spans: OtlpSpan[];
65
+ }>;
66
+ }>;
67
+ }
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ /**
3
+ * OTLP/HTTP+JSON encoding types (trace signal only).
4
+ *
5
+ * Reference: OpenTelemetry OTLP/HTTP JSON encoding spec
6
+ * https://opentelemetry.io/docs/specs/otlp/#otlphttp-with-json-encoding
7
+ *
8
+ * These mirror the subset of the OTLP `ExportTraceServiceRequest` shape we
9
+ * emit. The Kubit collector accepts both protobuf and JSON; this SDK sends
10
+ * JSON to keep the browser build free of a protobuf dependency.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@kubit-ai/sentry",
3
+ "version": "0.1.0",
4
+ "description": "Tee Sentry events (errors + transactions) to Kubit as OTLP for behavior analytics.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "keywords": [
13
+ "sentry",
14
+ "opentelemetry",
15
+ "otel",
16
+ "kubit",
17
+ "observability",
18
+ "behavior-analytics",
19
+ "tracing"
20
+ ],
21
+ "author": {
22
+ "name": "Kubit Inc",
23
+ "url": "https://www.kubit.ai"
24
+ },
25
+ "license": "SEE LICENSE IN LICENSE",
26
+ "homepage": "https://www.kubit.ai",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.build.json",
35
+ "build:browser": "node scripts/build-browser.mjs",
36
+ "build:all": "npm run build && npm run build:browser",
37
+ "typecheck": "tsc",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "smoke": "npm run build && node scripts/smoke.js",
41
+ "prepublishOnly": "npm test && npm run build:all"
42
+ },
43
+ "peerDependencies": {
44
+ "@sentry/core": ">=8.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@sentry/browser": "^10.57.0",
48
+ "@sentry/core": "^10.0.0",
49
+ "@types/node": "^20.0.0",
50
+ "esbuild": "^0.28.1",
51
+ "typescript": "^5.3.0",
52
+ "vitest": "^1.0.0"
53
+ }
54
+ }