@safaricom-mxl/effect-sdk 0.0.14 → 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.
- package/LICENSE +201 -0
- package/README.md +33 -6
- package/dist/browser/index.d.ts +81 -17
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +474 -12
- package/dist/browser/index.js.map +1 -1
- package/dist/cloudflare/index.d.ts +25 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +547 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/{index-8yBtnSxJ.d.mts → index-BF4-zL-9.d.mts} +71 -6
- package/dist/index-BF4-zL-9.d.mts.map +1 -0
- package/dist/index.server.d.mts +2 -2
- package/dist/index.server.mjs +2 -2
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.mjs +2 -2
- package/dist/{server-BhmjaLrj.mjs → server-DU685moi.mjs} +201 -21
- package/dist/server-DU685moi.mjs.map +1 -0
- package/package.json +35 -22
- package/dist/index-8yBtnSxJ.d.mts.map +0 -1
- package/dist/server-BhmjaLrj.mjs.map +0 -1
package/dist/browser/index.js
CHANGED
|
@@ -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";
|
|
@@ -27,11 +346,11 @@ function wrapTracerWithOtelContextBridge(base) {
|
|
|
27
346
|
return base.span(options);
|
|
28
347
|
},
|
|
29
348
|
context(primitive, fiber) {
|
|
30
|
-
if (fiber.
|
|
349
|
+
if (fiber.cache.span === void 0) {
|
|
31
350
|
if (existingContext) return existingContext(primitive, fiber);
|
|
32
351
|
return primitive[evaluate](fiber);
|
|
33
352
|
}
|
|
34
|
-
return Otel.context.with(effectSpanToOtelContext(Otel.context.active(), fiber.
|
|
353
|
+
return Otel.context.with(effectSpanToOtelContext(Otel.context.active(), fiber.cache.span), () => {
|
|
35
354
|
if (existingContext) return existingContext(primitive, fiber);
|
|
36
355
|
return primitive[evaluate](fiber);
|
|
37
356
|
});
|
|
@@ -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({
|
|
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 = {
|
|
@@ -71,17 +435,115 @@ const layer = (config = {}) => {
|
|
|
71
435
|
} : {},
|
|
72
436
|
...config.attributes ?? {}
|
|
73
437
|
};
|
|
74
|
-
return WebSdk.layer({
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
}))
|
|
84
|
-
}).pipe(Layer.updateService(Tracer.Tracer, wrapTracerWithOtelContextBridge));
|
|
444
|
+
spanProcessor: new UserIdSpanProcessor(new BatchSpanProcessor(new OTLPTraceExporter({
|
|
445
|
+
headers: config.headers,
|
|
446
|
+
url: `${endpoint}/v1/traces`
|
|
447
|
+
})))
|
|
448
|
+
})).pipe(Layer.updateService(Tracer.Tracer, wrapTracerWithOtelContextBridge));
|
|
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;
|
|
85
547
|
};
|
|
86
548
|
//#endregion
|
|
87
549
|
//#region src/shared/dependency-spans.ts
|
|
@@ -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
|