@safaricom-mxl/effect-sdk 0.0.15 → 0.0.16

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.
@@ -6,6 +6,325 @@ import { Effect, Layer } from "effect";
6
6
  import * as Tracer from "effect/Tracer";
7
7
  import * as Otel from "@opentelemetry/api";
8
8
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
9
+ const RESERVED_EVENT_NAMES = ["session_start", "session_end"];
10
+ const isReservedEventName = (name) => RESERVED_EVENT_NAMES.some((reserved) => reserved === name);
11
+ const coerceTrackProps = (props) => {
12
+ if (props === void 0) return {};
13
+ const attributes = {};
14
+ for (const [key, value] of Object.entries(props)) {
15
+ if (value === void 0) continue;
16
+ attributes[key] = typeof value === "string" ? value : String(value);
17
+ }
18
+ return attributes;
19
+ };
20
+ const toEventTimestamp = (value) => (value instanceof Date ? value : new Date(value ?? Date.now())).toISOString();
21
+ const encodeProductEventLine = (input) => {
22
+ const trimmed = typeof input.name === "string" ? input.name.trim() : "";
23
+ if (trimmed.length === 0 || isReservedEventName(trimmed)) return;
24
+ const options = input.options ?? {};
25
+ return {
26
+ attributes: coerceTrackProps(options.attributes),
27
+ group_id: options.groupId ?? "",
28
+ name: trimmed.slice(0, 80),
29
+ page_path: options.pagePath ?? "",
30
+ service_name: input.serviceName,
31
+ session_id: options.sessionId ?? "",
32
+ source: input.source,
33
+ timestamp: toEventTimestamp(options.timestamp),
34
+ traceparent: options.traceparent,
35
+ url: options.url ?? "",
36
+ user_id: options.userId ?? "",
37
+ visitor_id: options.visitorId ?? ""
38
+ };
39
+ };
40
+ const encodeNdjson = (lines) => `${lines.map((line) => JSON.stringify(line)).join("\n")}\n`;
41
+ //#endregion
42
+ //#region src/shared/identity.ts
43
+ const normalizeIdentity = (input) => {
44
+ if (input === null || input === void 0) return;
45
+ if (typeof input === "string") {
46
+ const id = input.trim();
47
+ return id.length > 0 ? { id } : void 0;
48
+ }
49
+ const id = input.id.trim();
50
+ if (id.length === 0) return;
51
+ return {
52
+ email: input.email,
53
+ groupId: input.groupId,
54
+ groupName: input.groupName,
55
+ id,
56
+ traits: input.traits
57
+ };
58
+ };
59
+ //#endregion
60
+ //#region src/version.ts
61
+ const SDK_NAME = "mxl-effect-sdk";
62
+ const SDK_VERSION = "0.0.15";
63
+ //#endregion
64
+ //#region src/browser/analytics.ts
65
+ const DEFAULT_INGEST_URL = "http://127.0.0.1:8000";
66
+ const MAX_QUEUE = 100;
67
+ let config;
68
+ let identity;
69
+ let consentGranted = true;
70
+ let sessionId = "";
71
+ let queue = [];
72
+ let started = false;
73
+ const randomId = () => {
74
+ if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
75
+ return `mxl_${Math.random().toString(36).slice(2)}_${Date.now()}`;
76
+ };
77
+ const storageGet = (key) => {
78
+ try {
79
+ const value = globalThis.sessionStorage?.getItem(key);
80
+ return value && value.length > 0 ? value : void 0;
81
+ } catch {
82
+ return;
83
+ }
84
+ };
85
+ const storageSet = (key, value) => {
86
+ try {
87
+ globalThis.sessionStorage?.setItem(key, value);
88
+ } catch {}
89
+ };
90
+ const resolveSessionId = () => {
91
+ if (sessionId.length > 0) return sessionId;
92
+ sessionId = storageGet("mxl.session.id") ?? randomId();
93
+ storageSet("mxl.session.id", sessionId);
94
+ return sessionId;
95
+ };
96
+ const ingestOrigin = () => (config?.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\/$/, "");
97
+ const headers = () => {
98
+ const next = {
99
+ "content-type": "application/json",
100
+ "mxl-client-id": config?.clientId ?? "",
101
+ "mxl-sdk-name": SDK_NAME,
102
+ "mxl-sdk-version": SDK_VERSION
103
+ };
104
+ if (config?.apiKey) next["mxl-api-key"] = config.apiKey;
105
+ return next;
106
+ };
107
+ const post = (path, body, contentType, keepalive = false) => {
108
+ if (!config?.clientId) return;
109
+ try {
110
+ fetch(`${ingestOrigin()}${path}`, {
111
+ body,
112
+ headers: {
113
+ ...headers(),
114
+ "content-type": contentType
115
+ },
116
+ keepalive,
117
+ method: "POST"
118
+ });
119
+ } catch {}
120
+ };
121
+ const captureAllowed = () => Boolean(config?.clientId) && consentGranted && started;
122
+ const getCurrentIdentity = () => identity;
123
+ const getCurrentUserId = () => identity?.id;
124
+ const getCurrentSessionId = () => resolveSessionId();
125
+ const configureBrowserAnalytics = (next) => {
126
+ config = next;
127
+ consentGranted = next.privacy?.requireConsent !== true;
128
+ started = true;
129
+ resolveSessionId();
130
+ drainQueue();
131
+ };
132
+ const setConsent = (granted) => {
133
+ consentGranted = granted;
134
+ if (granted) drainQueue();
135
+ };
136
+ const identify = (input) => {
137
+ identity = normalizeIdentity(input);
138
+ if (!captureAllowed() || identity === void 0) return;
139
+ post("/track", JSON.stringify({
140
+ payload: {
141
+ email: identity.email,
142
+ firstName: typeof identity.traits?.firstName === "string" ? identity.traits.firstName : void 0,
143
+ lastName: typeof identity.traits?.lastName === "string" ? identity.traits.lastName : void 0,
144
+ profileId: identity.id,
145
+ properties: {
146
+ ...identity.traits ?? {},
147
+ groupId: identity.groupId,
148
+ groupName: identity.groupName
149
+ }
150
+ },
151
+ type: "identify"
152
+ }), "application/json");
153
+ };
154
+ const clearIdentity = () => {
155
+ identity = void 0;
156
+ };
157
+ const postProductAndSession = (name, props, keepalive = false) => {
158
+ const line = encodeProductEventLine({
159
+ name,
160
+ options: {
161
+ attributes: props,
162
+ groupId: identity?.groupId,
163
+ sessionId: resolveSessionId(),
164
+ userId: identity?.id
165
+ },
166
+ serviceName: config?.serviceName ?? "unknown",
167
+ source: "browser"
168
+ });
169
+ if (line === void 0) return;
170
+ post("/events", encodeNdjson([line]), "application/x-ndjson", keepalive);
171
+ post("/sessionEvents", `${JSON.stringify({
172
+ attributes: line.attributes,
173
+ message: line.name,
174
+ session_id: line.session_id,
175
+ timestamp: line.timestamp,
176
+ type: "custom"
177
+ })}\n`, "application/x-ndjson", keepalive);
178
+ };
179
+ const drainQueue = (keepalive = false) => {
180
+ if (!captureAllowed()) return;
181
+ const pending = queue;
182
+ queue = [];
183
+ for (const event of pending) postProductAndSession(event.name, event.props, keepalive);
184
+ };
185
+ const track = (name, props) => {
186
+ if (!started || !consentGranted || !config?.clientId) {
187
+ if (queue.length >= MAX_QUEUE) queue.shift();
188
+ queue.push({
189
+ name,
190
+ props
191
+ });
192
+ return;
193
+ }
194
+ postProductAndSession(name, props);
195
+ };
196
+ const postProfileDelta = (type, property, value) => {
197
+ if (!captureAllowed() || identity === void 0) return;
198
+ post("/track", JSON.stringify({
199
+ payload: {
200
+ profileId: identity.id,
201
+ property,
202
+ value: value ?? 1
203
+ },
204
+ type
205
+ }), "application/json");
206
+ };
207
+ const increment = (property, value) => {
208
+ postProfileDelta("increment", property, value);
209
+ };
210
+ const decrement = (property, value) => {
211
+ postProfileDelta("decrement", property, value);
212
+ };
213
+ const flushAnalytics = (keepalive = false) => {
214
+ drainQueue(keepalive);
215
+ };
216
+ //#endregion
217
+ //#region src/browser/replay.ts
218
+ let stopRecorder;
219
+ let chunkSeq = 0;
220
+ const ingestHeaders = (clientId, apiKey, contentType) => {
221
+ const headers = {
222
+ "content-type": contentType,
223
+ "mxl-client-id": clientId,
224
+ "mxl-sdk-name": SDK_NAME,
225
+ "mxl-sdk-version": SDK_VERSION
226
+ };
227
+ if (apiKey) headers["mxl-api-key"] = apiKey;
228
+ return headers;
229
+ };
230
+ const isWebSdkPresent = () => {
231
+ const global = globalThis;
232
+ return Boolean(global.__MXL_WEB_SDK__ || global.MxlWebSdk || global.__MXL_REPLAY_SESSION__);
233
+ };
234
+ const shouldRecordReplay = (replay) => {
235
+ if (isWebSdkPresent()) return false;
236
+ if (replay?.enabled === false) return false;
237
+ const sampleRate = replay?.sampleRate ?? 1;
238
+ return sampleRate >= 1 || Math.random() < sampleRate;
239
+ };
240
+ const sessionMetaRow = (serviceName) => {
241
+ const identity = getCurrentIdentity();
242
+ const started = /* @__PURE__ */ new Date();
243
+ return {
244
+ service_name: serviceName,
245
+ session_id: getCurrentSessionId(),
246
+ start_time: started.toISOString().replace("T", " ").replace("Z", ""),
247
+ status: "active",
248
+ url_initial: typeof globalThis.location?.href === "string" ? globalThis.location.href : "",
249
+ user_agent: typeof globalThis.navigator?.userAgent === "string" ? globalThis.navigator.userAgent : "",
250
+ user_id: identity?.id ?? "",
251
+ version: 1
252
+ };
253
+ };
254
+ const postSessionReplayMeta = (ingestUrl, clientId, serviceName, apiKey) => {
255
+ try {
256
+ fetch(`${ingestUrl.replace(/\/$/, "")}/sessionReplays/meta`, {
257
+ body: `${JSON.stringify(sessionMetaRow(serviceName))}\n`,
258
+ headers: ingestHeaders(clientId, apiKey, "application/x-ndjson"),
259
+ keepalive: true,
260
+ method: "POST"
261
+ });
262
+ } catch {}
263
+ };
264
+ const postSessionReplayBlob = (ingestUrl, clientId, meta, body, apiKey) => {
265
+ try {
266
+ fetch(`${ingestUrl.replace(/\/$/, "")}/sessionReplays/blob`, {
267
+ body,
268
+ headers: {
269
+ ...ingestHeaders(clientId, apiKey, "application/octet-stream"),
270
+ "x-mxl-chunk-seq": String(meta.chunkSeq),
271
+ "x-mxl-duration-ms": String(meta.durationMs),
272
+ "x-mxl-event-count": String(meta.eventCount),
273
+ "x-mxl-is-checkpoint": meta.isCheckpoint ? "1" : "0",
274
+ "x-mxl-session-id": meta.sessionId
275
+ },
276
+ keepalive: true,
277
+ method: "POST"
278
+ });
279
+ } catch {}
280
+ };
281
+ const gzipJson = async (value) => {
282
+ if (typeof CompressionStream === "undefined") return;
283
+ const encoded = new TextEncoder().encode(JSON.stringify(value));
284
+ const stream = new Blob([encoded]).stream().pipeThrough(new CompressionStream("gzip"));
285
+ return new Uint8Array(await new Response(stream).arrayBuffer());
286
+ };
287
+ const stopReplay = () => {
288
+ stopRecorder?.();
289
+ stopRecorder = void 0;
290
+ };
291
+ const startReplay = async (config) => {
292
+ if (!shouldRecordReplay(config.replay)) return;
293
+ stopReplay();
294
+ chunkSeq = 0;
295
+ postSessionReplayMeta(config.ingestUrl, config.clientId, config.serviceName, config.apiKey);
296
+ try {
297
+ const rrweb = await import(
298
+ /* @vite-ignore */
299
+ "rrweb"
300
+ );
301
+ if (typeof rrweb.record !== "function") return;
302
+ const events = [];
303
+ const startedAt = Date.now();
304
+ const stop = rrweb.record({
305
+ emit: (event) => {
306
+ events.push(event);
307
+ },
308
+ maskAllInputs: config.replay?.maskAllInputs ?? true,
309
+ maskTextSelector: config.replay?.maskAllText === true ? "*" : void 0
310
+ });
311
+ stopRecorder = () => {
312
+ stop?.();
313
+ if (events.length === 0) return;
314
+ gzipJson(events).then((gzipped) => {
315
+ if (!gzipped) return;
316
+ postSessionReplayBlob(config.ingestUrl, config.clientId, {
317
+ chunkSeq: chunkSeq++,
318
+ durationMs: Date.now() - startedAt,
319
+ eventCount: events.length,
320
+ isCheckpoint: true,
321
+ sessionId: getCurrentSessionId()
322
+ }, gzipped, config.apiKey);
323
+ });
324
+ };
325
+ } catch {}
326
+ };
327
+ //#endregion
9
328
  //#region src/server/otel-context-bridge.ts
10
329
  const OTEL_SPAN_TYPE_ID = "~@effect/opentelemetry/OtelTracer/OtelSpan";
11
330
  const evaluate = "~effect/Effect/evaluate";
@@ -50,7 +369,36 @@ function normalizeOtlpBaseUrl(url) {
50
369
  }
51
370
  //#endregion
52
371
  //#region src/browser/layer.ts
53
- var layer_exports = /* @__PURE__ */ __exportAll({ layer: () => layer });
372
+ var layer_exports = /* @__PURE__ */ __exportAll({
373
+ clearIdentity: () => clearIdentity$1,
374
+ identify: () => identify$1,
375
+ layer: () => layer,
376
+ stampUserIdOnSpan: () => stampUserIdOnSpan,
377
+ track: () => track$1
378
+ });
379
+ const stampUserIdOnSpan = (span) => {
380
+ const userId = getCurrentUserId();
381
+ if (userId) span.setAttribute("user.id", userId);
382
+ };
383
+ var UserIdSpanProcessor = class {
384
+ inner;
385
+ constructor(inner) {
386
+ this.inner = inner;
387
+ }
388
+ forceFlush() {
389
+ return this.inner.forceFlush();
390
+ }
391
+ onEnd(span) {
392
+ this.inner.onEnd(span);
393
+ }
394
+ onStart(span, parentContext) {
395
+ stampUserIdOnSpan(span);
396
+ this.inner.onStart(span, parentContext);
397
+ }
398
+ shutdown() {
399
+ return this.inner.shutdown();
400
+ }
401
+ };
54
402
  function resolveBrowserEndpoint(config) {
55
403
  if (config.disabled) return;
56
404
  if (config.endpoint && config.endpoint.length > 0) return normalizeOtlpBaseUrl(config.endpoint);
@@ -61,6 +409,22 @@ function resolveBrowserEndpoint(config) {
61
409
  * Returns a no-op layer when `disabled` is true or no OTLP endpoint is configured.
62
410
  */
63
411
  const layer = (config = {}) => {
412
+ if (config.clientId) {
413
+ configureBrowserAnalytics({
414
+ apiKey: config.apiKey,
415
+ clientId: config.clientId,
416
+ ingestUrl: config.ingestUrl,
417
+ privacy: config.privacy,
418
+ serviceName: config.serviceName
419
+ });
420
+ startReplay({
421
+ apiKey: config.apiKey,
422
+ clientId: config.clientId,
423
+ ingestUrl: config.ingestUrl ?? "http://127.0.0.1:8000",
424
+ replay: config.replay,
425
+ serviceName: config.serviceName ?? "unknown"
426
+ });
427
+ }
64
428
  const endpoint = resolveBrowserEndpoint(config);
65
429
  if (!endpoint) return Layer.empty;
66
430
  const attributes = {
@@ -73,16 +437,114 @@ const layer = (config = {}) => {
73
437
  };
74
438
  return WebSdk.layer(() => ({
75
439
  resource: {
440
+ attributes,
76
441
  serviceName: config.serviceName ?? "unknown",
77
- serviceVersion: config.serviceVersion,
78
- attributes
442
+ serviceVersion: config.serviceVersion
79
443
  },
80
- spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter({
81
- url: `${endpoint}/v1/traces`,
82
- headers: config.headers
83
- }))
444
+ spanProcessor: new UserIdSpanProcessor(new BatchSpanProcessor(new OTLPTraceExporter({
445
+ headers: config.headers,
446
+ url: `${endpoint}/v1/traces`
447
+ })))
84
448
  })).pipe(Layer.updateService(Tracer.Tracer, wrapTracerWithOtelContextBridge));
85
449
  };
450
+ const identify$1 = (input) => Effect.sync(() => identify(input));
451
+ const clearIdentity$1 = Effect.sync(() => clearIdentity());
452
+ const track$1 = (name, props) => Effect.sync(() => track(name, props));
453
+ //#endregion
454
+ //#region src/shared/flush-core.ts
455
+ const COOLDOWN_MS = 6e4;
456
+ const makeSerializedFlush = (flush) => {
457
+ let pending = Promise.resolve();
458
+ return (...args) => {
459
+ pending = pending.then(() => flush(...args), () => flush(...args));
460
+ return pending;
461
+ };
462
+ };
463
+ const guardFlush = (prefix, flush, state) => {
464
+ return async (...args) => {
465
+ if (state && Date.now() < state.disabledUntil) return;
466
+ try {
467
+ await flush(...args);
468
+ } catch (error) {
469
+ if (state) state.disabledUntil = Date.now() + COOLDOWN_MS;
470
+ console.error(`${prefix} flush failed; cooling down 60s`, error);
471
+ }
472
+ };
473
+ };
474
+ //#endregion
475
+ //#region src/browser/flushable.ts
476
+ var flushable_exports = /* @__PURE__ */ __exportAll({
477
+ attachProcessor: () => attachProcessor,
478
+ make: () => make
479
+ });
480
+ const processors = /* @__PURE__ */ new WeakMap();
481
+ const DEFAULT_AUTO_FLUSH_MS = 5e3;
482
+ const attachProcessor = (telemetry, processor) => {
483
+ processors.set(telemetry, processor);
484
+ };
485
+ const make = (config = {}) => {
486
+ const layer$1 = layer(config);
487
+ const state = { disabledUntil: 0 };
488
+ const listeners = [];
489
+ const telemetry = {
490
+ captureException: (error, options) => {
491
+ track(options?.name ?? "uncaught_error", {
492
+ ...options?.attributes,
493
+ message: error instanceof Error ? error.message : String(error)
494
+ });
495
+ },
496
+ dispose: async () => {
497
+ for (const stop of listeners) stop();
498
+ listeners.length = 0;
499
+ stopReplay();
500
+ await telemetry.flush();
501
+ },
502
+ flush: makeSerializedFlush(guardFlush("[MxlFlush]", async () => {
503
+ flushAnalytics(true);
504
+ await processors.get(telemetry)?.forceFlush();
505
+ }, state)),
506
+ layer: layer$1
507
+ };
508
+ const intervalMs = config.autoFlushInterval === void 0 ? DEFAULT_AUTO_FLUSH_MS : config.autoFlushInterval === false ? 0 : config.autoFlushInterval;
509
+ if (intervalMs > 0) {
510
+ const timer = setInterval(() => {
511
+ telemetry.flush();
512
+ }, intervalMs);
513
+ timer.unref?.();
514
+ listeners.push(() => {
515
+ clearInterval(timer);
516
+ });
517
+ }
518
+ const documentRef = globalThis.document;
519
+ if (config.flushOnUnload !== false && documentRef) {
520
+ const onHidden = () => {
521
+ telemetry.flush();
522
+ };
523
+ const onVisibilityChange = () => {
524
+ if (documentRef.visibilityState === "hidden") onHidden();
525
+ };
526
+ documentRef.addEventListener("visibilitychange", onVisibilityChange);
527
+ globalThis.addEventListener?.("pagehide", onHidden);
528
+ listeners.push(() => {
529
+ documentRef.removeEventListener("visibilitychange", onVisibilityChange);
530
+ globalThis.removeEventListener?.("pagehide", onHidden);
531
+ });
532
+ }
533
+ if (config.captureGlobalErrors !== false && typeof globalThis.addEventListener === "function") {
534
+ const onError = (event) => {
535
+ const record = event;
536
+ telemetry.captureException(record.error ?? record.reason ?? event);
537
+ telemetry.flush();
538
+ };
539
+ globalThis.addEventListener("error", onError);
540
+ globalThis.addEventListener("unhandledrejection", onError);
541
+ listeners.push(() => {
542
+ globalThis.removeEventListener("error", onError);
543
+ globalThis.removeEventListener("unhandledrejection", onError);
544
+ });
545
+ }
546
+ return telemetry;
547
+ };
86
548
  //#endregion
87
549
  //#region src/shared/dependency-spans.ts
88
550
  const MAX_FLUSH_SPAN_LINKS = 32;
@@ -189,6 +651,6 @@ async function withPromiseDependencySpan(name, attributes, operation, kind = "cl
189
651
  });
190
652
  }
191
653
  //#endregion
192
- export { layer_exports as Mxl, addSpanLinksFromTraceCarriers, bufferSpanAttributes, dbClientSpanAttributes, geoSpanAttributes, kafkaProducerSpanAttributes, queueConsumerSpanAttributes, queueProducerSpanAttributes, withDependencySpan, withPromiseDependencySpan };
654
+ export { layer_exports as Mxl, flushable_exports as MxlFlush, addSpanLinksFromTraceCarriers, bufferSpanAttributes, clearIdentity, dbClientSpanAttributes, decrement, geoSpanAttributes, identify, increment, kafkaProducerSpanAttributes, queueConsumerSpanAttributes, queueProducerSpanAttributes, setConsent, track, withDependencySpan, withPromiseDependencySpan };
193
655
 
194
656
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["traceApi","otelContext"],"sources":["../../src/server/otel-context-bridge.ts","../../src/server/otlp.ts","../../src/browser/layer.ts","../../src/shared/dependency-spans.ts"],"sourcesContent":["import * as Otel from \"@opentelemetry/api\";\nimport { Effect, Layer } from \"effect\";\nimport * as Tracer from \"effect/Tracer\";\n\nconst OTEL_SPAN_TYPE_ID = \"~@effect/opentelemetry/OtelTracer/OtelSpan\";\nconst evaluate = \"~effect/Effect/evaluate\" as const;\n\ninterface OtelWrappedSpan {\n readonly span: Otel.Span;\n}\n\nexport function formatTraceparent(span: Tracer.AnySpan): string {\n const flags = span.sampled ? \"01\" : \"00\";\n return `00-${span.traceId}-${span.spanId}-${flags}`;\n}\n\nexport function buildTraceCarrierFromSpan(\n span: Tracer.AnySpan,\n headers?: Record<string, string>\n): Record<string, string> {\n const carrier: Record<string, string> = {\n traceparent: formatTraceparent(span),\n };\n\n if (headers?.tracestate) {\n carrier.tracestate = headers.tracestate;\n }\n if (headers?.baggage) {\n carrier.baggage = headers.baggage;\n }\n\n return carrier;\n}\n\n/** Map an Effect span to an OpenTelemetry context with the span active. */\nexport function effectSpanToOtelContext(\n activeContext: Otel.Context,\n span: Tracer.AnySpan\n): Otel.Context {\n if (\n typeof span === \"object\" &&\n span !== null &&\n OTEL_SPAN_TYPE_ID in span &&\n \"span\" in span\n ) {\n return Otel.trace.setSpan(activeContext, (span as OtelWrappedSpan).span);\n }\n\n return Otel.trace.setSpanContext(activeContext, {\n isRemote: span._tag === \"ExternalSpan\",\n spanId: span.spanId,\n traceFlags: span.sampled ? Otel.TraceFlags.SAMPLED : Otel.TraceFlags.NONE,\n traceId: span.traceId,\n });\n}\n\n/** Wrap a tracer so Effect spans activate global `@opentelemetry/api` context. */\nexport function wrapTracerWithOtelContextBridge(\n base: Tracer.Tracer\n): Tracer.Tracer {\n const existingContext = base.context;\n\n return Tracer.make({\n span(options) {\n return base.span(options);\n },\n context(primitive, fiber) {\n if (fiber.cache.span === undefined) {\n if (existingContext) {\n return existingContext(primitive, fiber);\n }\n return primitive[evaluate](fiber);\n }\n\n return Otel.context.with(\n effectSpanToOtelContext(Otel.context.active(), fiber.cache.span),\n () => {\n if (existingContext) {\n return existingContext(primitive, fiber);\n }\n return primitive[evaluate](fiber);\n }\n );\n },\n });\n}\n\n/**\n * Layer that wraps the configured Effect tracer with a global OTel context bridge.\n *\n * Must be merged after `Otlp.layerJson` so queue propagation, log correlation,\n * and outbound W3C headers see the active server span.\n */\nexport const otelGlobalContextBridgeLayer = Layer.effect(\n Tracer.Tracer,\n Effect.gen(function* () {\n const base = yield* Tracer.Tracer;\n return wrapTracerWithOtelContextBridge(base);\n })\n);\n","export function normalizeOtlpBaseUrl(url: string): string {\n const trimmed = url.replace(/\\/$/, \"\");\n if (trimmed.endsWith(\"/v1/traces\") || trimmed.endsWith(\"/v1/logs\")) {\n return trimmed.replace(/\\/v1\\/(traces|logs|metrics)$/, \"\");\n }\n return trimmed;\n}\n","import * as WebSdk from \"@effect/opentelemetry/WebSdk\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { Layer } from \"effect\";\nimport * as Tracer from \"effect/Tracer\";\n\nimport { wrapTracerWithOtelContextBridge } from \"../server/otel-context-bridge.js\";\nimport { normalizeOtlpBaseUrl } from \"../server/otlp.js\";\n\nexport interface MxlBrowserConfig {\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n readonly environment?: string | undefined;\n readonly endpoint?: string | undefined;\n readonly headers?: Record<string, string> | undefined;\n readonly attributes?: Record<string, string> | undefined;\n readonly disabled?: boolean | undefined;\n}\n\nfunction resolveBrowserEndpoint(config: MxlBrowserConfig): string | undefined {\n if (config.disabled) {\n return undefined;\n }\n if (config.endpoint && config.endpoint.length > 0) {\n return normalizeOtlpBaseUrl(config.endpoint);\n }\n return undefined;\n}\n\n/**\n * Browser Effect observability layer backed by OpenTelemetry WebSdk.\n *\n * Returns a no-op layer when `disabled` is true or no OTLP endpoint is configured.\n */\nexport const layer = (config: MxlBrowserConfig = {}) => {\n const endpoint = resolveBrowserEndpoint(config);\n if (!endpoint) {\n return Layer.empty;\n }\n\n const attributes: Record<string, string> = {\n \"mxl.sdk.type\": \"browser\",\n ...(config.environment\n ? {\n \"deployment.environment\": config.environment,\n \"deployment.environment.name\": config.environment,\n }\n : {}),\n ...(config.attributes ?? {}),\n };\n\n return WebSdk.layer(() => ({\n resource: {\n serviceName: config.serviceName ?? \"unknown\",\n serviceVersion: config.serviceVersion,\n attributes,\n },\n spanProcessor: new BatchSpanProcessor(\n new OTLPTraceExporter({\n url: `${endpoint}/v1/traces`,\n headers: config.headers,\n })\n ),\n })).pipe(Layer.updateService(Tracer.Tracer, wrapTracerWithOtelContextBridge));\n};\n","import {\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace as traceApi,\n} from \"@opentelemetry/api\";\nimport { Effect } from \"effect\";\n\nexport type DbSystem = \"clickhouse\" | \"postgresql\" | \"redis\";\n\nexport type DependencySpanKind = \"client\" | \"producer\" | \"consumer\";\n\nconst MAX_FLUSH_SPAN_LINKS = 32;\n\nexport interface QueueTraceContextCarrier {\n readonly traceparent?: string | undefined;\n readonly tracestate?: string | undefined;\n readonly baggage?: string | undefined;\n}\n\nexport function dbClientSpanAttributes(\n dbSystem: DbSystem,\n operation?: string\n): Record<string, string> {\n return {\n \"db.system\": dbSystem,\n \"db.system.name\": dbSystem,\n \"peer.service\": dbSystem,\n ...(operation ? { \"db.operation.name\": operation } : {}),\n };\n}\n\nexport function queueProducerSpanAttributes(\n destinationName: string,\n peerService = \"mxl-worker\"\n): Record<string, string> {\n return {\n \"messaging.destination.name\": destinationName,\n \"messaging.system\": \"bullmq\",\n \"peer.service\": peerService,\n };\n}\n\nexport function queueConsumerSpanAttributes(\n destinationName: string\n): Record<string, string> {\n return {\n \"messaging.destination.name\": destinationName,\n \"messaging.system\": \"bullmq\",\n };\n}\n\nexport function kafkaProducerSpanAttributes(\n topic: string,\n peerService = \"kafka\"\n): Record<string, string> {\n return {\n \"messaging.destination.name\": topic,\n \"messaging.system\": \"kafka\",\n \"peer.service\": peerService,\n };\n}\n\nexport function bufferSpanAttributes(\n bufferName: string,\n operation: string\n): Record<string, string> {\n return {\n \"buffer.name\": bufferName,\n \"db.operation.name\": operation,\n \"db.system\": \"redis\",\n \"db.system.name\": \"redis\",\n \"peer.service\": \"redis\",\n };\n}\n\nexport function geoSpanAttributes(): Record<string, string> {\n return {\n \"db.operation.name\": \"resolve\",\n \"db.system\": \"maxmind\",\n \"db.system.name\": \"maxmind\",\n \"peer.service\": \"maxmind\",\n };\n}\n\nexport function addSpanLinksFromTraceCarriers(\n carriers: readonly QueueTraceContextCarrier[]\n): number {\n const span = traceApi.getActiveSpan();\n if (!span) {\n return 0;\n }\n\n const seen = new Set<string>();\n let linked = 0;\n\n for (const carrier of carriers) {\n if (!carrier.traceparent || seen.has(carrier.traceparent)) {\n continue;\n }\n seen.add(carrier.traceparent);\n\n const extracted = propagation.extract(otelContext.active(), carrier);\n const linkedContext = traceApi.getSpanContext(extracted);\n if (!linkedContext) {\n continue;\n }\n\n span.addLink({ context: linkedContext });\n linked += 1;\n\n if (linked >= MAX_FLUSH_SPAN_LINKS) {\n break;\n }\n }\n\n return linked;\n}\n\nexport interface WithDependencySpanOptions {\n readonly attributes?: Record<string, string> | undefined;\n readonly kind?: DependencySpanKind | undefined;\n}\n\nexport function withDependencySpan<A, E, R>(\n name: string,\n attributes: Record<string, string>,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R>;\nexport function withDependencySpan<A, E, R>(\n name: string,\n options: WithDependencySpanOptions,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R>;\nexport function withDependencySpan<A, E, R>(\n name: string,\n attributesOrOptions: Record<string, string> | WithDependencySpanOptions,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R> {\n const options = isWithDependencySpanOptions(attributesOrOptions)\n ? attributesOrOptions\n : { attributes: attributesOrOptions };\n\n const attributes = options.attributes ?? {};\n const kind: DependencySpanKind = options.kind ?? \"client\";\n\n return program.pipe(Effect.withSpan(name, { attributes, kind }));\n}\n\nfunction isWithDependencySpanOptions(\n value: Record<string, string> | WithDependencySpanOptions\n): value is WithDependencySpanOptions {\n return (\n typeof value === \"object\" &&\n (\"kind\" in value ||\n (\"attributes\" in value && typeof value.attributes === \"object\"))\n );\n}\n\n/** Promise-based dependency span for non-Effect code paths. */\nexport async function withPromiseDependencySpan<T>(\n name: string,\n attributes: Record<string, string>,\n operation: () => Promise<T>,\n kind: DependencySpanKind = \"client\"\n): Promise<T> {\n const tracer = traceApi.getTracer(\"mxl-effect-sdk\");\n const spanKind =\n kind === \"producer\"\n ? SpanKind.PRODUCER\n : kind === \"consumer\"\n ? SpanKind.CONSUMER\n : SpanKind.CLIENT;\n return tracer.startActiveSpan(\n name,\n { attributes, kind: spanKind },\n async (span) => {\n try {\n const result = await operation();\n span.end();\n return result;\n } catch (error) {\n if (error instanceof Error) {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(error),\n });\n }\n span.end();\n throw error;\n }\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["identify","identifySync","clearIdentity","clearIdentitySync","track","trackSync","layer","browserLayer","traceApi","otelContext"],"sources":["../../src/shared/product-event.ts","../../src/shared/identity.ts","../../src/version.ts","../../src/browser/analytics.ts","../../src/browser/replay.ts","../../src/server/otel-context-bridge.ts","../../src/server/otlp.ts","../../src/browser/layer.ts","../../src/shared/flush-core.ts","../../src/browser/flushable.ts","../../src/shared/dependency-spans.ts"],"sourcesContent":["export type ProductEventSource = \"server\" | \"browser\";\n\nexport const MAX_EVENT_NAME_LENGTH = 80;\nexport const RESERVED_EVENT_NAMES = [\"session_start\", \"session_end\"] as const;\n\nexport interface ProductEventLine {\n readonly attributes: Record<string, string>;\n readonly group_id: string;\n readonly name: string;\n readonly page_path: string;\n readonly service_name: string;\n readonly session_id: string;\n readonly source: ProductEventSource;\n readonly timestamp: string;\n readonly traceparent?: string | undefined;\n readonly url: string;\n readonly user_id: string;\n readonly visitor_id: string;\n}\n\nexport interface TrackProps {\n readonly [key: string]: unknown;\n}\n\nexport interface TrackOptions {\n readonly attributes?: TrackProps | undefined;\n readonly groupId?: string | undefined;\n readonly pagePath?: string | undefined;\n readonly sessionId?: string | undefined;\n readonly timestamp?: Date | number | undefined;\n readonly traceparent?: string | undefined;\n readonly url?: string | undefined;\n readonly userId?: string | undefined;\n readonly visitorId?: string | undefined;\n}\n\nconst isReservedEventName = (name: string): boolean =>\n RESERVED_EVENT_NAMES.some((reserved) => reserved === name);\n\nexport const coerceTrackProps = (\n props: TrackProps | undefined\n): Record<string, string> => {\n if (props === undefined) {\n return {};\n }\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(props)) {\n if (value === undefined) {\n continue;\n }\n attributes[key] = typeof value === \"string\" ? value : String(value);\n }\n return attributes;\n};\n\nexport const toEventTimestamp = (value: Date | number | undefined): string =>\n (value instanceof Date ? value : new Date(value ?? Date.now())).toISOString();\n\nexport const encodeProductEventLine = (input: {\n readonly name: string;\n readonly options?: TrackOptions | undefined;\n readonly serviceName: string;\n readonly source: ProductEventSource;\n}): ProductEventLine | undefined => {\n const trimmed = typeof input.name === \"string\" ? input.name.trim() : \"\";\n if (trimmed.length === 0 || isReservedEventName(trimmed)) {\n return undefined;\n }\n const options = input.options ?? {};\n return {\n attributes: coerceTrackProps(options.attributes),\n group_id: options.groupId ?? \"\",\n name: trimmed.slice(0, MAX_EVENT_NAME_LENGTH),\n page_path: options.pagePath ?? \"\",\n service_name: input.serviceName,\n session_id: options.sessionId ?? \"\",\n source: input.source,\n timestamp: toEventTimestamp(options.timestamp),\n traceparent: options.traceparent,\n url: options.url ?? \"\",\n user_id: options.userId ?? \"\",\n visitor_id: options.visitorId ?? \"\",\n };\n};\n\nexport const encodeNdjson = (lines: ReadonlyArray<ProductEventLine>): string =>\n `${lines.map((line) => JSON.stringify(line)).join(\"\\n\")}\\n`;\n","export interface MapleIdentity {\n readonly email?: string | undefined;\n readonly groupId?: string | undefined;\n readonly groupName?: string | undefined;\n readonly id: string;\n readonly traits?: Record<string, unknown> | undefined;\n}\n\nexport type IdentifyInput = string | MapleIdentity | null | undefined;\n\nexport const normalizeIdentity = (\n input: IdentifyInput\n): MapleIdentity | undefined => {\n if (input === null || input === undefined) {\n return undefined;\n }\n if (typeof input === \"string\") {\n const id = input.trim();\n return id.length > 0 ? { id } : undefined;\n }\n const id = input.id.trim();\n if (id.length === 0) {\n return undefined;\n }\n return {\n email: input.email,\n groupId: input.groupId,\n groupName: input.groupName,\n id,\n traits: input.traits,\n };\n};\n","export const SDK_NAME = \"mxl-effect-sdk\";\nexport const SDK_VERSION = \"0.0.15\";\n","import {\n encodeNdjson,\n encodeProductEventLine,\n type TrackProps,\n} from \"../shared/product-event.js\";\nimport {\n type IdentifyInput,\n type MapleIdentity,\n normalizeIdentity,\n} from \"../shared/identity.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\nexport type { IdentifyInput, MapleIdentity, TrackProps };\n\nexport interface PrivacyOptions {\n readonly requireConsent?: boolean | undefined;\n}\n\nexport interface BrowserAnalyticsConfig {\n readonly apiKey?: string | undefined;\n readonly clientId?: string | undefined;\n readonly ingestUrl?: string | undefined;\n readonly privacy?: PrivacyOptions | undefined;\n readonly serviceName?: string | undefined;\n}\n\ninterface QueuedCustomEvent {\n readonly name: string;\n readonly props?: TrackProps | undefined;\n}\n\nconst DEFAULT_INGEST_URL = \"http://127.0.0.1:8000\";\nconst MAX_QUEUE = 100;\n\nlet config: BrowserAnalyticsConfig | undefined;\nlet identity: MapleIdentity | undefined;\nlet consentGranted = true;\nlet sessionId = \"\";\nlet queue: QueuedCustomEvent[] = [];\nlet started = false;\n\nconst randomId = (): string => {\n if (globalThis.crypto?.randomUUID) {\n return globalThis.crypto.randomUUID();\n }\n return `mxl_${Math.random().toString(36).slice(2)}_${Date.now()}`;\n};\n\nconst storageGet = (key: string): string | undefined => {\n try {\n const value = globalThis.sessionStorage?.getItem(key);\n return value && value.length > 0 ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\nconst storageSet = (key: string, value: string): void => {\n try {\n globalThis.sessionStorage?.setItem(key, value);\n } catch {\n // Private mode or missing storage.\n }\n};\n\nconst resolveSessionId = (): string => {\n if (sessionId.length > 0) {\n return sessionId;\n }\n sessionId = storageGet(\"mxl.session.id\") ?? randomId();\n storageSet(\"mxl.session.id\", sessionId);\n return sessionId;\n};\n\nconst ingestOrigin = (): string =>\n (config?.ingestUrl ?? DEFAULT_INGEST_URL).replace(/\\/$/, \"\");\n\nconst headers = (): Record<string, string> => {\n const next: Record<string, string> = {\n \"content-type\": \"application/json\",\n \"mxl-client-id\": config?.clientId ?? \"\",\n \"mxl-sdk-name\": SDK_NAME,\n \"mxl-sdk-version\": SDK_VERSION,\n };\n if (config?.apiKey) {\n next[\"mxl-api-key\"] = config.apiKey;\n }\n return next;\n};\n\nconst post = (\n path: string,\n body: string,\n contentType: string,\n keepalive = false\n): void => {\n if (!config?.clientId) {\n return;\n }\n try {\n void fetch(`${ingestOrigin()}${path}`, {\n body,\n headers: { ...headers(), \"content-type\": contentType },\n keepalive,\n method: \"POST\",\n });\n } catch {\n // Never throw into the host app.\n }\n};\n\nconst captureAllowed = (): boolean =>\n Boolean(config?.clientId) && consentGranted && started;\n\nexport const getCurrentIdentity = (): MapleIdentity | undefined => identity;\n\nexport const getCurrentUserId = (): string | undefined => identity?.id;\n\nexport const getCurrentSessionId = (): string => resolveSessionId();\n\nexport const configureBrowserAnalytics = (\n next: BrowserAnalyticsConfig\n): void => {\n config = next;\n consentGranted = next.privacy?.requireConsent !== true;\n started = true;\n resolveSessionId();\n drainQueue();\n};\n\nexport const resetBrowserAnalytics = (): void => {\n config = undefined;\n identity = undefined;\n consentGranted = true;\n sessionId = \"\";\n queue = [];\n started = false;\n};\n\nexport const setConsent = (granted: boolean): void => {\n consentGranted = granted;\n if (granted) {\n drainQueue();\n }\n};\n\nexport const identify = (input?: IdentifyInput): void => {\n identity = normalizeIdentity(input);\n if (!captureAllowed() || identity === undefined) {\n return;\n }\n post(\n \"/track\",\n JSON.stringify({\n payload: {\n email: identity.email,\n firstName:\n typeof identity.traits?.firstName === \"string\"\n ? identity.traits.firstName\n : undefined,\n lastName:\n typeof identity.traits?.lastName === \"string\"\n ? identity.traits.lastName\n : undefined,\n profileId: identity.id,\n properties: {\n ...(identity.traits ?? {}),\n groupId: identity.groupId,\n groupName: identity.groupName,\n },\n },\n type: \"identify\",\n }),\n \"application/json\"\n );\n};\n\nexport const clearIdentity = (): void => {\n identity = undefined;\n};\n\nconst postProductAndSession = (\n name: string,\n props?: TrackProps,\n keepalive = false\n): void => {\n const line = encodeProductEventLine({\n name,\n options: {\n attributes: props,\n groupId: identity?.groupId,\n sessionId: resolveSessionId(),\n userId: identity?.id,\n },\n serviceName: config?.serviceName ?? \"unknown\",\n source: \"browser\",\n });\n if (line === undefined) {\n return;\n }\n post(\"/events\", encodeNdjson([line]), \"application/x-ndjson\", keepalive);\n post(\n \"/sessionEvents\",\n `${JSON.stringify({\n attributes: line.attributes,\n message: line.name,\n session_id: line.session_id,\n timestamp: line.timestamp,\n type: \"custom\",\n })}\\n`,\n \"application/x-ndjson\",\n keepalive\n );\n};\n\nconst drainQueue = (keepalive = false): void => {\n if (!captureAllowed()) {\n return;\n }\n const pending = queue;\n queue = [];\n for (const event of pending) {\n postProductAndSession(event.name, event.props, keepalive);\n }\n};\n\nexport const track = (name: string, props?: TrackProps): void => {\n if (!started || !consentGranted || !config?.clientId) {\n if (queue.length >= MAX_QUEUE) {\n queue.shift();\n }\n queue.push({ name, props });\n return;\n }\n postProductAndSession(name, props);\n};\n\nconst postProfileDelta = (\n type: \"increment\" | \"decrement\",\n property: string,\n value?: number\n): void => {\n if (!captureAllowed() || identity === undefined) {\n return;\n }\n post(\n \"/track\",\n JSON.stringify({\n payload: {\n profileId: identity.id,\n property,\n value: value ?? 1,\n },\n type,\n }),\n \"application/json\"\n );\n};\n\nexport const increment = (property: string, value?: number): void => {\n postProfileDelta(\"increment\", property, value);\n};\n\nexport const decrement = (property: string, value?: number): void => {\n postProfileDelta(\"decrement\", property, value);\n};\n\nexport const flushAnalytics = (keepalive = false): void => {\n drainQueue(keepalive);\n};\n","import { getCurrentIdentity, getCurrentSessionId } from \"./analytics.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\nexport interface ClientReplayConfig {\n readonly enabled?: boolean | undefined;\n readonly maskAllInputs?: boolean | undefined;\n readonly maskAllText?: boolean | undefined;\n readonly sampleRate?: number | undefined;\n}\n\nexport interface ReplayChunkMeta {\n readonly chunkSeq: number;\n readonly durationMs: number;\n readonly eventCount: number;\n readonly isCheckpoint: boolean;\n readonly sessionId: string;\n}\n\ntype StopReplay = () => void;\n\nlet stopRecorder: StopReplay | undefined;\nlet chunkSeq = 0;\n\nconst ingestHeaders = (\n clientId: string,\n apiKey: string | undefined,\n contentType: string\n): Record<string, string> => {\n const headers: Record<string, string> = {\n \"content-type\": contentType,\n \"mxl-client-id\": clientId,\n \"mxl-sdk-name\": SDK_NAME,\n \"mxl-sdk-version\": SDK_VERSION,\n };\n if (apiKey) {\n headers[\"mxl-api-key\"] = apiKey;\n }\n return headers;\n};\n\nexport const isWebSdkPresent = (): boolean => {\n const global = globalThis as {\n MxlWebSdk?: unknown;\n __MXL_REPLAY_SESSION__?: unknown;\n __MXL_WEB_SDK__?: unknown;\n };\n return Boolean(\n global.__MXL_WEB_SDK__ || global.MxlWebSdk || global.__MXL_REPLAY_SESSION__\n );\n};\n\nexport const shouldRecordReplay = (\n replay: ClientReplayConfig | undefined\n): boolean => {\n if (isWebSdkPresent()) {\n return false;\n }\n if (replay?.enabled === false) {\n return false;\n }\n const sampleRate = replay?.sampleRate ?? 1;\n return sampleRate >= 1 || Math.random() < sampleRate;\n};\n\nexport const sessionMetaRow = (serviceName: string) => {\n const identity = getCurrentIdentity();\n const started = new Date();\n return {\n service_name: serviceName,\n session_id: getCurrentSessionId(),\n start_time: started.toISOString().replace(\"T\", \" \").replace(\"Z\", \"\"),\n status: \"active\",\n url_initial:\n typeof globalThis.location?.href === \"string\"\n ? globalThis.location.href\n : \"\",\n user_agent:\n typeof globalThis.navigator?.userAgent === \"string\"\n ? globalThis.navigator.userAgent\n : \"\",\n user_id: identity?.id ?? \"\",\n version: 1,\n };\n};\n\nexport const postSessionReplayMeta = (\n ingestUrl: string,\n clientId: string,\n serviceName: string,\n apiKey?: string\n): void => {\n try {\n void fetch(`${ingestUrl.replace(/\\/$/, \"\")}/sessionReplays/meta`, {\n body: `${JSON.stringify(sessionMetaRow(serviceName))}\\n`,\n headers: ingestHeaders(clientId, apiKey, \"application/x-ndjson\"),\n keepalive: true,\n method: \"POST\",\n });\n } catch {\n // Replay must never throw into the host app.\n }\n};\n\nexport const postSessionReplayBlob = (\n ingestUrl: string,\n clientId: string,\n meta: ReplayChunkMeta,\n body: Uint8Array,\n apiKey?: string\n): void => {\n try {\n void fetch(`${ingestUrl.replace(/\\/$/, \"\")}/sessionReplays/blob`, {\n body: body as unknown as Blob,\n headers: {\n ...ingestHeaders(clientId, apiKey, \"application/octet-stream\"),\n \"x-mxl-chunk-seq\": String(meta.chunkSeq),\n \"x-mxl-duration-ms\": String(meta.durationMs),\n \"x-mxl-event-count\": String(meta.eventCount),\n \"x-mxl-is-checkpoint\": meta.isCheckpoint ? \"1\" : \"0\",\n \"x-mxl-session-id\": meta.sessionId,\n },\n keepalive: true,\n method: \"POST\",\n });\n } catch {\n // Replay must never throw into the host app.\n }\n};\n\nconst gzipJson = async (value: unknown): Promise<Uint8Array | undefined> => {\n if (typeof CompressionStream === \"undefined\") {\n return undefined;\n }\n const encoded = new TextEncoder().encode(JSON.stringify(value));\n const stream = new Blob([encoded])\n .stream()\n .pipeThrough(new CompressionStream(\"gzip\"));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n};\n\nexport const stopReplay = (): void => {\n stopRecorder?.();\n stopRecorder = undefined;\n};\n\nexport const startReplay = async (config: {\n readonly apiKey?: string | undefined;\n readonly clientId: string;\n readonly ingestUrl: string;\n readonly replay?: ClientReplayConfig | undefined;\n readonly serviceName: string;\n}): Promise<void> => {\n if (!shouldRecordReplay(config.replay)) {\n return;\n }\n stopReplay();\n chunkSeq = 0;\n postSessionReplayMeta(\n config.ingestUrl,\n config.clientId,\n config.serviceName,\n config.apiKey\n );\n try {\n const specifier = \"rrweb\";\n const rrweb = (await import(\n /* @vite-ignore */ specifier\n )) as {\n record?: (options: {\n emit: (event: unknown) => void;\n maskAllInputs?: boolean;\n maskTextSelector?: string;\n }) => (() => void) | undefined;\n };\n if (typeof rrweb.record !== \"function\") {\n return;\n }\n const events: unknown[] = [];\n const startedAt = Date.now();\n const stop = rrweb.record({\n emit: (event) => {\n events.push(event);\n },\n maskAllInputs: config.replay?.maskAllInputs ?? true,\n maskTextSelector: config.replay?.maskAllText === true ? \"*\" : undefined,\n });\n stopRecorder = () => {\n stop?.();\n if (events.length === 0) {\n return;\n }\n void gzipJson(events).then((gzipped) => {\n if (!gzipped) {\n return;\n }\n postSessionReplayBlob(\n config.ingestUrl,\n config.clientId,\n {\n chunkSeq: chunkSeq++,\n durationMs: Date.now() - startedAt,\n eventCount: events.length,\n isCheckpoint: true,\n sessionId: getCurrentSessionId(),\n },\n gzipped,\n config.apiKey\n );\n });\n };\n } catch {\n // rrweb is optional; metadata rows still make the session visible.\n }\n};\n","import * as Otel from \"@opentelemetry/api\";\nimport { Effect, Layer } from \"effect\";\nimport * as Tracer from \"effect/Tracer\";\n\nconst OTEL_SPAN_TYPE_ID = \"~@effect/opentelemetry/OtelTracer/OtelSpan\";\nconst evaluate = \"~effect/Effect/evaluate\" as const;\n\ninterface OtelWrappedSpan {\n readonly span: Otel.Span;\n}\n\nexport function formatTraceparent(span: Tracer.AnySpan): string {\n const flags = span.sampled ? \"01\" : \"00\";\n return `00-${span.traceId}-${span.spanId}-${flags}`;\n}\n\nexport function buildTraceCarrierFromSpan(\n span: Tracer.AnySpan,\n headers?: Record<string, string>\n): Record<string, string> {\n const carrier: Record<string, string> = {\n traceparent: formatTraceparent(span),\n };\n\n if (headers?.tracestate) {\n carrier.tracestate = headers.tracestate;\n }\n if (headers?.baggage) {\n carrier.baggage = headers.baggage;\n }\n\n return carrier;\n}\n\n/** Map an Effect span to an OpenTelemetry context with the span active. */\nexport function effectSpanToOtelContext(\n activeContext: Otel.Context,\n span: Tracer.AnySpan\n): Otel.Context {\n if (\n typeof span === \"object\" &&\n span !== null &&\n OTEL_SPAN_TYPE_ID in span &&\n \"span\" in span\n ) {\n return Otel.trace.setSpan(activeContext, (span as OtelWrappedSpan).span);\n }\n\n return Otel.trace.setSpanContext(activeContext, {\n isRemote: span._tag === \"ExternalSpan\",\n spanId: span.spanId,\n traceFlags: span.sampled ? Otel.TraceFlags.SAMPLED : Otel.TraceFlags.NONE,\n traceId: span.traceId,\n });\n}\n\n/** Wrap a tracer so Effect spans activate global `@opentelemetry/api` context. */\nexport function wrapTracerWithOtelContextBridge(\n base: Tracer.Tracer\n): Tracer.Tracer {\n const existingContext = base.context;\n\n return Tracer.make({\n span(options) {\n return base.span(options);\n },\n context(primitive, fiber) {\n if (fiber.cache.span === undefined) {\n if (existingContext) {\n return existingContext(primitive, fiber);\n }\n return primitive[evaluate](fiber);\n }\n\n return Otel.context.with(\n effectSpanToOtelContext(Otel.context.active(), fiber.cache.span),\n () => {\n if (existingContext) {\n return existingContext(primitive, fiber);\n }\n return primitive[evaluate](fiber);\n }\n );\n },\n });\n}\n\n/**\n * Layer that wraps the configured Effect tracer with a global OTel context bridge.\n *\n * Must be merged after `Otlp.layerJson` so queue propagation, log correlation,\n * and outbound W3C headers see the active server span.\n */\nexport const otelGlobalContextBridgeLayer = Layer.effect(\n Tracer.Tracer,\n Effect.gen(function* () {\n const base = yield* Tracer.Tracer;\n return wrapTracerWithOtelContextBridge(base);\n })\n);\n","export function normalizeOtlpBaseUrl(url: string): string {\n const trimmed = url.replace(/\\/$/, \"\");\n if (trimmed.endsWith(\"/v1/traces\") || trimmed.endsWith(\"/v1/logs\")) {\n return trimmed.replace(/\\/v1\\/(traces|logs|metrics)$/, \"\");\n }\n return trimmed;\n}\n","import * as WebSdk from \"@effect/opentelemetry/WebSdk\";\nimport type { Context } from \"@opentelemetry/api\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n BatchSpanProcessor,\n type ReadableSpan,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { Effect, Layer } from \"effect\";\nimport * as Tracer from \"effect/Tracer\";\n\nimport {\n clearIdentity as clearIdentitySync,\n configureBrowserAnalytics,\n getCurrentUserId,\n identify as identifySync,\n type IdentifyInput,\n type PrivacyOptions,\n type TrackProps,\n track as trackSync,\n} from \"./analytics.js\";\nimport { type ClientReplayConfig, startReplay } from \"./replay.js\";\nimport { wrapTracerWithOtelContextBridge } from \"../server/otel-context-bridge.js\";\nimport { normalizeOtlpBaseUrl } from \"../server/otlp.js\";\n\nexport interface MxlBrowserConfig {\n readonly apiKey?: string | undefined;\n readonly attributes?: Record<string, string> | undefined;\n readonly clientId?: string | undefined;\n readonly disabled?: boolean | undefined;\n readonly endpoint?: string | undefined;\n readonly environment?: string | undefined;\n readonly headers?: Record<string, string> | undefined;\n readonly ingestUrl?: string | undefined;\n readonly privacy?: PrivacyOptions | undefined;\n readonly replay?: ClientReplayConfig | undefined;\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n}\n\nexport const stampUserIdOnSpan = (span: {\n setAttribute: (key: string, value: string) => unknown;\n}): void => {\n const userId = getCurrentUserId();\n if (userId) {\n span.setAttribute(\"user.id\", userId);\n }\n};\n\nclass UserIdSpanProcessor implements SpanProcessor {\n constructor(private readonly inner: SpanProcessor) {}\n\n forceFlush(): Promise<void> {\n return this.inner.forceFlush();\n }\n\n onEnd(span: ReadableSpan): void {\n this.inner.onEnd(span);\n }\n\n onStart(\n span: Parameters<SpanProcessor[\"onStart\"]>[0],\n parentContext: Context\n ): void {\n stampUserIdOnSpan(span);\n this.inner.onStart(span, parentContext);\n }\n\n shutdown(): Promise<void> {\n return this.inner.shutdown();\n }\n}\n\nfunction resolveBrowserEndpoint(config: MxlBrowserConfig): string | undefined {\n if (config.disabled) {\n return undefined;\n }\n if (config.endpoint && config.endpoint.length > 0) {\n return normalizeOtlpBaseUrl(config.endpoint);\n }\n return undefined;\n}\n\n/**\n * Browser Effect observability layer backed by OpenTelemetry WebSdk.\n *\n * Returns a no-op layer when `disabled` is true or no OTLP endpoint is configured.\n */\nexport const layer = (config: MxlBrowserConfig = {}) => {\n if (config.clientId) {\n configureBrowserAnalytics({\n apiKey: config.apiKey,\n clientId: config.clientId,\n ingestUrl: config.ingestUrl,\n privacy: config.privacy,\n serviceName: config.serviceName,\n });\n void startReplay({\n apiKey: config.apiKey,\n clientId: config.clientId,\n ingestUrl: config.ingestUrl ?? \"http://127.0.0.1:8000\",\n replay: config.replay,\n serviceName: config.serviceName ?? \"unknown\",\n });\n }\n const endpoint = resolveBrowserEndpoint(config);\n if (!endpoint) {\n return Layer.empty;\n }\n\n const attributes: Record<string, string> = {\n \"mxl.sdk.type\": \"browser\",\n ...(config.environment\n ? {\n \"deployment.environment\": config.environment,\n \"deployment.environment.name\": config.environment,\n }\n : {}),\n ...(config.attributes ?? {}),\n };\n\n return WebSdk.layer(() => ({\n resource: {\n attributes,\n serviceName: config.serviceName ?? \"unknown\",\n serviceVersion: config.serviceVersion,\n },\n spanProcessor: new UserIdSpanProcessor(\n new BatchSpanProcessor(\n new OTLPTraceExporter({\n headers: config.headers,\n url: `${endpoint}/v1/traces`,\n })\n )\n ),\n })).pipe(Layer.updateService(Tracer.Tracer, wrapTracerWithOtelContextBridge));\n};\n\nexport const identify = (input?: IdentifyInput) =>\n Effect.sync(() => identifySync(input));\n\nexport const clearIdentity = Effect.sync(() => clearIdentitySync());\n\nexport const track = (name: string, props?: TrackProps) =>\n Effect.sync(() => trackSync(name, props));\n","export interface SignalState {\n disabledUntil: number;\n}\n\nexport const COOLDOWN_MS = 60_000;\n\nexport const makeSerializedFlush = <Args extends unknown[]>(\n flush: (...args: Args) => Promise<void>\n): ((...args: Args) => Promise<void>) => {\n let pending: Promise<void> = Promise.resolve();\n return (...args: Args) => {\n pending = pending.then(\n () => flush(...args),\n () => flush(...args)\n );\n return pending;\n };\n};\n\nexport const guardFlush = <Args extends unknown[]>(\n prefix: string,\n flush: (...args: Args) => Promise<void>,\n state?: SignalState\n): ((...args: Args) => Promise<void>) => {\n return async (...args: Args) => {\n if (state && Date.now() < state.disabledUntil) {\n return;\n }\n try {\n await flush(...args);\n } catch (error) {\n if (state) {\n state.disabledUntil = Date.now() + COOLDOWN_MS;\n }\n console.error(`${prefix} flush failed; cooling down 60s`, error);\n }\n };\n};\n\nexport const fetchTransport = async (input: {\n readonly body: string | Uint8Array;\n readonly headers: Record<string, string>;\n readonly keepalive?: boolean | undefined;\n readonly url: string;\n}): Promise<void> => {\n const response = await fetch(input.url, {\n body: input.body,\n headers: input.headers,\n keepalive: input.keepalive,\n method: \"POST\",\n });\n if (!response.ok) {\n throw new Error(`POST ${input.url} → ${response.status}`);\n }\n};\n","import type { BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { Layer } from \"effect\";\n\nimport { flushAnalytics, track } from \"./analytics.js\";\nimport type { MxlBrowserConfig } from \"./layer.js\";\nimport { layer as browserLayer } from \"./layer.js\";\nimport { stopReplay } from \"./replay.js\";\nimport { guardFlush, makeSerializedFlush } from \"../shared/flush-core.js\";\n\nexport interface MxlBrowserFlushableConfig extends MxlBrowserConfig {\n readonly autoFlushInterval?: number | false | undefined;\n readonly captureGlobalErrors?: boolean | undefined;\n readonly flushOnUnload?: boolean | undefined;\n}\n\nexport interface FlushableTelemetry {\n readonly captureException: (\n error: unknown,\n options?: { readonly attributes?: Record<string, string>; readonly name?: string }\n ) => void;\n readonly dispose: () => Promise<void>;\n readonly flush: () => Promise<void>;\n readonly layer: Layer.Layer<never>;\n}\n\nconst processors = new WeakMap<FlushableTelemetry, BatchSpanProcessor>();\nconst DEFAULT_AUTO_FLUSH_MS = 5_000;\n\nexport const attachProcessor = (\n telemetry: FlushableTelemetry,\n processor: BatchSpanProcessor\n): void => {\n processors.set(telemetry, processor);\n};\n\nexport const make = (\n config: MxlBrowserFlushableConfig = {}\n): FlushableTelemetry => {\n const layer = browserLayer(config);\n const state = { disabledUntil: 0 };\n const listeners: Array<() => void> = [];\n\n const telemetry: FlushableTelemetry = {\n captureException: (error, options) => {\n track(options?.name ?? \"uncaught_error\", {\n ...options?.attributes,\n message: error instanceof Error ? error.message : String(error),\n });\n },\n dispose: async () => {\n for (const stop of listeners) {\n stop();\n }\n listeners.length = 0;\n stopReplay();\n await telemetry.flush();\n },\n flush: makeSerializedFlush(\n guardFlush(\n \"[MxlFlush]\",\n async () => {\n flushAnalytics(true);\n await processors.get(telemetry)?.forceFlush();\n },\n state\n )\n ),\n layer,\n };\n\n const intervalMs =\n config.autoFlushInterval === undefined\n ? DEFAULT_AUTO_FLUSH_MS\n : config.autoFlushInterval === false\n ? 0\n : config.autoFlushInterval;\n if (intervalMs > 0) {\n const timer = setInterval(() => {\n void telemetry.flush();\n }, intervalMs);\n timer.unref?.();\n listeners.push(() => {\n clearInterval(timer);\n });\n }\n\n const documentRef = (\n globalThis as {\n document?: {\n addEventListener: (type: string, listener: () => void) => void;\n removeEventListener: (type: string, listener: () => void) => void;\n visibilityState?: string;\n };\n }\n ).document;\n\n if (config.flushOnUnload !== false && documentRef) {\n const onHidden = () => {\n void telemetry.flush();\n };\n const onVisibilityChange = () => {\n if (documentRef.visibilityState === \"hidden\") {\n onHidden();\n }\n };\n documentRef.addEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.addEventListener?.(\"pagehide\", onHidden);\n listeners.push(() => {\n documentRef.removeEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener?.(\"pagehide\", onHidden);\n });\n }\n\n if (config.captureGlobalErrors !== false && typeof globalThis.addEventListener === \"function\") {\n const onError = (event: Event) => {\n const record = event as { error?: unknown; reason?: unknown };\n telemetry.captureException(record.error ?? record.reason ?? event);\n void telemetry.flush();\n };\n globalThis.addEventListener(\"error\", onError);\n globalThis.addEventListener(\"unhandledrejection\", onError);\n listeners.push(() => {\n globalThis.removeEventListener(\"error\", onError);\n globalThis.removeEventListener(\"unhandledrejection\", onError);\n });\n }\n\n return telemetry;\n};\n","import {\n context as otelContext,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace as traceApi,\n} from \"@opentelemetry/api\";\nimport { Effect } from \"effect\";\n\nexport type DbSystem = \"clickhouse\" | \"postgresql\" | \"redis\";\n\nexport type DependencySpanKind = \"client\" | \"producer\" | \"consumer\";\n\nconst MAX_FLUSH_SPAN_LINKS = 32;\n\nexport interface QueueTraceContextCarrier {\n readonly traceparent?: string | undefined;\n readonly tracestate?: string | undefined;\n readonly baggage?: string | undefined;\n}\n\nexport function dbClientSpanAttributes(\n dbSystem: DbSystem,\n operation?: string\n): Record<string, string> {\n return {\n \"db.system\": dbSystem,\n \"db.system.name\": dbSystem,\n \"peer.service\": dbSystem,\n ...(operation ? { \"db.operation.name\": operation } : {}),\n };\n}\n\nexport function queueProducerSpanAttributes(\n destinationName: string,\n peerService = \"mxl-worker\"\n): Record<string, string> {\n return {\n \"messaging.destination.name\": destinationName,\n \"messaging.system\": \"bullmq\",\n \"peer.service\": peerService,\n };\n}\n\nexport function queueConsumerSpanAttributes(\n destinationName: string\n): Record<string, string> {\n return {\n \"messaging.destination.name\": destinationName,\n \"messaging.system\": \"bullmq\",\n };\n}\n\nexport function kafkaProducerSpanAttributes(\n topic: string,\n peerService = \"kafka\"\n): Record<string, string> {\n return {\n \"messaging.destination.name\": topic,\n \"messaging.system\": \"kafka\",\n \"peer.service\": peerService,\n };\n}\n\nexport function bufferSpanAttributes(\n bufferName: string,\n operation: string\n): Record<string, string> {\n return {\n \"buffer.name\": bufferName,\n \"db.operation.name\": operation,\n \"db.system\": \"redis\",\n \"db.system.name\": \"redis\",\n \"peer.service\": \"redis\",\n };\n}\n\nexport function geoSpanAttributes(): Record<string, string> {\n return {\n \"db.operation.name\": \"resolve\",\n \"db.system\": \"maxmind\",\n \"db.system.name\": \"maxmind\",\n \"peer.service\": \"maxmind\",\n };\n}\n\nexport function addSpanLinksFromTraceCarriers(\n carriers: readonly QueueTraceContextCarrier[]\n): number {\n const span = traceApi.getActiveSpan();\n if (!span) {\n return 0;\n }\n\n const seen = new Set<string>();\n let linked = 0;\n\n for (const carrier of carriers) {\n if (!carrier.traceparent || seen.has(carrier.traceparent)) {\n continue;\n }\n seen.add(carrier.traceparent);\n\n const extracted = propagation.extract(otelContext.active(), carrier);\n const linkedContext = traceApi.getSpanContext(extracted);\n if (!linkedContext) {\n continue;\n }\n\n span.addLink({ context: linkedContext });\n linked += 1;\n\n if (linked >= MAX_FLUSH_SPAN_LINKS) {\n break;\n }\n }\n\n return linked;\n}\n\nexport interface WithDependencySpanOptions {\n readonly attributes?: Record<string, string> | undefined;\n readonly kind?: DependencySpanKind | undefined;\n}\n\nexport function withDependencySpan<A, E, R>(\n name: string,\n attributes: Record<string, string>,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R>;\nexport function withDependencySpan<A, E, R>(\n name: string,\n options: WithDependencySpanOptions,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R>;\nexport function withDependencySpan<A, E, R>(\n name: string,\n attributesOrOptions: Record<string, string> | WithDependencySpanOptions,\n program: Effect.Effect<A, E, R>\n): Effect.Effect<A, E, R> {\n const options = isWithDependencySpanOptions(attributesOrOptions)\n ? attributesOrOptions\n : { attributes: attributesOrOptions };\n\n const attributes = options.attributes ?? {};\n const kind: DependencySpanKind = options.kind ?? \"client\";\n\n return program.pipe(Effect.withSpan(name, { attributes, kind }));\n}\n\nfunction isWithDependencySpanOptions(\n value: Record<string, string> | WithDependencySpanOptions\n): value is WithDependencySpanOptions {\n return (\n typeof value === \"object\" &&\n (\"kind\" in value ||\n (\"attributes\" in value && typeof value.attributes === \"object\"))\n );\n}\n\n/** Promise-based dependency span for non-Effect code paths. */\nexport async function withPromiseDependencySpan<T>(\n name: string,\n attributes: Record<string, string>,\n operation: () => Promise<T>,\n kind: DependencySpanKind = \"client\"\n): Promise<T> {\n const tracer = traceApi.getTracer(\"mxl-effect-sdk\");\n const spanKind =\n kind === \"producer\"\n ? SpanKind.PRODUCER\n : kind === \"consumer\"\n ? SpanKind.CONSUMER\n : SpanKind.CLIENT;\n return tracer.startActiveSpan(\n name,\n { attributes, kind: spanKind },\n async (span) => {\n try {\n const result = await operation();\n span.end();\n return result;\n } catch (error) {\n if (error instanceof Error) {\n span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message,\n });\n } else {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: String(error),\n });\n }\n span.end();\n throw error;\n }\n }\n );\n}\n"],"mappings":"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"}