@safaricom-mxl/effect-sdk 0.0.23 → 0.0.24

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.
@@ -2,8 +2,8 @@ import { Cause, Config, Context, Duration, Effect, Layer, ManagedRuntime, Match,
2
2
  import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http";
3
3
  import { SchemaJITCompiler } from "effect/schema";
4
4
  import { Otlp } from "effect/observability";
5
- import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
6
5
  import * as Otel from "@opentelemetry/api";
6
+ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
7
7
  import * as Tracer from "effect/Tracer";
8
8
  import { v7 } from "uuid";
9
9
  import { platform, provider, runtime } from "std-env";
@@ -39,12 +39,10 @@ const encodeProductEventLine = (input) => {
39
39
  };
40
40
  };
41
41
  const encodeNdjson = (lines) => `${lines.map((line) => JSON.stringify(line)).join("\n")}\n`;
42
- //#endregion
43
- //#region src/version.ts
42
+ // src/version.ts
44
43
  const SDK_NAME = "mxl-effect-sdk";
45
- const SDK_VERSION = "0.0.15";
46
- //#endregion
47
- //#region src/server/events.ts
44
+ const SDK_VERSION = "0.0.24";
45
+ // src/server/events.ts
48
46
  const DEFAULT_FLUSH_INTERVAL = Duration.seconds(5);
49
47
  const DEFAULT_MAX_BATCH_SIZE = 100;
50
48
  const MAX_BUFFERED_EVENTS = 1e3;
@@ -162,8 +160,7 @@ const makeHandle = (config = {}) => {
162
160
  }
163
161
  };
164
162
  };
165
- //#endregion
166
- //#region src/server/otel-context-bridge.ts
163
+ // src/server/otel-context-bridge.ts
167
164
  const evaluate = "~effect/Effect/evaluate";
168
165
  const contextManagerProbe = Otel.createContextKey("mxl.effect-sdk.context-manager-probe");
169
166
  let contextManagerInstalled = false;
@@ -207,7 +204,7 @@ function effectSpanToOtelContext(activeContext, span) {
207
204
  * Wrap a tracer so Effect spans activate global `@opentelemetry/api` context.
208
205
  *
209
206
  * Prefer this over `Otlp.layerJson({ tracerContext })` until Effect changes
210
- * that hook. On effect@4.0.2 the callback is still `(primitive, span)`
207
+ * that hook. On effect@4.0.6 the callback is still `(primitive, span)`
211
208
  * and must return the evaluation, but `EffectPrimitive` only evaluates with a
212
209
  * `fiber`. Keep this wrap until Effect passes fiber or evaluates after the hook.
213
210
  */
@@ -239,8 +236,7 @@ function wrapTracerWithOtelContextBridge(base) {
239
236
  const otelGlobalContextBridgeLayer = Layer.effect(Tracer.Tracer, Effect.gen(function* () {
240
237
  return wrapTracerWithOtelContextBridge(yield* Tracer.Tracer);
241
238
  }));
242
- //#endregion
243
- //#region src/server/otlp.ts
239
+ // src/server/otlp.ts
244
240
  function normalizeOtlpBaseUrl(url) {
245
241
  const trimmed = url.replace(/\/$/, "");
246
242
  if (trimmed.endsWith("/v1/traces") || trimmed.endsWith("/v1/logs")) return trimmed.replace(/\/v1\/(traces|logs|metrics)$/, "");
@@ -325,8 +321,58 @@ const otlpHeaders = Effect.gen(function* () {
325
321
  onSome: (value) => parseOtlpHeaders(value)
326
322
  });
327
323
  });
328
- //#endregion
329
- //#region src/server/platform.ts
324
+ // src/shared/try-sync.ts
325
+ /** Run a throwing synchronous call, `None` if it threw. */
326
+ const trySync = (thunk) => Effect.runSync(Effect.option(Effect.try(thunk)));
327
+ /** Run a throwing synchronous call, `undefined` if it threw. */
328
+ const trySyncOrUndefined = (thunk) => Option.getOrUndefined(trySync(thunk));
329
+ // src/server/container.ts
330
+ const CONTAINER_ID_RE = /\/docker\/containers\/([0-9a-f]{64})\//;
331
+ const CGROUP_ID_RE = /([0-9a-f]{64})/;
332
+ const SHORT_ID_HOSTNAME_RE = /^[0-9a-f]{12}$/;
333
+ /**
334
+ * Pure derivation from probed inputs — the testable core. Every probe call is
335
+ * guarded by the caller (`getContainerAttributes` wraps the whole thing).
336
+ */
337
+ const deriveContainerAttributes = (probe) => {
338
+ const inDocker = trySyncOrUndefined(() => probe.exists("/.dockerenv")) ?? false;
339
+ const attrs = {};
340
+ if (inDocker) attrs["container.runtime.name"] = "docker";
341
+ const containerId = trySyncOrUndefined(() => probe.readFile("/proc/self/mountinfo").match(CONTAINER_ID_RE)?.[1]) ?? trySyncOrUndefined(() => probe.readFile("/proc/self/cgroup").match(CGROUP_ID_RE)?.[1]) ?? (inDocker ? trySyncOrUndefined(() => {
342
+ const name = probe.hostname();
343
+ return SHORT_ID_HOSTNAME_RE.test(name) ? name : void 0;
344
+ }) : void 0);
345
+ if (containerId) attrs["container.id"] = containerId;
346
+ return attrs;
347
+ };
348
+ let cached;
349
+ /**
350
+ * Read live container identity from the filesystem, memoized for the process
351
+ * lifetime (container identity cannot change under a running process). Returns
352
+ * `{}` anywhere the probes aren't available — non-Linux, workerd, browsers.
353
+ */
354
+ const getContainerAttributes = () => {
355
+ if (cached) return cached;
356
+ const proc = globalThis.process;
357
+ const loadBuiltin = proc?.getBuiltinModule;
358
+ if (proc?.platform !== "linux" || typeof loadBuiltin !== "function") {
359
+ cached = {};
360
+ return cached;
361
+ }
362
+ const fs = trySyncOrUndefined(() => loadBuiltin("node:fs"));
363
+ const os = trySyncOrUndefined(() => loadBuiltin("node:os"));
364
+ if (!fs || !os) {
365
+ cached = {};
366
+ return cached;
367
+ }
368
+ cached = deriveContainerAttributes({
369
+ exists: (path) => fs.existsSync(path),
370
+ readFile: (path) => fs.readFileSync(path, "utf8"),
371
+ hostname: () => os.hostname()
372
+ });
373
+ return cached;
374
+ };
375
+ // src/server/platform.ts
330
376
  const empty = {};
331
377
  const runtimeAttrs = (rt) => Match.value(rt).pipe(Match.when("node", () => ({ "process.runtime.name": "nodejs" })), Match.whenOr("bun", "deno", "workerd", "edge-light", "fastly", (name) => ({ "process.runtime.name": name })), Match.orElse(() => empty));
332
378
  const osAttrs = (plat) => Match.value(plat).pipe(Match.when("win32", () => ({ "os.type": "windows" })), Match.whenOr("darwin", "linux", "freebsd", "openbsd", "netbsd", (p) => ({ "os.type": p })), Match.orElse(() => empty));
@@ -438,8 +484,7 @@ const getAutoPlatformAttributes = () => {
438
484
  env
439
485
  });
440
486
  };
441
- //#endregion
442
- //#region src/server/resource.ts
487
+ // src/server/resource.ts
443
488
  const stringOrUndefined = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
444
489
  const DEFAULT_VCS_REPOSITORY_URL = "https://gitlab.safaricom.co.ke/mxl/apm/mxl-effect-sdk";
445
490
  function resolveCommitRevision() {
@@ -447,6 +492,12 @@ function resolveCommitRevision() {
447
492
  }
448
493
  let serviceInstanceId;
449
494
  const getServiceInstanceId = () => serviceInstanceId ??= v7();
495
+ /**
496
+ * Best-effort guard for `vcs.ref.head.revision`: only stamp values that look
497
+ * like a git commit SHA — `serviceVersion` may legitimately be a semver
498
+ * release string, which belongs in `service.version` but not in `vcs.*`.
499
+ */
500
+ const isCommitSha = (value) => value !== void 0 && /^[0-9a-f]{7,40}$/i.test(value);
450
501
  const resolveResource = (config) => Effect.gen(function* () {
451
502
  if (yield* sdkDisabled) return {
452
503
  endpoint: void 0,
@@ -471,6 +522,7 @@ const resolveResource = (config) => Effect.gen(function* () {
471
522
  const envResourceAttributes = yield* otelResourceAttributes;
472
523
  const attributes = {};
473
524
  Object.assign(attributes, getAutoPlatformAttributes());
525
+ Object.assign(attributes, getContainerAttributes());
474
526
  attributes["mxl.sdk.type"] = config.sdkType ?? "server";
475
527
  attributes["service.instance.id"] = getServiceInstanceId();
476
528
  if (environment$1) {
@@ -479,11 +531,11 @@ const resolveResource = (config) => Effect.gen(function* () {
479
531
  }
480
532
  if (serviceVersion$1) attributes["service.version"] = serviceVersion$1;
481
533
  const commitRevision = resolveCommitRevision();
482
- if (commitRevision) {
483
- attributes["deployment.commit_sha"] = commitRevision;
484
- attributes["vcs.ref.head.revision"] = commitRevision;
485
- }
534
+ if (commitRevision) attributes["deployment.commit_sha"] = commitRevision;
535
+ const headRevision = commitRevision ?? (isCommitSha(serviceVersion$1) ? serviceVersion$1 : void 0);
536
+ if (headRevision) attributes["vcs.ref.head.revision"] = headRevision;
486
537
  attributes["vcs.repository.url.full"] = stringOrUndefined(process.env["MXL_VCS_REPOSITORY_URL"]) ?? DEFAULT_VCS_REPOSITORY_URL;
538
+ if (config.serviceNamespace) attributes["service.namespace"] = config.serviceNamespace;
487
539
  Object.assign(attributes, envResourceAttributes);
488
540
  if (config.attributes) Object.assign(attributes, config.attributes);
489
541
  return {
@@ -497,8 +549,7 @@ const resolveResource = (config) => Effect.gen(function* () {
497
549
  }
498
550
  };
499
551
  }).pipe(Effect.orDie);
500
- //#endregion
501
- //#region src/server/layer.ts
552
+ // src/server/layer.ts
502
553
  /**
503
554
  * Create an Effect Layer that provides OpenTelemetry traces, logs, and metrics
504
555
  * configured for MXL's OTLP collector.
@@ -506,7 +557,7 @@ const resolveResource = (config) => Effect.gen(function* () {
506
557
  * Returns a no-op layer when `OTEL_SDK_DISABLED=true`.
507
558
  *
508
559
  * Wraps the OTLP tracer with `otelGlobalContextBridgeLayer` via `provideMerge`.
509
- * Measured on effect@4.0.2: `Otlp.layerJson({ tracerContext })` still
560
+ * Measured on effect@4.0.6: `Otlp.layerJson({ tracerContext })` still
510
561
  * types the hook as `(primitive, span) => X` and Effect returns that result as
511
562
  * the evaluation, so the callback cannot call `~effect/Effect/evaluate` without
512
563
  * a `fiber`. `Layer.updateService` also cannot wrap a Tracer the OTLP layer
@@ -531,8 +582,7 @@ const layer = (config = {}) => Layer.unwrap(Effect.gen(function* () {
531
582
  }).pipe(Layer.provide(FetchHttpClient.layer));
532
583
  return otelGlobalContextBridgeLayer.pipe(Layer.provideMerge(otlp));
533
584
  }));
534
- //#endregion
535
- //#region src/shared/flush-core.ts
585
+ // src/shared/flush-core.ts
536
586
  const COOLDOWN_MS = 6e4;
537
587
  const makeSerializedFlush = (flush) => {
538
588
  let pending = Promise.resolve();
@@ -552,8 +602,7 @@ const guardFlush = (prefix, flush, state) => {
552
602
  }
553
603
  };
554
604
  };
555
- //#endregion
556
- //#region src/cloudflare/index.ts
605
+ // src/cloudflare/index.ts
557
606
  var WorkerEnvironment = class extends Context.Service()("Cloudflare.Workers.WorkerEnvironment") {};
558
607
  const stringFromEnv = (env, key) => {
559
608
  const value = env[key];
@@ -597,7 +646,6 @@ const make = (config = {}) => {
597
646
  };
598
647
  };
599
648
  const MxlCloudflareSDK = { make };
600
- //#endregion
601
649
  export { MxlCloudflareSDK, WorkerEnvironment, make };
602
650
 
603
651
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["stringOrUndefined","resolveEventsConfig","make","layer","uuidv7","EnvConfig.sdkDisabled","EnvConfig.endpoint","endpoint","EnvConfig.otlpHeaders","EnvConfig.serviceVersion","serviceVersion","EnvConfig.environment","environment","EnvConfig.otelServiceName","EnvConfig.otelResourceAttributes","layer","otelLayer","eventsLayer"],"sources":["../../src/shared/product-event.ts","../../src/version.ts","../../src/server/events.ts","../../src/server/otel-context-bridge.ts","../../src/server/otlp.ts","../../src/server/config.ts","../../src/server/platform.ts","../../src/server/resource.ts","../../src/server/layer.ts","../../src/shared/flush-core.ts","../../src/cloudflare/index.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 const SDK_NAME = \"mxl-effect-sdk\";\nexport const SDK_VERSION = \"0.0.15\";\n","import {\n Cause,\n Context,\n Duration,\n Effect,\n Layer,\n ManagedRuntime,\n Option,\n Schedule,\n Schema,\n Semaphore,\n} from \"effect\";\nimport { FetchHttpClient, HttpClient, HttpClientRequest } from \"effect/http\";\nimport { SchemaJITCompiler } from \"effect/schema\";\n\nimport {\n encodeNdjson,\n encodeProductEventLine,\n type ProductEventLine,\n type TrackOptions,\n} from \"../shared/product-event.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\nexport type { TrackOptions } from \"../shared/product-event.js\";\n\nconst DEFAULT_FLUSH_INTERVAL = Duration.seconds(5);\nconst DEFAULT_MAX_BATCH_SIZE = 100;\nconst MAX_BUFFERED_EVENTS = 1_000;\nconst WARN_INTERVAL_MS = 30_000;\nconst DEFAULT_BASKET_URL = \"http://127.0.0.1:8000\";\n\nexport interface MxlEventsConfig {\n readonly apiKey?: string | undefined;\n readonly clientId?: string | undefined;\n readonly endpoint?: string | undefined;\n readonly eventsPath?: string | undefined;\n readonly flushInterval?: Duration.Input | undefined;\n readonly maxBatchSize?: number | undefined;\n readonly serviceName?: string | undefined;\n}\n\nexport interface MxlEventsApi {\n readonly flush: Effect.Effect<void>;\n readonly track: (name: string, options?: TrackOptions) => Effect.Effect<void>;\n}\n\nexport class MxlEvents extends Context.Service<MxlEvents, MxlEventsApi>()(\n \"@safaricom-mxl/effect-sdk/MxlEvents\"\n) {}\n\nexport class ProductEventsPostError extends Schema.TaggedError<ProductEventsPostError>()(\n \"@safaricom-mxl/effect-sdk/ProductEventsPostError\",\n {\n cause: Schema.optionalKey(Schema.Defect()),\n message: Schema.String,\n status: Schema.optionalKey(Schema.Number),\n }\n) {}\n\nSchemaJITCompiler.enable(ProductEventsPostError.ast);\nconst stringOrUndefined = (value: string | undefined): string | undefined =>\n value && value.length > 0 ? value : undefined;\n\nconst resolveEventsConfig = (config: MxlEventsConfig) => {\n const endpoint = (\n config.endpoint ??\n process.env[\"MXL_BASKET_URL\"] ??\n DEFAULT_BASKET_URL\n ).replace(/\\/$/, \"\");\n return {\n apiKey: stringOrUndefined(config.apiKey ?? process.env[\"MXL_API_KEY\"]),\n clientId: stringOrUndefined(config.clientId ?? process.env[\"MXL_CLIENT_ID\"]),\n serviceName:\n config.serviceName ?? process.env[\"OTEL_SERVICE_NAME\"] ?? \"unknown\",\n url: `${endpoint}${config.eventsPath ?? \"/events\"}`,\n };\n};\n\nexport const make = Effect.fn(\"MxlEvents.make\")(function* (\n config: MxlEventsConfig = {}\n) {\n const resolved = resolveEventsConfig(config);\n const maxBatchSize = Math.max(1, config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE);\n const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);\n const flushing = yield* Semaphore.make(1);\n\n let buffer: Array<ProductEventLine> = [];\n let warnedAboutName = false;\n let warnedAboutClient = false;\n let lastPostWarnAt = 0;\n\n const warnPost = (error: ProductEventsPostError): void => {\n const now = Date.now();\n if (now - lastPostWarnAt < WARN_INTERVAL_MS) return;\n lastPostWarnAt = now;\n console.warn(`[MxlEvents] ${error.message} (dropping batch)`, error.cause ?? \"\");\n };\n const warnMissingClient = (): void => {\n if (warnedAboutClient) return;\n warnedAboutClient = true;\n console.warn(\n \"[MxlEvents] no client id — set MXL_CLIENT_ID or pass `clientId`; dropping events\"\n );\n };\n const warnEmptyName = (): void => {\n if (warnedAboutName) return;\n warnedAboutName = true;\n console.warn(\"[MxlEvents] track() needs a non-empty event name; the call was ignored.\");\n };\n\n const post = (lines: ReadonlyArray<ProductEventLine>) => {\n const headers: Record<string, string> = {\n \"mxl-client-id\": resolved.clientId ?? \"\",\n \"mxl-sdk-name\": SDK_NAME,\n \"mxl-sdk-version\": SDK_VERSION,\n \"user-agent\": `${SDK_NAME}-server/${SDK_VERSION}`,\n };\n if (resolved.apiKey) {\n headers[\"mxl-api-key\"] = resolved.apiKey;\n }\n return HttpClientRequest.post(resolved.url).pipe(\n HttpClientRequest.setHeaders(headers),\n HttpClientRequest.bodyText(encodeNdjson(lines), \"application/x-ndjson\"),\n client.execute,\n Effect.asVoid,\n Effect.mapError((error) =>\n error.response !== undefined\n ? new ProductEventsPostError({\n message: `POST ${resolved.url} → ${error.response.status}`,\n status: error.response.status,\n })\n : new ProductEventsPostError({\n cause: error,\n message: `POST ${resolved.url} failed: ${error.message}`,\n })\n )\n );\n };\n\n const flush: Effect.Effect<void> = flushing.withPermits(1)(\n Effect.suspend(() => {\n if (buffer.length === 0) return Effect.void;\n const batch = buffer;\n buffer = [];\n if (resolved.clientId === undefined) {\n warnMissingClient();\n return Effect.void;\n }\n return post(batch).pipe(\n Effect.catchCause((cause) =>\n Effect.sync(() => {\n warnPost(\n Option.getOrElse(Cause.findErrorOption(cause), () =>\n new ProductEventsPostError({\n cause: Cause.squash(cause),\n message: `POST ${resolved.url} failed`,\n })\n )\n );\n })\n )\n );\n })\n );\n\n const track = (name: string, options: TrackOptions = {}): Effect.Effect<void> =>\n Effect.sync(() => {\n const line = encodeProductEventLine({\n name,\n options,\n serviceName: resolved.serviceName,\n source: \"server\",\n });\n if (line === undefined) {\n warnEmptyName();\n return false;\n }\n buffer.push(line);\n if (buffer.length > MAX_BUFFERED_EVENTS) {\n buffer.splice(0, buffer.length - MAX_BUFFERED_EVENTS);\n }\n return buffer.length >= maxBatchSize;\n }).pipe(\n Effect.flatMap((full) => (full ? Effect.forkDetach(flush) : Effect.void)),\n Effect.asVoid\n );\n\n const interval = config.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n if (Duration.toMillis(interval) > 0) {\n yield* Effect.forkScoped(flush.pipe(Effect.schedule(Schedule.spaced(interval))));\n }\n yield* Effect.addFinalizer(() => flush);\n\n return { flush, track } satisfies MxlEventsApi;\n});\n\nexport const layer = (config: MxlEventsConfig = {}): Layer.Layer<MxlEvents> =>\n Layer.effect(MxlEvents, make(config)).pipe(Layer.provide(FetchHttpClient.layer));\n\nexport interface MxlEventsHandle {\n readonly dispose: () => Promise<void>;\n readonly flush: () => Promise<void>;\n readonly track: (name: string, options?: TrackOptions) => void;\n}\n\nexport const makeHandle = (config: MxlEventsConfig = {}): MxlEventsHandle => {\n const runtime = ManagedRuntime.make(layer(config));\n const swallow = (promise: Promise<unknown>): Promise<void> =>\n promise.then(\n () => undefined,\n () => undefined\n );\n return {\n dispose: () => swallow(runtime.dispose()),\n flush: () =>\n swallow(\n runtime.runPromise(\n Effect.gen(function* () {\n const events = yield* MxlEvents;\n yield* events.flush;\n })\n )\n ),\n track: (name, options) => {\n void swallow(\n runtime.runPromise(\n Effect.gen(function* () {\n const events = yield* MxlEvents;\n yield* events.track(name, options);\n })\n )\n );\n },\n };\n};\n","import { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport * as Otel from \"@opentelemetry/api\";\nimport { Effect, Layer } from \"effect\";\nimport * as Tracer from \"effect/Tracer\";\n\nconst evaluate = \"~effect/Effect/evaluate\" as const;\nconst contextManagerProbe = Otel.createContextKey(\n \"mxl.effect-sdk.context-manager-probe\"\n);\n\nlet contextManagerInstalled = false;\n\nfunction hasWorkingContextManager(): boolean {\n return Otel.context.with(\n Otel.ROOT_CONTEXT.setValue(contextManagerProbe, true),\n () => Otel.context.active().getValue(contextManagerProbe) === true\n );\n}\n\n/**\n * Install AsyncLocalStorage so `Otel.context.with` actually propagates.\n * Does not replace a context manager the host app already registered.\n */\nexport function ensureOtelContextManager(): void {\n if (contextManagerInstalled) return;\n if (hasWorkingContextManager()) {\n contextManagerInstalled = true;\n return;\n }\n const manager = new AsyncLocalStorageContextManager();\n manager.enable();\n if (!Otel.context.setGlobalContextManager(manager)) {\n manager.disable();\n contextManagerInstalled = true;\n return;\n }\n contextManagerInstalled = true;\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/**\n * Put Effect span ids on the global OpenTelemetry context.\n *\n * Id-only `setSpanContext`. Live `Otel.Span` objects come from\n * `OtelTracer.currentOtelSpan` in Effect space, not from a private TypeId.\n */\nexport function effectSpanToOtelContext(\n activeContext: Otel.Context,\n span: Tracer.AnySpan\n): Otel.Context {\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/**\n * Wrap a tracer so Effect spans activate global `@opentelemetry/api` context.\n *\n * Prefer this over `Otlp.layerJson({ tracerContext })` until Effect changes\n * that hook. On effect@4.0.2 the callback is still `(primitive, span)`\n * and must return the evaluation, but `EffectPrimitive` only evaluates with a\n * `fiber`. Keep this wrap until Effect passes fiber or evaluates after the hook.\n */\nexport function wrapTracerWithOtelContextBridge(\n base: Tracer.Tracer\n): Tracer.Tracer {\n ensureOtelContextManager();\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 provided over `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 { Config, Effect, Option } from \"effect\";\n\nconst DEFAULT_OTLP_ENDPOINT = \"http://127.0.0.1:4318\";\n\n/** Resolve the OTLP exporter endpoint from standard env vars. */\nexport const endpoint = Effect.gen(function* () {\n const otlp = yield* Config.option(\n Config.String(\"OTEL_EXPORTER_OTLP_ENDPOINT\")\n );\n if (Option.isSome(otlp)) return otlp;\n return yield* Config.option(Config.String(\"OTLP_ENDPOINT\"));\n});\n\nexport const defaultOtlpEndpoint = DEFAULT_OTLP_ENDPOINT;\n\nexport const sdkDisabled = Config.Boolean(\"OTEL_SDK_DISABLED\").pipe(\n Config.withDefault(false)\n);\n\n/**\n * Resolve service version / commit SHA from platform-specific env vars.\n *\n * Priority: npm_package_version > COMMIT_SHA > RAILWAY_GIT_COMMIT_SHA >\n * VERCEL_GIT_COMMIT_SHA > CF_PAGES_COMMIT_SHA > RENDER_GIT_COMMIT\n */\nexport const serviceVersion = Config.option(\n Config.String(\"npm_package_version\").pipe(\n Config.orElse(() => Config.String(\"COMMIT_SHA\")),\n Config.orElse(() => Config.String(\"RAILWAY_GIT_COMMIT_SHA\")),\n Config.orElse(() => Config.String(\"VERCEL_GIT_COMMIT_SHA\")),\n Config.orElse(() => Config.String(\"CF_PAGES_COMMIT_SHA\")),\n Config.orElse(() => Config.String(\"RENDER_GIT_COMMIT\"))\n )\n);\n\n/**\n * Resolve deployment environment from env vars.\n *\n * Priority: NODE_ENV > DEPLOYMENT_ENV > \"development\"\n */\nexport const environment = Config.String(\"NODE_ENV\").pipe(\n Config.orElse(() => Config.String(\"DEPLOYMENT_ENV\")),\n Config.withDefault(\"development\")\n);\n\n/** OTel-standard service name override. */\nexport const otelServiceName = Config.option(\n Config.String(\"OTEL_SERVICE_NAME\")\n);\n\n/**\n * Parse the OTel-standard `OTEL_RESOURCE_ATTRIBUTES` env var.\n *\n * Format per the OTel spec: comma-separated `key=value` pairs, values may be\n * URL-encoded.\n */\nexport const parseOtelResourceAttributes = (\n input: string\n): Record<string, string> => {\n const result: Record<string, string> = {};\n for (const pair of input.split(\",\")) {\n const eq = pair.indexOf(\"=\");\n if (eq === -1) continue;\n const key = pair.slice(0, eq).trim();\n if (!key) continue;\n const raw = pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // Fall back to raw string when decoding fails.\n }\n result[key] = value;\n }\n return result;\n};\n\n/** Resolve and parse `OTEL_RESOURCE_ATTRIBUTES`. */\nexport const otelResourceAttributes = Effect.gen(function* () {\n const raw = yield* Config.option(Config.String(\"OTEL_RESOURCE_ATTRIBUTES\"));\n return Option.match(raw, {\n onNone: () => ({}) as Record<string, string>,\n onSome: (value) => parseOtelResourceAttributes(value),\n });\n});\n\n/**\n * Parse `OTEL_EXPORTER_OTLP_HEADERS` (`key=value,key2=value2`) into a header map.\n */\nexport const parseOtlpHeaders = (input: string): Record<string, string> => {\n const result: Record<string, string> = {};\n for (const pair of input.split(\",\")) {\n const eq = pair.indexOf(\"=\");\n if (eq === -1) continue;\n const key = pair.slice(0, eq).trim();\n if (!key) continue;\n const raw = pair.slice(eq + 1).trim();\n try {\n result[key] = decodeURIComponent(raw);\n } catch {\n result[key] = raw;\n }\n }\n return result;\n};\n\n/** Resolve OTLP export headers from env. */\nexport const otlpHeaders = Effect.gen(function* () {\n const raw = yield* Config.option(Config.String(\"OTEL_EXPORTER_OTLP_HEADERS\"));\n return Option.match(raw, {\n onNone: () => ({}) as Record<string, string>,\n onSome: (value) => parseOtlpHeaders(value),\n });\n});\n","import { Match } from \"effect\";\nimport { platform, provider, runtime } from \"std-env\";\n\nexport interface PlatformEnv {\n readonly AWS_LAMBDA_FUNCTION_NAME?: string | undefined;\n readonly AWS_LAMBDA_FUNCTION_VERSION?: string | undefined;\n readonly AWS_LAMBDA_LOG_STREAM_NAME?: string | undefined;\n readonly AWS_REGION?: string | undefined;\n readonly AWS_DEFAULT_REGION?: string | undefined;\n readonly VERCEL?: string | undefined;\n readonly VERCEL_REGION?: string | undefined;\n readonly VERCEL_DEPLOYMENT_ID?: string | undefined;\n readonly K_SERVICE?: string | undefined;\n readonly K_REVISION?: string | undefined;\n readonly CLOUD_RUN_REGION?: string | undefined;\n readonly DENO_REGION?: string | undefined;\n readonly DENO_DEPLOYMENT_ID?: string | undefined;\n readonly RENDER_INSTANCE_ID?: string | undefined;\n readonly RAILWAY_REPLICA_ID?: string | undefined;\n}\n\nexport interface PlatformInputs {\n readonly runtime: string;\n readonly provider: string;\n readonly platform: string;\n readonly arch: string;\n readonly env: PlatformEnv;\n}\n\ntype Attrs = Record<string, string>;\nconst empty: Attrs = {};\n\nconst runtimeAttrs = (rt: string): Attrs =>\n Match.value(rt).pipe(\n Match.when(\"node\", () => ({ \"process.runtime.name\": \"nodejs\" })),\n Match.whenOr(\"bun\", \"deno\", \"workerd\", \"edge-light\", \"fastly\", (name) => ({\n \"process.runtime.name\": name,\n })),\n Match.orElse((): Attrs => empty)\n );\n\nconst osAttrs = (plat: string): Attrs =>\n Match.value(plat).pipe(\n Match.when(\"win32\", () => ({ \"os.type\": \"windows\" })),\n Match.whenOr(\"darwin\", \"linux\", \"freebsd\", \"openbsd\", \"netbsd\", (p) => ({\n \"os.type\": p,\n })),\n Match.orElse((): Attrs => empty)\n );\n\nconst archAttrs = (a: string): Attrs =>\n a === \"\"\n ? empty\n : {\n \"host.arch\": Match.value(a).pipe(\n Match.when(\"x64\", () => \"amd64\"),\n Match.when(\"ia32\", () => \"x86\"),\n Match.orElse((other) => other)\n ),\n };\n\nconst lambdaAttrs = (env: PlatformEnv): Attrs => ({\n \"cloud.provider\": \"aws\",\n \"cloud.platform\": \"aws_lambda\",\n \"faas.name\": env.AWS_LAMBDA_FUNCTION_NAME!,\n ...(env.AWS_LAMBDA_FUNCTION_VERSION && {\n \"faas.version\": env.AWS_LAMBDA_FUNCTION_VERSION,\n }),\n ...(env.AWS_LAMBDA_LOG_STREAM_NAME && {\n \"faas.instance\": env.AWS_LAMBDA_LOG_STREAM_NAME,\n }),\n ...(env.AWS_REGION\n ? { \"cloud.region\": env.AWS_REGION }\n : env.AWS_DEFAULT_REGION\n ? { \"cloud.region\": env.AWS_DEFAULT_REGION }\n : {}),\n});\n\nconst providerAttrs = (prov: string, env: PlatformEnv): Attrs =>\n Match.value(prov).pipe(\n Match.when(\"cloudflare_workers\", (): Attrs => ({\n \"cloud.provider\": \"cloudflare\",\n \"cloud.platform\": \"cloudflare.workers\",\n })),\n Match.when(\"cloudflare_pages\", (): Attrs => ({\n \"cloud.provider\": \"cloudflare\",\n \"cloud.platform\": \"cloudflare.pages\",\n })),\n Match.when(\"vercel\", (): Attrs => ({\n \"cloud.provider\": \"vercel\",\n \"cloud.platform\": \"vercel\",\n ...(env.VERCEL_REGION && { \"cloud.region\": env.VERCEL_REGION }),\n ...(env.VERCEL_DEPLOYMENT_ID && {\n \"faas.instance\": env.VERCEL_DEPLOYMENT_ID,\n }),\n })),\n Match.when(\"netlify\", (): Attrs => ({\n \"cloud.provider\": \"netlify\",\n \"cloud.platform\": \"netlify\",\n })),\n Match.whenOr(\"google_cloudrun\", \"google_cloudrun_job\", (): Attrs => ({\n \"cloud.provider\": \"gcp\",\n \"cloud.platform\": \"gcp_cloud_run\",\n ...(env.K_SERVICE && { \"faas.name\": env.K_SERVICE }),\n ...(env.K_REVISION && { \"faas.version\": env.K_REVISION }),\n ...(env.CLOUD_RUN_REGION && { \"cloud.region\": env.CLOUD_RUN_REGION }),\n })),\n Match.when(\"firebase_app_hosting\", (): Attrs => ({\n \"cloud.provider\": \"gcp\",\n \"cloud.platform\": \"gcp_firebase_app_hosting\",\n })),\n Match.when(\"aws_amplify\", (): Attrs => ({\n \"cloud.provider\": \"aws\",\n \"cloud.platform\": \"aws_amplify\",\n })),\n Match.when(\"deno-deploy\", (): Attrs => ({\n \"cloud.provider\": \"deno\",\n \"cloud.platform\": \"deno_deploy\",\n ...(env.DENO_REGION && { \"cloud.region\": env.DENO_REGION }),\n ...(env.DENO_DEPLOYMENT_ID && {\n \"faas.instance\": env.DENO_DEPLOYMENT_ID,\n }),\n })),\n Match.when(\"render\", (): Attrs => ({\n \"cloud.provider\": \"render\",\n \"cloud.platform\": \"render\",\n ...(env.RENDER_INSTANCE_ID && {\n \"faas.instance\": env.RENDER_INSTANCE_ID,\n }),\n })),\n Match.when(\"railway\", (): Attrs => ({\n \"cloud.provider\": \"railway\",\n \"cloud.platform\": \"railway\",\n ...(env.RAILWAY_REPLICA_ID && {\n \"faas.instance\": env.RAILWAY_REPLICA_ID,\n }),\n })),\n Match.when(\"edgeone_pages\", (): Attrs => ({\n \"cloud.provider\": \"tencent_cloud\",\n \"cloud.platform\": \"tencent_edgeone_pages\",\n })),\n Match.orElse((): Attrs => empty)\n );\n\nconst workerdFallback = (rt: string, alreadyResolved: boolean): Attrs =>\n rt === \"workerd\" && !alreadyResolved\n ? {\n \"cloud.provider\": \"cloudflare\",\n \"cloud.platform\": \"cloudflare.workers\",\n }\n : empty;\n\nconst vercelEdgeFallback = (\n rt: string,\n alreadyResolved: boolean,\n env: PlatformEnv\n): Attrs =>\n rt === \"edge-light\" && !alreadyResolved && env.VERCEL\n ? { \"cloud.provider\": \"vercel\", \"cloud.platform\": \"vercel\" }\n : empty;\n\nexport const derivePlatformAttributes = (\n inputs: PlatformInputs\n): PlatformAttributes => {\n const { runtime: rt, provider: prov, platform: plat, arch, env } = inputs;\n\n const cloudAttrs = env.AWS_LAMBDA_FUNCTION_NAME\n ? lambdaAttrs(env)\n : providerAttrs(prov, env);\n const cloudResolved = \"cloud.provider\" in cloudAttrs;\n\n return {\n ...(rt && { \"mxl.runtime\": rt }),\n ...(prov && { \"mxl.provider\": prov }),\n ...runtimeAttrs(rt),\n ...osAttrs(plat),\n ...archAttrs(arch),\n ...cloudAttrs,\n ...workerdFallback(rt, cloudResolved),\n ...vercelEdgeFallback(rt, cloudResolved, env),\n };\n};\n\nexport type PlatformAttributes = Attrs;\n\nconst PLATFORM_ENV_KEYS = [\n \"AWS_LAMBDA_FUNCTION_NAME\",\n \"AWS_LAMBDA_FUNCTION_VERSION\",\n \"AWS_LAMBDA_LOG_STREAM_NAME\",\n \"AWS_REGION\",\n \"AWS_DEFAULT_REGION\",\n \"VERCEL\",\n \"VERCEL_REGION\",\n \"VERCEL_DEPLOYMENT_ID\",\n \"K_SERVICE\",\n \"K_REVISION\",\n \"CLOUD_RUN_REGION\",\n \"DENO_REGION\",\n \"DENO_DEPLOYMENT_ID\",\n \"RENDER_INSTANCE_ID\",\n \"RAILWAY_REPLICA_ID\",\n] as const;\n\nexport const getAutoPlatformAttributes = (): PlatformAttributes => {\n const proc = (\n globalThis as { process?: { env?: Record<string, string>; arch?: string } }\n ).process;\n const procEnv = proc?.env ?? {};\n const env: PlatformEnv = {};\n for (const key of PLATFORM_ENV_KEYS) {\n const value = procEnv[key];\n if (value !== undefined) {\n (env as Record<string, string | undefined>)[key] = value;\n }\n }\n\n return derivePlatformAttributes({\n runtime: runtime ?? \"\",\n provider: provider ?? \"\",\n platform: platform ?? \"\",\n arch: proc?.arch ?? \"\",\n env,\n });\n};\n","import { v7 as uuidv7 } from \"uuid\";\nimport { Effect, Option } from \"effect\";\n\nimport * as EnvConfig from \"./config.js\";\nimport { getAutoPlatformAttributes } from \"./platform.js\";\n\nconst stringOrUndefined = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.length > 0 ? value : undefined;\n\nconst DEFAULT_VCS_REPOSITORY_URL =\n \"https://gitlab.safaricom.co.ke/mxl/apm/mxl-effect-sdk\";\n\nfunction resolveCommitRevision(): string | undefined {\n return (\n stringOrUndefined(process.env[\"COMMIT_SHA\"]) ??\n stringOrUndefined(process.env[\"VERCEL_GIT_COMMIT_SHA\"]) ??\n stringOrUndefined(process.env[\"GITHUB_SHA\"]) ??\n stringOrUndefined(process.env[\"RAILWAY_GIT_COMMIT_SHA\"]) ??\n stringOrUndefined(process.env[\"CF_PAGES_COMMIT_SHA\"]) ??\n stringOrUndefined(process.env[\"RENDER_GIT_COMMIT\"])\n );\n}\n\nlet serviceInstanceId: string | undefined;\nconst getServiceInstanceId = (): string => (serviceInstanceId ??= uuidv7());\n\nexport interface ResourceConfigInput {\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 sdkType?: \"server\" | undefined;\n}\n\nexport interface ResolvedResource {\n readonly endpoint: string | undefined;\n readonly headers: Record<string, string> | undefined;\n readonly resource: {\n readonly serviceName: string;\n readonly serviceVersion: string | undefined;\n readonly attributes: Record<string, string>;\n };\n readonly environment: string | undefined;\n}\n\nexport const resolveResource = (\n config: ResourceConfigInput\n): Effect.Effect<ResolvedResource> =>\n Effect.gen(function* () {\n const disabled = yield* EnvConfig.sdkDisabled;\n if (disabled) {\n return {\n endpoint: undefined,\n environment: undefined,\n headers: undefined,\n resource: {\n attributes: {},\n serviceName: config.serviceName ?? \"unknown\",\n serviceVersion: undefined,\n },\n } satisfies ResolvedResource;\n }\n\n const envEndpoint = yield* EnvConfig.endpoint;\n const endpoint =\n config.endpoint ??\n Option.getOrUndefined(envEndpoint) ??\n EnvConfig.defaultOtlpEndpoint;\n\n const envHeaders = yield* EnvConfig.otlpHeaders;\n const headers =\n config.headers && Object.keys(config.headers).length > 0\n ? config.headers\n : Object.keys(envHeaders).length > 0\n ? envHeaders\n : undefined;\n\n const envServiceVersion = yield* EnvConfig.serviceVersion;\n const serviceVersion =\n config.serviceVersion ?? Option.getOrUndefined(envServiceVersion);\n\n const envEnvironment = yield* EnvConfig.environment;\n const environment = config.environment ?? envEnvironment;\n\n const envOtelServiceName = yield* EnvConfig.otelServiceName;\n const serviceName =\n config.serviceName ??\n Option.getOrUndefined(envOtelServiceName) ??\n \"unknown\";\n\n const envResourceAttributes = yield* EnvConfig.otelResourceAttributes;\n\n const attributes: Record<string, string> = {};\n Object.assign(attributes, getAutoPlatformAttributes());\n attributes[\"mxl.sdk.type\"] = config.sdkType ?? \"server\";\n attributes[\"service.instance.id\"] = getServiceInstanceId();\n if (environment) {\n attributes[\"deployment.environment\"] = environment;\n attributes[\"deployment.environment.name\"] = environment;\n }\n if (serviceVersion) {\n attributes[\"service.version\"] = serviceVersion;\n }\n const commitRevision = resolveCommitRevision();\n if (commitRevision) {\n attributes[\"deployment.commit_sha\"] = commitRevision;\n attributes[\"vcs.ref.head.revision\"] = commitRevision;\n }\n attributes[\"vcs.repository.url.full\"] =\n stringOrUndefined(process.env[\"MXL_VCS_REPOSITORY_URL\"]) ??\n DEFAULT_VCS_REPOSITORY_URL;\n Object.assign(attributes, envResourceAttributes);\n if (config.attributes) Object.assign(attributes, config.attributes);\n\n return {\n endpoint,\n environment,\n headers,\n resource: { serviceName, serviceVersion, attributes },\n } satisfies ResolvedResource;\n }).pipe(Effect.orDie);\n","import type { Duration } from \"effect\";\nimport { Effect, Layer } from \"effect\";\nimport { FetchHttpClient } from \"effect/http\";\nimport { Otlp } from \"effect/observability\";\n\nimport { otelGlobalContextBridgeLayer } from \"./otel-context-bridge.js\";\nimport { normalizeOtlpBaseUrl } from \"./otlp.js\";\nimport { resolveResource } from \"./resource.js\";\n\nexport interface MxlConfig {\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 maxBatchSize?: number | undefined;\n readonly loggerExportInterval?: Duration.Input | undefined;\n readonly metricsExportInterval?: Duration.Input | undefined;\n readonly tracerExportInterval?: Duration.Input | undefined;\n readonly shutdownTimeout?: Duration.Input | undefined;\n}\n\n/**\n * Create an Effect Layer that provides OpenTelemetry traces, logs, and metrics\n * configured for MXL's OTLP collector.\n *\n * Returns a no-op layer when `OTEL_SDK_DISABLED=true`.\n *\n * Wraps the OTLP tracer with `otelGlobalContextBridgeLayer` via `provideMerge`.\n * Measured on effect@4.0.2: `Otlp.layerJson({ tracerContext })` still\n * types the hook as `(primitive, span) => X` and Effect returns that result as\n * the evaluation, so the callback cannot call `~effect/Effect/evaluate` without\n * a `fiber`. `Layer.updateService` also cannot wrap a Tracer the OTLP layer\n * already provides. Keep the merge wrap until Effect passes fiber or evaluates\n * after a side-effect-only context hook.\n */\nexport const layer = (config: MxlConfig = {}) =>\n Layer.unwrap(\n Effect.gen(function* () {\n const resolved = yield* resolveResource({ ...config, sdkType: \"server\" });\n if (!resolved.endpoint) return Layer.empty;\n\n const otlp = Otlp.layerJson({\n baseUrl: normalizeOtlpBaseUrl(resolved.endpoint),\n headers: resolved.headers,\n resource: resolved.resource,\n maxBatchSize: config.maxBatchSize,\n loggerExportInterval: config.loggerExportInterval,\n metricsExportInterval: config.metricsExportInterval,\n tracerExportInterval: config.tracerExportInterval,\n shutdownTimeout: config.shutdownTimeout,\n }).pipe(Layer.provide(FetchHttpClient.layer));\n\n return otelGlobalContextBridgeLayer.pipe(Layer.provideMerge(otlp));\n })\n );\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 body: BodyInit =\n typeof input.body === \"string\" ? input.body : new Uint8Array(input.body);\n const response = await fetch(input.url, {\n 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 { Context, Effect, Layer } from \"effect\";\n\nimport {\n type MxlEventsConfig,\n type MxlEventsHandle,\n layer as eventsLayer,\n makeHandle,\n} from \"../server/events.js\";\nimport { layer as otelLayer } from \"../server/layer.js\";\nimport { guardFlush, makeSerializedFlush } from \"../shared/flush-core.js\";\n\nexport interface Config {\n readonly apiKey?: string | undefined;\n readonly attributes?: Record<string, unknown> | undefined;\n readonly clientId?: string | undefined;\n readonly endpoint?: string | undefined;\n readonly environment?: string | undefined;\n readonly ingestUrl?: string | undefined;\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n}\n\nexport class WorkerEnvironment extends Context.Service<\n WorkerEnvironment,\n Record<string, unknown>\n>()(\"Cloudflare.Workers.WorkerEnvironment\") {}\n\nexport interface Telemetry {\n readonly flush: (env: Record<string, unknown>) => Promise<void>;\n readonly layer: Layer.Layer<never>;\n readonly requestLayer: Layer.Layer<never, never, WorkerEnvironment>;\n}\n\nconst stringFromEnv = (\n env: Record<string, unknown>,\n key: string\n): string | undefined => {\n const value = env[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n};\n\nconst resolveEventsConfig = (\n env: Record<string, unknown>,\n config: Config\n): MxlEventsConfig => ({\n apiKey: config.apiKey ?? stringFromEnv(env, \"MXL_API_KEY\"),\n clientId: config.clientId ?? stringFromEnv(env, \"MXL_CLIENT_ID\"),\n endpoint:\n config.ingestUrl ?? stringFromEnv(env, \"MXL_BASKET_URL\") ?? undefined,\n flushInterval: 0,\n serviceName:\n config.serviceName ?? stringFromEnv(env, \"OTEL_SERVICE_NAME\") ?? \"unknown\",\n});\n\nexport const make = (config: Config = {}): Telemetry => {\n let handle: MxlEventsHandle | undefined;\n const state = { disabledUntil: 0 };\n\n const flush = makeSerializedFlush(\n guardFlush(\n \"[MxlCloudflareSDK]\",\n async (env: Record<string, unknown>): Promise<void> => {\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n const eventsConfig = resolveEventsConfig(env, config);\n if (!eventsConfig.clientId) {\n return;\n }\n handle ??= makeHandle(eventsConfig);\n await handle.flush();\n },\n state\n )\n );\n\n const layer = otelLayer({\n attributes: Object.fromEntries(\n Object.entries(config.attributes ?? {}).map(([key, value]) => [\n key,\n String(value),\n ])\n ),\n endpoint: config.endpoint,\n environment: config.environment,\n serviceName: config.serviceName,\n serviceVersion: config.serviceVersion,\n });\n\n const requestLayer = Layer.mergeAll(\n layer,\n Layer.unwrap(\n Effect.gen(function* () {\n const env = yield* WorkerEnvironment;\n const eventsConfig = resolveEventsConfig(env, config);\n if (!eventsConfig.clientId) {\n return Layer.empty;\n }\n return eventsLayer(eventsConfig);\n })\n ),\n Layer.effectDiscard(\n Effect.gen(function* () {\n const env = yield* WorkerEnvironment;\n yield* Effect.addFinalizer(() => Effect.promise(() => flush(env)));\n })\n )\n );\n\n return {\n flush,\n layer,\n requestLayer,\n };\n};\n\nexport const MxlCloudflareSDK = { make };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["stringOrUndefined","resolveEventsConfig","make","layer","uuidv7","EnvConfig.sdkDisabled","EnvConfig.endpoint","endpoint","EnvConfig.otlpHeaders","EnvConfig.serviceVersion","serviceVersion","EnvConfig.environment","environment","EnvConfig.otelServiceName","EnvConfig.otelResourceAttributes","layer","otelLayer","eventsLayer"],"sources":["../../src/shared/product-event.ts","../../src/version.ts","../../src/server/events.ts","../../src/server/otel-context-bridge.ts","../../src/server/otlp.ts","../../src/server/config.ts","../../src/shared/try-sync.ts","../../src/server/container.ts","../../src/server/platform.ts","../../src/server/resource.ts","../../src/server/layer.ts","../../src/shared/flush-core.ts","../../src/cloudflare/index.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 const SDK_NAME = 'mxl-effect-sdk';\nexport const SDK_VERSION = '0.0.24';\n","import {\n Cause,\n Context,\n Duration,\n Effect,\n Layer,\n ManagedRuntime,\n Option,\n Schedule,\n Schema,\n Semaphore,\n} from 'effect';\nimport { FetchHttpClient, HttpClient, HttpClientRequest } from 'effect/http';\nimport { SchemaJITCompiler } from 'effect/schema';\nimport type {\n ProductEventLine,\n TrackOptions,\n} from '../shared/product-event.js';\nimport {\n encodeNdjson,\n encodeProductEventLine,\n} from '../shared/product-event.js';\nimport { SDK_NAME, SDK_VERSION } from '../version.js';\n\nexport type { TrackOptions } from '../shared/product-event.js';\n\nconst DEFAULT_FLUSH_INTERVAL = Duration.seconds(5);\nconst DEFAULT_MAX_BATCH_SIZE = 100;\nconst MAX_BUFFERED_EVENTS = 1_000;\nconst WARN_INTERVAL_MS = 30_000;\nconst DEFAULT_BASKET_URL = 'http://127.0.0.1:8000';\n\nexport interface MxlEventsConfig {\n readonly apiKey?: string | undefined;\n readonly clientId?: string | undefined;\n readonly endpoint?: string | undefined;\n readonly eventsPath?: string | undefined;\n readonly flushInterval?: Duration.Input | undefined;\n readonly maxBatchSize?: number | undefined;\n readonly serviceName?: string | undefined;\n}\n\nexport interface MxlEventsApi {\n readonly flush: Effect.Effect<void>;\n readonly track: (name: string, options?: TrackOptions) => Effect.Effect<void>;\n}\n\nexport class MxlEvents extends Context.Service<MxlEvents, MxlEventsApi>()(\n '@safaricom-mxl/effect-sdk/MxlEvents',\n) {}\n\nexport class ProductEventsPostError extends Schema.TaggedError<ProductEventsPostError>()(\n '@safaricom-mxl/effect-sdk/ProductEventsPostError',\n {\n cause: Schema.optionalKey(Schema.Defect()),\n message: Schema.String,\n status: Schema.optionalKey(Schema.Number),\n },\n) {}\n\nSchemaJITCompiler.enable(ProductEventsPostError.ast);\nconst stringOrUndefined = (value: string | undefined): string | undefined =>\n value && value.length > 0 ? value : undefined;\n\nconst resolveEventsConfig = (config: MxlEventsConfig) => {\n const endpoint = (\n config.endpoint ??\n process.env['MXL_BASKET_URL'] ??\n DEFAULT_BASKET_URL\n ).replace(/\\/$/, '');\n return {\n apiKey: stringOrUndefined(config.apiKey ?? process.env['MXL_API_KEY']),\n clientId: stringOrUndefined(\n config.clientId ?? process.env['MXL_CLIENT_ID'],\n ),\n serviceName:\n config.serviceName ?? process.env['OTEL_SERVICE_NAME'] ?? 'unknown',\n url: `${endpoint}${config.eventsPath ?? '/events'}`,\n };\n};\n\nexport const make = Effect.fn('MxlEvents.make')(function* (\n config: MxlEventsConfig = {},\n) {\n const resolved = resolveEventsConfig(config);\n const maxBatchSize = Math.max(\n 1,\n config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n );\n const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);\n const flushing = yield* Semaphore.make(1);\n\n let buffer: Array<ProductEventLine> = [];\n let warnedAboutName = false;\n let warnedAboutClient = false;\n let lastPostWarnAt = 0;\n\n const warnPost = (error: ProductEventsPostError): void => {\n const now = Date.now();\n if (now - lastPostWarnAt < WARN_INTERVAL_MS) return;\n lastPostWarnAt = now;\n console.warn(\n `[MxlEvents] ${error.message} (dropping batch)`,\n error.cause ?? '',\n );\n };\n const warnMissingClient = (): void => {\n if (warnedAboutClient) return;\n warnedAboutClient = true;\n console.warn(\n '[MxlEvents] no client id — set MXL_CLIENT_ID or pass `clientId`; dropping events',\n );\n };\n const warnEmptyName = (): void => {\n if (warnedAboutName) return;\n warnedAboutName = true;\n console.warn(\n '[MxlEvents] track() needs a non-empty event name; the call was ignored.',\n );\n };\n\n const post = (lines: ReadonlyArray<ProductEventLine>) => {\n const headers: Record<string, string> = {\n 'mxl-client-id': resolved.clientId ?? '',\n 'mxl-sdk-name': SDK_NAME,\n 'mxl-sdk-version': SDK_VERSION,\n 'user-agent': `${SDK_NAME}-server/${SDK_VERSION}`,\n };\n if (resolved.apiKey) {\n headers['mxl-api-key'] = resolved.apiKey;\n }\n return HttpClientRequest.post(resolved.url).pipe(\n HttpClientRequest.setHeaders(headers),\n HttpClientRequest.bodyText(encodeNdjson(lines), 'application/x-ndjson'),\n client.execute,\n Effect.asVoid,\n Effect.mapError((error) =>\n error.response !== undefined\n ? new ProductEventsPostError({\n message: `POST ${resolved.url} → ${error.response.status}`,\n status: error.response.status,\n })\n : new ProductEventsPostError({\n cause: error,\n message: `POST ${resolved.url} failed: ${error.message}`,\n }),\n ),\n );\n };\n\n const flush: Effect.Effect<void> = flushing.withPermits(1)(\n Effect.suspend(() => {\n if (buffer.length === 0) return Effect.void;\n const batch = buffer;\n buffer = [];\n if (resolved.clientId === undefined) {\n warnMissingClient();\n return Effect.void;\n }\n return post(batch).pipe(\n Effect.catchCause((cause) =>\n Effect.sync(() => {\n warnPost(\n Option.getOrElse(\n Cause.findErrorOption(cause),\n () =>\n new ProductEventsPostError({\n cause: Cause.squash(cause),\n message: `POST ${resolved.url} failed`,\n }),\n ),\n );\n }),\n ),\n );\n }),\n );\n\n const track = (\n name: string,\n options: TrackOptions = {},\n ): Effect.Effect<void> =>\n Effect.sync(() => {\n const line = encodeProductEventLine({\n name,\n options,\n serviceName: resolved.serviceName,\n source: 'server',\n });\n if (line === undefined) {\n warnEmptyName();\n return false;\n }\n buffer.push(line);\n if (buffer.length > MAX_BUFFERED_EVENTS) {\n buffer.splice(0, buffer.length - MAX_BUFFERED_EVENTS);\n }\n return buffer.length >= maxBatchSize;\n }).pipe(\n Effect.flatMap((full) => (full ? Effect.forkDetach(flush) : Effect.void)),\n Effect.asVoid,\n );\n\n const interval = config.flushInterval ?? DEFAULT_FLUSH_INTERVAL;\n if (Duration.toMillis(interval) > 0) {\n yield* Effect.forkScoped(\n flush.pipe(Effect.schedule(Schedule.spaced(interval))),\n );\n }\n yield* Effect.addFinalizer(() => flush);\n\n return { flush, track } satisfies MxlEventsApi;\n});\n\nexport const layer = (config: MxlEventsConfig = {}): Layer.Layer<MxlEvents> =>\n Layer.effect(MxlEvents, make(config)).pipe(\n Layer.provide(FetchHttpClient.layer),\n );\n\nexport interface MxlEventsHandle {\n readonly dispose: () => Promise<void>;\n readonly flush: () => Promise<void>;\n readonly track: (name: string, options?: TrackOptions) => void;\n}\n\nexport const makeHandle = (config: MxlEventsConfig = {}): MxlEventsHandle => {\n const runtime = ManagedRuntime.make(layer(config));\n const swallow = (promise: Promise<unknown>): Promise<void> =>\n promise.then(\n () => undefined,\n () => undefined,\n );\n return {\n dispose: () => swallow(runtime.dispose()),\n flush: () =>\n swallow(\n runtime.runPromise(\n Effect.gen(function* () {\n const events = yield* MxlEvents;\n yield* events.flush;\n }),\n ),\n ),\n track: (name, options) => {\n void swallow(\n runtime.runPromise(\n Effect.gen(function* () {\n const events = yield* MxlEvents;\n yield* events.track(name, options);\n }),\n ),\n );\n },\n };\n};\n","import * as Otel from '@opentelemetry/api';\nimport { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';\nimport { Effect, Layer } from 'effect';\nimport * as Tracer from 'effect/Tracer';\n\nconst evaluate = '~effect/Effect/evaluate' as const;\nconst contextManagerProbe = Otel.createContextKey(\n 'mxl.effect-sdk.context-manager-probe',\n);\n\nlet contextManagerInstalled = false;\n\nfunction hasWorkingContextManager(): boolean {\n return Otel.context.with(\n Otel.ROOT_CONTEXT.setValue(contextManagerProbe, true),\n () => Otel.context.active().getValue(contextManagerProbe) === true,\n );\n}\n\n/**\n * Install AsyncLocalStorage so `Otel.context.with` actually propagates.\n * Does not replace a context manager the host app already registered.\n */\nexport function ensureOtelContextManager(): void {\n if (contextManagerInstalled) return;\n if (hasWorkingContextManager()) {\n contextManagerInstalled = true;\n return;\n }\n const manager = new AsyncLocalStorageContextManager();\n manager.enable();\n if (!Otel.context.setGlobalContextManager(manager)) {\n manager.disable();\n contextManagerInstalled = true;\n return;\n }\n contextManagerInstalled = true;\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/**\n * Put Effect span ids on the global OpenTelemetry context.\n *\n * Id-only `setSpanContext`. Live `Otel.Span` objects come from\n * `OtelTracer.currentOtelSpan` in Effect space, not from a private TypeId.\n */\nexport function effectSpanToOtelContext(\n activeContext: Otel.Context,\n span: Tracer.AnySpan,\n): Otel.Context {\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/**\n * Wrap a tracer so Effect spans activate global `@opentelemetry/api` context.\n *\n * Prefer this over `Otlp.layerJson({ tracerContext })` until Effect changes\n * that hook. On effect@4.0.6 the callback is still `(primitive, span)`\n * and must return the evaluation, but `EffectPrimitive` only evaluates with a\n * `fiber`. Keep this wrap until Effect passes fiber or evaluates after the hook.\n */\nexport function wrapTracerWithOtelContextBridge(\n base: Tracer.Tracer,\n): Tracer.Tracer {\n ensureOtelContextManager();\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 provided over `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 { Config, Effect, Option } from 'effect';\n\nconst DEFAULT_OTLP_ENDPOINT = 'http://127.0.0.1:4318';\n\n/** Resolve the OTLP exporter endpoint from standard env vars. */\nexport const endpoint = Effect.gen(function* () {\n const otlp = yield* Config.option(\n Config.String('OTEL_EXPORTER_OTLP_ENDPOINT'),\n );\n if (Option.isSome(otlp)) return otlp;\n return yield* Config.option(Config.String('OTLP_ENDPOINT'));\n});\n\nexport const defaultOtlpEndpoint = DEFAULT_OTLP_ENDPOINT;\n\nexport const sdkDisabled = Config.Boolean('OTEL_SDK_DISABLED').pipe(\n Config.withDefault(false),\n);\n\n/**\n * Resolve service version / commit SHA from platform-specific env vars.\n *\n * Priority: npm_package_version > COMMIT_SHA > RAILWAY_GIT_COMMIT_SHA >\n * VERCEL_GIT_COMMIT_SHA > CF_PAGES_COMMIT_SHA > RENDER_GIT_COMMIT\n */\nexport const serviceVersion = Config.option(\n Config.String('npm_package_version').pipe(\n Config.orElse(() => Config.String('COMMIT_SHA')),\n Config.orElse(() => Config.String('RAILWAY_GIT_COMMIT_SHA')),\n Config.orElse(() => Config.String('VERCEL_GIT_COMMIT_SHA')),\n Config.orElse(() => Config.String('CF_PAGES_COMMIT_SHA')),\n Config.orElse(() => Config.String('RENDER_GIT_COMMIT')),\n ),\n);\n\n/**\n * Resolve deployment environment from env vars.\n *\n * Priority: NODE_ENV > DEPLOYMENT_ENV > \"development\"\n */\nexport const environment = Config.String('NODE_ENV').pipe(\n Config.orElse(() => Config.String('DEPLOYMENT_ENV')),\n Config.withDefault('development'),\n);\n\n/** OTel-standard service name override. */\nexport const otelServiceName = Config.option(\n Config.String('OTEL_SERVICE_NAME'),\n);\n\n/**\n * Parse the OTel-standard `OTEL_RESOURCE_ATTRIBUTES` env var.\n *\n * Format per the OTel spec: comma-separated `key=value` pairs, values may be\n * URL-encoded.\n */\nexport const parseOtelResourceAttributes = (\n input: string,\n): Record<string, string> => {\n const result: Record<string, string> = {};\n for (const pair of input.split(',')) {\n const eq = pair.indexOf('=');\n if (eq === -1) continue;\n const key = pair.slice(0, eq).trim();\n if (!key) continue;\n const raw = pair.slice(eq + 1).trim();\n let value = raw;\n try {\n value = decodeURIComponent(raw);\n } catch {\n // Fall back to raw string when decoding fails.\n }\n result[key] = value;\n }\n return result;\n};\n\n/** Resolve and parse `OTEL_RESOURCE_ATTRIBUTES`. */\nexport const otelResourceAttributes = Effect.gen(function* () {\n const raw = yield* Config.option(Config.String('OTEL_RESOURCE_ATTRIBUTES'));\n return Option.match(raw, {\n onNone: () => ({}) as Record<string, string>,\n onSome: (value) => parseOtelResourceAttributes(value),\n });\n});\n\n/**\n * Parse `OTEL_EXPORTER_OTLP_HEADERS` (`key=value,key2=value2`) into a header map.\n */\nexport const parseOtlpHeaders = (input: string): Record<string, string> => {\n const result: Record<string, string> = {};\n for (const pair of input.split(',')) {\n const eq = pair.indexOf('=');\n if (eq === -1) continue;\n const key = pair.slice(0, eq).trim();\n if (!key) continue;\n const raw = pair.slice(eq + 1).trim();\n try {\n result[key] = decodeURIComponent(raw);\n } catch {\n result[key] = raw;\n }\n }\n return result;\n};\n\n/** Resolve OTLP export headers from env. */\nexport const otlpHeaders = Effect.gen(function* () {\n const raw = yield* Config.option(Config.String('OTEL_EXPORTER_OTLP_HEADERS'));\n return Option.match(raw, {\n onNone: () => ({}) as Record<string, string>,\n onSome: (value) => parseOtlpHeaders(value),\n });\n});\n","// A throwing host call, as a value.\n//\n// The SDK runs inside someone else's app, so it touches host APIs that throw\n// rather than return a failure: `Intl` in a locale-stripped build, `BigInt` on a\n// wire value that isn't numeric, `decodeURIComponent` on a literal `%`, a\n// `node:fs` read the sandbox refuses. Every one of those is best-effort — the\n// SDK degrades an attribute rather than breaking the host — and `Effect.try` is\n// what makes that an `Option` the caller has to answer for instead of a `catch`\n// block that quietly covers the next three statements too.\nimport { Effect, Option } from 'effect';\n\n/** Run a throwing synchronous call, `None` if it threw. */\nexport const trySync = <A>(thunk: () => A): Option.Option<A> =>\n Effect.runSync(Effect.option(Effect.try(thunk)));\n\n/** Run a throwing synchronous call, `undefined` if it threw. */\nexport const trySyncOrUndefined = <A>(thunk: () => A): A | undefined =>\n Option.getOrUndefined(trySync(thunk));\n","// Best-effort Docker container identity detection\n//\n// Emits `container.runtime` / `container.id` resource attributes so spans and\n// logs from an app running inside a plain Docker container correlate with the\n// docker_stats metrics the Maple Docker agent collects.\n//\n// Detection is inherently best-effort on modern Docker:\n// - cgroup v2 with a private cgroup namespace (the default) renders\n// `/proc/self/cgroup` as `0::/`, hiding the container id — but\n// `/proc/self/mountinfo` usually still carries the id in the overlay paths.\n// - The hostname fallback only fires when `/.dockerenv` exists AND the hostname\n// is 12 hex chars (Docker's default); a user-set `hostname:` defeats it.\n// The reliable path is documented instead: set\n// `OTEL_RESOURCE_ATTRIBUTES=container.id=…,container.name=…`, which takes\n// precedence over these values (later writers win in `resolveResource`).\n//\n// No static `node:fs`/`node:os` imports: this module is bundled into the\n// Cloudflare preset's dependency graph, where those specifiers break the\n// build. `process.getBuiltinModule` (Node ≥ 20.16, Bun) loads them lazily and\n// synchronously; when it's absent, detection just reports nothing.\n\nimport { trySyncOrUndefined } from '../shared/try-sync.js';\n\nconst CONTAINER_ID_RE = /\\/docker\\/containers\\/([0-9a-f]{64})\\//;\nconst CGROUP_ID_RE = /([0-9a-f]{64})/;\nconst SHORT_ID_HOSTNAME_RE = /^[0-9a-f]{12}$/;\n\nexport interface ContainerProbe {\n /** True when the path exists — `/.dockerenv` is the Docker marker file. */\n readonly exists: (path: string) => boolean;\n readonly readFile: (path: string) => string;\n readonly hostname: () => string;\n}\n\n/**\n * Pure derivation from probed inputs — the testable core. Every probe call is\n * guarded by the caller (`getContainerAttributes` wraps the whole thing).\n */\nexport const deriveContainerAttributes = (\n probe: ContainerProbe,\n): Record<string, string> => {\n const inDocker =\n trySyncOrUndefined(() => probe.exists('/.dockerenv')) ?? false;\n\n const attrs: Record<string, string> = {};\n // `container.runtime.name` since semconv v1.37.0; the bare `container.runtime`\n // it replaced is not emitted, because every read path coalesces both\n // spellings anyway (`containerRuntimeExpr`) for the sake of third-party\n // collectors that still send the old one.\n if (inDocker) attrs['container.runtime.name'] = 'docker';\n\n // Lazy fallback chain — this runs at every SDK boot (customer processes),\n // and mountinfo can run to hundreds of KB, so later probes only fire when\n // the earlier ones miss.\n const containerId =\n trySyncOrUndefined(\n () => probe.readFile('/proc/self/mountinfo').match(CONTAINER_ID_RE)?.[1],\n ) ??\n trySyncOrUndefined(\n () => probe.readFile('/proc/self/cgroup').match(CGROUP_ID_RE)?.[1],\n ) ??\n (inDocker\n ? trySyncOrUndefined(() => {\n const name = probe.hostname();\n return SHORT_ID_HOSTNAME_RE.test(name) ? name : undefined;\n })\n : undefined);\n if (containerId) attrs['container.id'] = containerId;\n return attrs;\n};\n\ntype FsModule = {\n existsSync: (path: string) => boolean;\n readFileSync: (path: string, enc: string) => string;\n};\ntype OsModule = { hostname: () => string };\n\n/**\n * The two builtin loads this module performs, declared as an overload pair so\n * `getBuiltinModule` never surfaces an untyped value to callers.\n */\ntype LoadBuiltin = ((id: 'node:fs') => FsModule | undefined) &\n ((id: 'node:os') => OsModule | undefined);\n\nlet cached: Record<string, string> | undefined;\n\n/**\n * Read live container identity from the filesystem, memoized for the process\n * lifetime (container identity cannot change under a running process). Returns\n * `{}` anywhere the probes aren't available — non-Linux, workerd, browsers.\n */\nexport const getContainerAttributes = (): Record<string, string> => {\n if (cached) return cached;\n const proc = (\n globalThis as {\n process?: { platform?: string; getBuiltinModule?: LoadBuiltin };\n }\n ).process;\n const loadBuiltin = proc?.getBuiltinModule;\n if (proc?.platform !== 'linux' || typeof loadBuiltin !== 'function') {\n cached = {};\n return cached;\n }\n const fs = trySyncOrUndefined(() => loadBuiltin('node:fs'));\n const os = trySyncOrUndefined(() => loadBuiltin('node:os'));\n if (!fs || !os) {\n cached = {};\n return cached;\n }\n\n cached = deriveContainerAttributes({\n exists: (path) => fs.existsSync(path),\n readFile: (path) => fs.readFileSync(path, 'utf8'),\n hostname: () => os.hostname(),\n });\n return cached;\n};\n","import { Match } from 'effect';\nimport { platform, provider, runtime } from 'std-env';\n\nexport interface PlatformEnv {\n readonly AWS_LAMBDA_FUNCTION_NAME?: string | undefined;\n readonly AWS_LAMBDA_FUNCTION_VERSION?: string | undefined;\n readonly AWS_LAMBDA_LOG_STREAM_NAME?: string | undefined;\n readonly AWS_REGION?: string | undefined;\n readonly AWS_DEFAULT_REGION?: string | undefined;\n readonly VERCEL?: string | undefined;\n readonly VERCEL_REGION?: string | undefined;\n readonly VERCEL_DEPLOYMENT_ID?: string | undefined;\n readonly K_SERVICE?: string | undefined;\n readonly K_REVISION?: string | undefined;\n readonly CLOUD_RUN_REGION?: string | undefined;\n readonly DENO_REGION?: string | undefined;\n readonly DENO_DEPLOYMENT_ID?: string | undefined;\n readonly RENDER_INSTANCE_ID?: string | undefined;\n readonly RAILWAY_REPLICA_ID?: string | undefined;\n}\n\nexport interface PlatformInputs {\n readonly runtime: string;\n readonly provider: string;\n readonly platform: string;\n readonly arch: string;\n readonly env: PlatformEnv;\n}\n\ntype Attrs = Record<string, string>;\nconst empty: Attrs = {};\n\nconst runtimeAttrs = (rt: string): Attrs =>\n Match.value(rt).pipe(\n Match.when('node', () => ({ 'process.runtime.name': 'nodejs' })),\n Match.whenOr('bun', 'deno', 'workerd', 'edge-light', 'fastly', (name) => ({\n 'process.runtime.name': name,\n })),\n Match.orElse((): Attrs => empty),\n );\n\nconst osAttrs = (plat: string): Attrs =>\n Match.value(plat).pipe(\n Match.when('win32', () => ({ 'os.type': 'windows' })),\n Match.whenOr('darwin', 'linux', 'freebsd', 'openbsd', 'netbsd', (p) => ({\n 'os.type': p,\n })),\n Match.orElse((): Attrs => empty),\n );\n\nconst archAttrs = (a: string): Attrs =>\n a === ''\n ? empty\n : {\n 'host.arch': Match.value(a).pipe(\n Match.when('x64', () => 'amd64'),\n Match.when('ia32', () => 'x86'),\n Match.orElse((other) => other),\n ),\n };\n\nconst lambdaAttrs = (env: PlatformEnv): Attrs => ({\n 'cloud.provider': 'aws',\n 'cloud.platform': 'aws_lambda',\n 'faas.name': env.AWS_LAMBDA_FUNCTION_NAME!,\n ...(env.AWS_LAMBDA_FUNCTION_VERSION && {\n 'faas.version': env.AWS_LAMBDA_FUNCTION_VERSION,\n }),\n ...(env.AWS_LAMBDA_LOG_STREAM_NAME && {\n 'faas.instance': env.AWS_LAMBDA_LOG_STREAM_NAME,\n }),\n ...(env.AWS_REGION\n ? { 'cloud.region': env.AWS_REGION }\n : env.AWS_DEFAULT_REGION\n ? { 'cloud.region': env.AWS_DEFAULT_REGION }\n : {}),\n});\n\nconst providerAttrs = (prov: string, env: PlatformEnv): Attrs =>\n Match.value(prov).pipe(\n Match.when('cloudflare_workers', (): Attrs => ({\n 'cloud.provider': 'cloudflare',\n 'cloud.platform': 'cloudflare.workers',\n })),\n Match.when('cloudflare_pages', (): Attrs => ({\n 'cloud.provider': 'cloudflare',\n 'cloud.platform': 'cloudflare.pages',\n })),\n Match.when('vercel', (): Attrs => ({\n 'cloud.provider': 'vercel',\n 'cloud.platform': 'vercel',\n ...(env.VERCEL_REGION && { 'cloud.region': env.VERCEL_REGION }),\n ...(env.VERCEL_DEPLOYMENT_ID && {\n 'faas.instance': env.VERCEL_DEPLOYMENT_ID,\n }),\n })),\n Match.when('netlify', (): Attrs => ({\n 'cloud.provider': 'netlify',\n 'cloud.platform': 'netlify',\n })),\n Match.whenOr('google_cloudrun', 'google_cloudrun_job', (): Attrs => ({\n 'cloud.provider': 'gcp',\n 'cloud.platform': 'gcp_cloud_run',\n ...(env.K_SERVICE && { 'faas.name': env.K_SERVICE }),\n ...(env.K_REVISION && { 'faas.version': env.K_REVISION }),\n ...(env.CLOUD_RUN_REGION && { 'cloud.region': env.CLOUD_RUN_REGION }),\n })),\n Match.when('firebase_app_hosting', (): Attrs => ({\n 'cloud.provider': 'gcp',\n 'cloud.platform': 'gcp_firebase_app_hosting',\n })),\n Match.when('aws_amplify', (): Attrs => ({\n 'cloud.provider': 'aws',\n 'cloud.platform': 'aws_amplify',\n })),\n Match.when('deno-deploy', (): Attrs => ({\n 'cloud.provider': 'deno',\n 'cloud.platform': 'deno_deploy',\n ...(env.DENO_REGION && { 'cloud.region': env.DENO_REGION }),\n ...(env.DENO_DEPLOYMENT_ID && {\n 'faas.instance': env.DENO_DEPLOYMENT_ID,\n }),\n })),\n Match.when('render', (): Attrs => ({\n 'cloud.provider': 'render',\n 'cloud.platform': 'render',\n ...(env.RENDER_INSTANCE_ID && {\n 'faas.instance': env.RENDER_INSTANCE_ID,\n }),\n })),\n Match.when('railway', (): Attrs => ({\n 'cloud.provider': 'railway',\n 'cloud.platform': 'railway',\n ...(env.RAILWAY_REPLICA_ID && {\n 'faas.instance': env.RAILWAY_REPLICA_ID,\n }),\n })),\n Match.when('edgeone_pages', (): Attrs => ({\n 'cloud.provider': 'tencent_cloud',\n 'cloud.platform': 'tencent_edgeone_pages',\n })),\n Match.orElse((): Attrs => empty),\n );\n\nconst workerdFallback = (rt: string, alreadyResolved: boolean): Attrs =>\n rt === 'workerd' && !alreadyResolved\n ? {\n 'cloud.provider': 'cloudflare',\n 'cloud.platform': 'cloudflare.workers',\n }\n : empty;\n\nconst vercelEdgeFallback = (\n rt: string,\n alreadyResolved: boolean,\n env: PlatformEnv,\n): Attrs =>\n rt === 'edge-light' && !alreadyResolved && env.VERCEL\n ? { 'cloud.provider': 'vercel', 'cloud.platform': 'vercel' }\n : empty;\n\nexport const derivePlatformAttributes = (\n inputs: PlatformInputs,\n): PlatformAttributes => {\n const { runtime: rt, provider: prov, platform: plat, arch, env } = inputs;\n\n const cloudAttrs = env.AWS_LAMBDA_FUNCTION_NAME\n ? lambdaAttrs(env)\n : providerAttrs(prov, env);\n const cloudResolved = 'cloud.provider' in cloudAttrs;\n\n return {\n ...(rt && { 'mxl.runtime': rt }),\n ...(prov && { 'mxl.provider': prov }),\n ...runtimeAttrs(rt),\n ...osAttrs(plat),\n ...archAttrs(arch),\n ...cloudAttrs,\n ...workerdFallback(rt, cloudResolved),\n ...vercelEdgeFallback(rt, cloudResolved, env),\n };\n};\n\nexport type PlatformAttributes = Attrs;\n\nconst PLATFORM_ENV_KEYS = [\n 'AWS_LAMBDA_FUNCTION_NAME',\n 'AWS_LAMBDA_FUNCTION_VERSION',\n 'AWS_LAMBDA_LOG_STREAM_NAME',\n 'AWS_REGION',\n 'AWS_DEFAULT_REGION',\n 'VERCEL',\n 'VERCEL_REGION',\n 'VERCEL_DEPLOYMENT_ID',\n 'K_SERVICE',\n 'K_REVISION',\n 'CLOUD_RUN_REGION',\n 'DENO_REGION',\n 'DENO_DEPLOYMENT_ID',\n 'RENDER_INSTANCE_ID',\n 'RAILWAY_REPLICA_ID',\n] as const;\n\nexport const getAutoPlatformAttributes = (): PlatformAttributes => {\n const proc = (\n globalThis as { process?: { env?: Record<string, string>; arch?: string } }\n ).process;\n const procEnv = proc?.env ?? {};\n const env: PlatformEnv = {};\n for (const key of PLATFORM_ENV_KEYS) {\n const value = procEnv[key];\n if (value !== undefined) {\n (env as Record<string, string | undefined>)[key] = value;\n }\n }\n\n return derivePlatformAttributes({\n runtime: runtime ?? '',\n provider: provider ?? '',\n platform: platform ?? '',\n arch: proc?.arch ?? '',\n env,\n });\n};\n","import { Effect, Option } from 'effect';\nimport { v7 as uuidv7 } from 'uuid';\n\nimport * as EnvConfig from './config.js';\nimport { getContainerAttributes } from './container.js';\nimport { getAutoPlatformAttributes } from './platform.js';\n\nconst stringOrUndefined = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined;\n\nconst DEFAULT_VCS_REPOSITORY_URL =\n 'https://gitlab.safaricom.co.ke/mxl/apm/mxl-effect-sdk';\n\nfunction resolveCommitRevision(): string | undefined {\n return (\n stringOrUndefined(process.env['COMMIT_SHA']) ??\n stringOrUndefined(process.env['VERCEL_GIT_COMMIT_SHA']) ??\n stringOrUndefined(process.env['GITHUB_SHA']) ??\n stringOrUndefined(process.env['RAILWAY_GIT_COMMIT_SHA']) ??\n stringOrUndefined(process.env['CF_PAGES_COMMIT_SHA']) ??\n stringOrUndefined(process.env['RENDER_GIT_COMMIT'])\n );\n}\n\nlet serviceInstanceId: string | undefined;\nconst getServiceInstanceId = (): string => (serviceInstanceId ??= uuidv7());\n\n/**\n * Best-effort guard for `vcs.ref.head.revision`: only stamp values that look\n * like a git commit SHA — `serviceVersion` may legitimately be a semver\n * release string, which belongs in `service.version` but not in `vcs.*`.\n */\nconst isCommitSha = (value: string | undefined): value is string =>\n value !== undefined && /^[0-9a-f]{7,40}$/i.test(value);\n\nexport interface ResourceConfigInput {\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n readonly serviceNamespace?: 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 sdkType?: 'server' | undefined;\n}\n\nexport interface ResolvedResource {\n readonly endpoint: string | undefined;\n readonly headers: Record<string, string> | undefined;\n readonly resource: {\n readonly serviceName: string;\n readonly serviceVersion: string | undefined;\n readonly attributes: Record<string, string>;\n };\n readonly environment: string | undefined;\n}\n\nexport const resolveResource = (\n config: ResourceConfigInput,\n): Effect.Effect<ResolvedResource> =>\n Effect.gen(function* () {\n const disabled = yield* EnvConfig.sdkDisabled;\n if (disabled) {\n return {\n endpoint: undefined,\n environment: undefined,\n headers: undefined,\n resource: {\n attributes: {},\n serviceName: config.serviceName ?? 'unknown',\n serviceVersion: undefined,\n },\n } satisfies ResolvedResource;\n }\n\n const envEndpoint = yield* EnvConfig.endpoint;\n const endpoint =\n config.endpoint ??\n Option.getOrUndefined(envEndpoint) ??\n EnvConfig.defaultOtlpEndpoint;\n\n const envHeaders = yield* EnvConfig.otlpHeaders;\n const headers =\n config.headers && Object.keys(config.headers).length > 0\n ? config.headers\n : Object.keys(envHeaders).length > 0\n ? envHeaders\n : undefined;\n\n const envServiceVersion = yield* EnvConfig.serviceVersion;\n const serviceVersion =\n config.serviceVersion ?? Option.getOrUndefined(envServiceVersion);\n\n const envEnvironment = yield* EnvConfig.environment;\n const environment = config.environment ?? envEnvironment;\n\n const envOtelServiceName = yield* EnvConfig.otelServiceName;\n const serviceName =\n config.serviceName ??\n Option.getOrUndefined(envOtelServiceName) ??\n 'unknown';\n\n const envResourceAttributes = yield* EnvConfig.otelResourceAttributes;\n\n const attributes: Record<string, string> = {};\n Object.assign(attributes, getAutoPlatformAttributes());\n Object.assign(attributes, getContainerAttributes());\n attributes['mxl.sdk.type'] = config.sdkType ?? 'server';\n attributes['service.instance.id'] = getServiceInstanceId();\n if (environment) {\n attributes['deployment.environment'] = environment;\n attributes['deployment.environment.name'] = environment;\n }\n if (serviceVersion) {\n attributes['service.version'] = serviceVersion;\n }\n const commitRevision = resolveCommitRevision();\n if (commitRevision) {\n attributes['deployment.commit_sha'] = commitRevision;\n }\n const headRevision =\n commitRevision ??\n (isCommitSha(serviceVersion) ? serviceVersion : undefined);\n if (headRevision) {\n attributes['vcs.ref.head.revision'] = headRevision;\n }\n attributes['vcs.repository.url.full'] =\n stringOrUndefined(process.env['MXL_VCS_REPOSITORY_URL']) ??\n DEFAULT_VCS_REPOSITORY_URL;\n if (config.serviceNamespace) {\n attributes['service.namespace'] = config.serviceNamespace;\n }\n Object.assign(attributes, envResourceAttributes);\n if (config.attributes) Object.assign(attributes, config.attributes);\n\n return {\n endpoint,\n environment,\n headers,\n resource: { serviceName, serviceVersion, attributes },\n } satisfies ResolvedResource;\n }).pipe(Effect.orDie);\n","import type { Duration } from 'effect';\nimport { Effect, Layer } from 'effect';\nimport { FetchHttpClient } from 'effect/http';\nimport { Otlp } from 'effect/observability';\n\nimport type { MxlSpanOptions } from '../shared/span-options.js';\nimport { otelGlobalContextBridgeLayer } from './otel-context-bridge.js';\nimport { normalizeOtlpBaseUrl } from './otlp.js';\nimport { resolveResource } from './resource.js';\n\nexport interface MxlConfig extends MxlSpanOptions {\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n readonly serviceNamespace?: 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 maxBatchSize?: number | undefined;\n readonly loggerExportInterval?: Duration.Input | undefined;\n readonly metricsExportInterval?: Duration.Input | undefined;\n readonly tracerExportInterval?: Duration.Input | undefined;\n readonly shutdownTimeout?: Duration.Input | undefined;\n}\n\n/**\n * Create an Effect Layer that provides OpenTelemetry traces, logs, and metrics\n * configured for MXL's OTLP collector.\n *\n * Returns a no-op layer when `OTEL_SDK_DISABLED=true`.\n *\n * Wraps the OTLP tracer with `otelGlobalContextBridgeLayer` via `provideMerge`.\n * Measured on effect@4.0.6: `Otlp.layerJson({ tracerContext })` still\n * types the hook as `(primitive, span) => X` and Effect returns that result as\n * the evaluation, so the callback cannot call `~effect/Effect/evaluate` without\n * a `fiber`. `Layer.updateService` also cannot wrap a Tracer the OTLP layer\n * already provides. Keep the merge wrap until Effect passes fiber or evaluates\n * after a side-effect-only context hook.\n */\nexport const layer = (config: MxlConfig = {}) =>\n Layer.unwrap(\n Effect.gen(function* () {\n const resolved = yield* resolveResource({ ...config, sdkType: 'server' });\n if (!resolved.endpoint) return Layer.empty;\n\n const otlp = Otlp.layerJson({\n baseUrl: normalizeOtlpBaseUrl(resolved.endpoint),\n headers: resolved.headers,\n resource: resolved.resource,\n maxBatchSize: config.maxBatchSize,\n loggerExportInterval: config.loggerExportInterval,\n metricsExportInterval: config.metricsExportInterval,\n tracerExportInterval: config.tracerExportInterval,\n shutdownTimeout: config.shutdownTimeout,\n }).pipe(Layer.provide(FetchHttpClient.layer));\n\n return otelGlobalContextBridgeLayer.pipe(Layer.provideMerge(otlp));\n }),\n );\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 body: BodyInit =\n typeof input.body === 'string' ? input.body : new Uint8Array(input.body);\n const response = await fetch(input.url, {\n 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 { Context, Effect, Layer } from 'effect';\nimport type { MxlEventsConfig, MxlEventsHandle } from '../server/events.js';\nimport { layer as eventsLayer, makeHandle } from '../server/events.js';\nimport { layer as otelLayer } from '../server/layer.js';\nimport { guardFlush, makeSerializedFlush } from '../shared/flush-core.js';\n\nexport interface Config {\n readonly apiKey?: string | undefined;\n readonly attributes?: Record<string, unknown> | undefined;\n readonly clientId?: string | undefined;\n readonly endpoint?: string | undefined;\n readonly environment?: string | undefined;\n readonly ingestUrl?: string | undefined;\n readonly serviceName?: string | undefined;\n readonly serviceVersion?: string | undefined;\n}\n\nexport class WorkerEnvironment extends Context.Service<\n WorkerEnvironment,\n Record<string, unknown>\n>()('Cloudflare.Workers.WorkerEnvironment') {}\n\nexport interface Telemetry {\n readonly flush: (env: Record<string, unknown>) => Promise<void>;\n readonly layer: Layer.Layer<never>;\n readonly requestLayer: Layer.Layer<never, never, WorkerEnvironment>;\n}\n\nconst stringFromEnv = (\n env: Record<string, unknown>,\n key: string,\n): string | undefined => {\n const value = env[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n};\n\nconst resolveEventsConfig = (\n env: Record<string, unknown>,\n config: Config,\n): MxlEventsConfig => ({\n apiKey: config.apiKey ?? stringFromEnv(env, 'MXL_API_KEY'),\n clientId: config.clientId ?? stringFromEnv(env, 'MXL_CLIENT_ID'),\n endpoint:\n config.ingestUrl ?? stringFromEnv(env, 'MXL_BASKET_URL') ?? undefined,\n flushInterval: 0,\n serviceName:\n config.serviceName ?? stringFromEnv(env, 'OTEL_SERVICE_NAME') ?? 'unknown',\n});\n\nexport const make = (config: Config = {}): Telemetry => {\n let handle: MxlEventsHandle | undefined;\n const state = { disabledUntil: 0 };\n\n const flush = makeSerializedFlush(\n guardFlush(\n '[MxlCloudflareSDK]',\n async (env: Record<string, unknown>): Promise<void> => {\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n const eventsConfig = resolveEventsConfig(env, config);\n if (!eventsConfig.clientId) {\n return;\n }\n handle ??= makeHandle(eventsConfig);\n await handle.flush();\n },\n state,\n ),\n );\n\n const layer = otelLayer({\n attributes: Object.fromEntries(\n Object.entries(config.attributes ?? {}).map(([key, value]) => [\n key,\n String(value),\n ]),\n ),\n endpoint: config.endpoint,\n environment: config.environment,\n serviceName: config.serviceName,\n serviceVersion: config.serviceVersion,\n });\n\n const requestLayer = Layer.mergeAll(\n layer,\n Layer.unwrap(\n Effect.gen(function* () {\n const env = yield* WorkerEnvironment;\n const eventsConfig = resolveEventsConfig(env, config);\n if (!eventsConfig.clientId) {\n return Layer.empty;\n }\n return eventsLayer(eventsConfig);\n }),\n ),\n Layer.effectDiscard(\n Effect.gen(function* () {\n const env = yield* WorkerEnvironment;\n yield* Effect.addFinalizer(() => Effect.promise(() => flush(env)));\n }),\n ),\n );\n\n return {\n flush,\n layer,\n requestLayer,\n };\n};\n\nexport const MxlCloudflareSDK = { make };\n"],"mappings":"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"}