better-effect 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +544 -10
  2. package/dist/adapters/iti.d.mts +3 -2
  3. package/dist/adapters/iti.d.mts.map +1 -1
  4. package/dist/adapters/iti.mjs +11 -10
  5. package/dist/adapters/iti.mjs.map +1 -1
  6. package/dist/bun.d.mts +43 -0
  7. package/dist/bun.d.mts.map +1 -0
  8. package/dist/bun.mjs +40 -0
  9. package/dist/bun.mjs.map +1 -0
  10. package/dist/{context-BUEf1qjL.mjs → context-BphfFUMO.mjs} +4 -2
  11. package/dist/context-BphfFUMO.mjs.map +1 -0
  12. package/dist/current-request-CIU7AHhF.d.mts +22 -0
  13. package/dist/current-request-CIU7AHhF.d.mts.map +1 -0
  14. package/dist/current-request-DDKNyymv.mjs +20 -0
  15. package/dist/current-request-DDKNyymv.mjs.map +1 -0
  16. package/dist/disposable-CP-Q2H3l.mjs +57 -0
  17. package/dist/disposable-CP-Q2H3l.mjs.map +1 -0
  18. package/dist/{effect-2ZcGZI8A.mjs → effect-CPdV7Yv8.mjs} +56 -10
  19. package/dist/effect-CPdV7Yv8.mjs.map +1 -0
  20. package/dist/hono.d.mts +7 -5
  21. package/dist/hono.d.mts.map +1 -1
  22. package/dist/hono.mjs +88 -30
  23. package/dist/hono.mjs.map +1 -1
  24. package/dist/{index-heuaRmXR.d.mts → index-AiKM71-u.d.mts} +3 -1
  25. package/dist/{index-heuaRmXR.d.mts.map → index-AiKM71-u.d.mts.map} +1 -1
  26. package/dist/index-BodMy6rO.d.mts +180 -0
  27. package/dist/index-BodMy6rO.d.mts.map +1 -0
  28. package/dist/{index-C7Qild0_.d.mts → index-CCi07kUw.d.mts} +15 -151
  29. package/dist/index-CCi07kUw.d.mts.map +1 -0
  30. package/dist/index-DAKn0qHk.d.mts +19 -0
  31. package/dist/index-DAKn0qHk.d.mts.map +1 -0
  32. package/dist/{index-DStz0JMN.d.mts → index-cXgyMa0v.d.mts} +75 -22
  33. package/dist/index-cXgyMa0v.d.mts.map +1 -0
  34. package/dist/index.d.mts +7 -4
  35. package/dist/index.mjs +8 -6
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/{internal-identity-DmUpBeeL.mjs → internal-identity-CbXoZIfw.mjs} +3 -3
  38. package/dist/internal-identity-CbXoZIfw.mjs.map +1 -0
  39. package/dist/{signal-B97cs85Z.mjs → layer-D16GKxuX.mjs} +31 -112
  40. package/dist/layer-D16GKxuX.mjs.map +1 -0
  41. package/dist/next.d.mts +116 -0
  42. package/dist/next.d.mts.map +1 -0
  43. package/dist/next.mjs +68 -0
  44. package/dist/next.mjs.map +1 -0
  45. package/dist/node-context-Ca18oNSg.mjs +25 -0
  46. package/dist/node-context-Ca18oNSg.mjs.map +1 -0
  47. package/dist/node-context-DczjAcrX.d.mts +12 -0
  48. package/dist/node-context-DczjAcrX.d.mts.map +1 -0
  49. package/dist/node.d.mts +51 -0
  50. package/dist/node.d.mts.map +1 -0
  51. package/dist/node.mjs +233 -0
  52. package/dist/node.mjs.map +1 -0
  53. package/dist/observer-9nhbL7Pr.d.mts +68 -0
  54. package/dist/observer-9nhbL7Pr.d.mts.map +1 -0
  55. package/dist/opentelemetry.d.mts +94 -0
  56. package/dist/opentelemetry.d.mts.map +1 -0
  57. package/dist/opentelemetry.mjs +453 -0
  58. package/dist/opentelemetry.mjs.map +1 -0
  59. package/dist/{index-rQhZk3Nt.d.mts → outcome-DI8LPIQ4.d.mts} +27 -72
  60. package/dist/outcome-DI8LPIQ4.d.mts.map +1 -0
  61. package/dist/{scope-GGnmTQck.mjs → program-metadata-BxjGkT17.mjs} +19 -4
  62. package/dist/program-metadata-BxjGkT17.mjs.map +1 -0
  63. package/dist/registration-drnZk0SY.mjs +182 -0
  64. package/dist/registration-drnZk0SY.mjs.map +1 -0
  65. package/dist/runtime/explicit.d.mts +1 -1
  66. package/dist/runtime/explicit.mjs +1 -1
  67. package/dist/runtime/node.d.mts +3 -12
  68. package/dist/runtime/node.mjs +2 -24
  69. package/dist/{runtime-DnMn0X0X.mjs → runtime-C_55ofnL.mjs} +245 -67
  70. package/dist/runtime-C_55ofnL.mjs.map +1 -0
  71. package/dist/signal-nfgFyNCk.mjs +52 -0
  72. package/dist/signal-nfgFyNCk.mjs.map +1 -0
  73. package/dist/{standard-services-BFBq-4lo.mjs → standard-services-vnGIafkS.mjs} +200 -30
  74. package/dist/standard-services-vnGIafkS.mjs.map +1 -0
  75. package/dist/standard-services.d.mts +4 -3
  76. package/dist/standard-services.mjs +4 -3
  77. package/dist/testing.d.mts +67 -7
  78. package/dist/testing.d.mts.map +1 -1
  79. package/dist/testing.mjs +192 -9
  80. package/dist/testing.mjs.map +1 -1
  81. package/dist/types-oihI-ESv.d.mts +54 -0
  82. package/dist/types-oihI-ESv.d.mts.map +1 -0
  83. package/dist/web-Bq3MfMrj.mjs +147 -0
  84. package/dist/web-Bq3MfMrj.mjs.map +1 -0
  85. package/dist/web.d.mts +47 -0
  86. package/dist/web.d.mts.map +1 -0
  87. package/dist/web.mjs +2 -0
  88. package/package.json +39 -3
  89. package/dist/context-BUEf1qjL.mjs.map +0 -1
  90. package/dist/effect-2ZcGZI8A.mjs.map +0 -1
  91. package/dist/errors-Dnjhzbt0.mjs +0 -99
  92. package/dist/errors-Dnjhzbt0.mjs.map +0 -1
  93. package/dist/index-C7Qild0_.d.mts.map +0 -1
  94. package/dist/index-DStz0JMN.d.mts.map +0 -1
  95. package/dist/index-rQhZk3Nt.d.mts.map +0 -1
  96. package/dist/internal-identity-DmUpBeeL.mjs.map +0 -1
  97. package/dist/runtime/node.d.mts.map +0 -1
  98. package/dist/runtime/node.mjs.map +0 -1
  99. package/dist/runtime-DnMn0X0X.mjs.map +0 -1
  100. package/dist/scope-GGnmTQck.mjs.map +0 -1
  101. package/dist/signal-B97cs85Z.mjs.map +0 -1
  102. package/dist/standard-services-BFBq-4lo.mjs.map +0 -1
@@ -0,0 +1,453 @@
1
+ import { ROOT_CONTEXT, SpanStatusCode, context, trace } from "@opentelemetry/api";
2
+ //#region src/opentelemetry/index.ts
3
+ const DEFAULT_TRACER_NAME = "better-effect";
4
+ const DEFAULT_EXECUTION_SPAN_NAME = "better-effect.execution";
5
+ const DEFAULT_SERVICE_SPAN_NAME = "better-effect.service";
6
+ const DEFAULT_MAX_ATTRIBUTE_LENGTH = 256;
7
+ const DEFAULT_MAX_ATTRIBUTE_COUNT = 32;
8
+ const DEFAULT_MAX_RESOLUTION_PATH_LENGTH = 16;
9
+ const MAX_ATTRIBUTE_LENGTH = 4096;
10
+ const MAX_ATTRIBUTE_COUNT = 128;
11
+ const MAX_RESOLUTION_PATH_LENGTH = 128;
12
+ const MAX_ARRAY_LENGTH = 16;
13
+ const EXECUTION_ID_ATTRIBUTE = "better_effect.execution_id";
14
+ const PROGRAM_NAME_ATTRIBUTE = "better_effect.program_name";
15
+ const OUTCOME_ATTRIBUTE = "better_effect.outcome";
16
+ const SERVICE_TAG_ATTRIBUTE = "better_effect.service_tag";
17
+ const RESOLUTION_PATH_ATTRIBUTE = "better_effect.resolution_path";
18
+ const RESOLUTION_PATH_TRUNCATED_ATTRIBUTE = "better_effect.resolution_path_truncated";
19
+ const LIBRARY_ATTRIBUTE = "better_effect.library";
20
+ const FAILURE_EVENT_NAME = "better-effect.failure";
21
+ const serviceEventStatus = (event) => "error" in event && event.error !== void 0 ? "failure" : event.outcome.status;
22
+ const executionEventStatus = (event) => event.cleanupFailure !== void 0 ? "failure" : event.outcome.status;
23
+ const isStartedExecutionSpan = (execution) => execution.span !== void 0;
24
+ const isActiveExecutionSpan = (execution) => isStartedExecutionSpan(execution) && execution.executionContext !== void 0;
25
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26
+ const isSpan = (value) => isRecord(value) && typeof value.end === "function";
27
+ const isTracer = (value) => isRecord(value) && typeof value.startSpan === "function";
28
+ const boundedString = (value, limit) => typeof value === "string" ? value.slice(0, limit) : void 0;
29
+ const boundedNonEmptyString = (value, limit) => {
30
+ const text = boundedString(value, limit);
31
+ return text === void 0 || text.length === 0 ? void 0 : text;
32
+ };
33
+ const normalizeLimit = (value, fallback, maximum, name) => {
34
+ if (value === void 0) return fallback;
35
+ if (!Number.isSafeInteger(value) || value < 1 || value > maximum) throw new RangeError(`${name} must be an integer between 1 and ${maximum}`);
36
+ return value;
37
+ };
38
+ const normalizeLimits = (options) => ({
39
+ maxAttributeLength: normalizeLimit(options.maxAttributeLength, DEFAULT_MAX_ATTRIBUTE_LENGTH, MAX_ATTRIBUTE_LENGTH, "maxAttributeLength"),
40
+ maxAttributeCount: normalizeLimit(options.maxAttributeCount, DEFAULT_MAX_ATTRIBUTE_COUNT, MAX_ATTRIBUTE_COUNT, "maxAttributeCount"),
41
+ maxResolutionPathLength: normalizeLimit(options.maxResolutionPathLength, DEFAULT_MAX_RESOLUTION_PATH_LENGTH, MAX_RESOLUTION_PATH_LENGTH, "maxResolutionPathLength")
42
+ });
43
+ const normalizeStringArray = (values, limit) => {
44
+ const normalized = [];
45
+ const length = Math.min(values.length, MAX_ARRAY_LENGTH);
46
+ for (let index = 0; index < length; index += 1) {
47
+ const value = values[index];
48
+ if (value === void 0 || value === null) continue;
49
+ if (typeof value !== "string") return;
50
+ normalized.push(value.slice(0, limit));
51
+ }
52
+ return normalized;
53
+ };
54
+ const normalizeNumberArray = (values) => {
55
+ const normalized = [];
56
+ const length = Math.min(values.length, MAX_ARRAY_LENGTH);
57
+ normalized.length = length;
58
+ for (let index = 0; index < length; index += 1) {
59
+ if (!(index in values)) continue;
60
+ const value = values[index];
61
+ if (typeof value !== "number" || !Number.isFinite(value)) return;
62
+ normalized[index] = value;
63
+ }
64
+ return normalized;
65
+ };
66
+ const normalizeBooleanArray = (values) => {
67
+ const normalized = [];
68
+ const length = Math.min(values.length, MAX_ARRAY_LENGTH);
69
+ normalized.length = length;
70
+ for (let index = 0; index < length; index += 1) {
71
+ if (!(index in values)) continue;
72
+ const value = values[index];
73
+ if (typeof value !== "boolean") return;
74
+ normalized[index] = value;
75
+ }
76
+ return normalized;
77
+ };
78
+ const normalizeAttributeValue = (value, maxAttributeLength) => {
79
+ if (typeof value === "string") return value.slice(0, maxAttributeLength);
80
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
81
+ if (typeof value === "boolean") return value;
82
+ if (!Array.isArray(value) || value.length === 0) return;
83
+ return normalizeStringArray(value, maxAttributeLength) ?? normalizeNumberArray(value) ?? normalizeBooleanArray(value);
84
+ };
85
+ const normalizeAttributes = (value, limits) => {
86
+ const result = {};
87
+ let inspected = 0;
88
+ let normalizedCount = 0;
89
+ if (!isRecord(value)) return result;
90
+ for (const key in value) {
91
+ if (inspected >= limits.maxAttributeCount) break;
92
+ inspected += 1;
93
+ if (!Object.prototype.propertyIsEnumerable.call(value, key)) continue;
94
+ if (key.length === 0 || key.length > limits.maxAttributeLength) continue;
95
+ let normalized;
96
+ try {
97
+ normalized = normalizeAttributeValue(value[key], limits.maxAttributeLength);
98
+ } catch {
99
+ continue;
100
+ }
101
+ if (normalized !== void 0) {
102
+ result[key] = normalized;
103
+ normalizedCount += 1;
104
+ if (normalizedCount >= limits.maxAttributeCount) break;
105
+ }
106
+ }
107
+ return result;
108
+ };
109
+ const selectAllowlistedAttributes = (source, allowlist, limits) => {
110
+ if (source === void 0 || allowlist === void 0) return {};
111
+ const selected = {};
112
+ const length = Math.min(allowlist.length, limits.maxAttributeCount);
113
+ let selectedCount = 0;
114
+ for (let index = 0; index < length; index += 1) {
115
+ const key = allowlist[index];
116
+ if (typeof key !== "string" || key.length === 0 || key.length > limits.maxAttributeLength) continue;
117
+ try {
118
+ if (!(key in selected)) selectedCount += 1;
119
+ selected[key] = source[key];
120
+ } catch {}
121
+ if (selectedCount >= limits.maxAttributeCount) break;
122
+ }
123
+ return normalizeAttributes(selected, limits);
124
+ };
125
+ const makeExecutionAttributes = (event, options, limits) => {
126
+ const result = selectAllowlistedAttributes(event.attributes, options.executionAttributeAllowlist, limits);
127
+ if (event.attributes !== void 0 && options.sanitizeExecutionAttributes !== void 0) try {
128
+ const sanitized = options.sanitizeExecutionAttributes(event.attributes);
129
+ ignoreRejection(sanitized);
130
+ Object.assign(result, normalizeAttributes(sanitized, limits));
131
+ } catch {}
132
+ return result;
133
+ };
134
+ const makeFailureDetails = (cause, sanitizer, limits) => {
135
+ if (sanitizer === void 0) return;
136
+ let sanitized;
137
+ try {
138
+ sanitized = sanitizer(cause);
139
+ ignoreRejection(sanitized);
140
+ } catch {
141
+ return;
142
+ }
143
+ if (typeof sanitized === "string") return {
144
+ message: sanitized.slice(0, limits.maxAttributeLength),
145
+ attributes: {}
146
+ };
147
+ if (!isRecord(sanitized)) return;
148
+ let message;
149
+ let attributes;
150
+ try {
151
+ message = boundedString(sanitized.message, limits.maxAttributeLength);
152
+ attributes = sanitized.attributes;
153
+ } catch {
154
+ return;
155
+ }
156
+ let normalizedAttributes;
157
+ try {
158
+ normalizedAttributes = normalizeAttributes(attributes, limits);
159
+ } catch {
160
+ return;
161
+ }
162
+ const details = { attributes: normalizedAttributes };
163
+ if (message !== void 0) details.message = message;
164
+ return details;
165
+ };
166
+ const mergeAdditionalAttributes = (target, additional, limit) => {
167
+ for (const [key, value] of Object.entries(additional)) {
168
+ if (Object.keys(target).length >= limit) break;
169
+ if (!(key in target)) target[key] = value;
170
+ }
171
+ };
172
+ const ignoreRejection = (value) => {
173
+ try {
174
+ Promise.resolve(value).catch(() => {});
175
+ } catch {}
176
+ };
177
+ const callSpan = (operation) => {
178
+ try {
179
+ ignoreRejection(operation());
180
+ } catch {}
181
+ };
182
+ const addFailureDetails = (span, details) => {
183
+ const message = details.message;
184
+ if (message !== void 0) callSpan(() => span.recordException(message));
185
+ if (Object.keys(details.attributes).length > 0) callSpan(() => span.addEvent(FAILURE_EVENT_NAME, details.attributes));
186
+ };
187
+ const addAttribute = (target, key, value, limit) => {
188
+ if (key in target || Object.keys(target).length < limit) target[key] = value;
189
+ };
190
+ const makeExecutionSpanAttributes = (event, options, limits) => {
191
+ const attributes = {};
192
+ const executionId = boundedNonEmptyString(event.executionId, limits.maxAttributeLength);
193
+ const name = boundedNonEmptyString(event.name, limits.maxAttributeLength);
194
+ if (executionId !== void 0) addAttribute(attributes, EXECUTION_ID_ATTRIBUTE, executionId, limits.maxAttributeCount);
195
+ if (name !== void 0) addAttribute(attributes, PROGRAM_NAME_ATTRIBUTE, name, limits.maxAttributeCount);
196
+ addAttribute(attributes, LIBRARY_ATTRIBUTE, "better-effect", limits.maxAttributeCount);
197
+ mergeAdditionalAttributes(attributes, makeExecutionAttributes(event, options, limits), limits.maxAttributeCount);
198
+ return attributes;
199
+ };
200
+ const makeResolutionPathAttributes = (path, limits) => {
201
+ const tags = [];
202
+ for (const service of path.slice(0, limits.maxResolutionPathLength)) {
203
+ const tag = boundedNonEmptyString(service.serviceTag, limits.maxAttributeLength);
204
+ if (tag !== void 0) tags.push(tag);
205
+ }
206
+ const result = {};
207
+ if (tags.length > 0) result[RESOLUTION_PATH_ATTRIBUTE] = tags;
208
+ if (path.length > limits.maxResolutionPathLength) result[RESOLUTION_PATH_TRUNCATED_ATTRIBUTE] = true;
209
+ return result;
210
+ };
211
+ const makeServiceEventAttributes = (event, limits, executionId) => {
212
+ const serviceTag = boundedNonEmptyString(event.service.serviceTag, limits.maxAttributeLength);
213
+ const attributes = {};
214
+ if (serviceTag !== void 0) addAttribute(attributes, SERVICE_TAG_ATTRIBUTE, serviceTag, limits.maxAttributeCount);
215
+ addAttribute(attributes, OUTCOME_ATTRIBUTE, serviceEventStatus(event), limits.maxAttributeCount);
216
+ if ("resolutionPath" in event) mergeAdditionalAttributes(attributes, makeResolutionPathAttributes(event.resolutionPath, limits), limits.maxAttributeCount);
217
+ if (executionId !== void 0) addAttribute(attributes, EXECUTION_ID_ATTRIBUTE, executionId, limits.maxAttributeCount);
218
+ return attributes;
219
+ };
220
+ const readActiveContext = () => {
221
+ try {
222
+ return context.active();
223
+ } catch {
224
+ return;
225
+ }
226
+ };
227
+ const setSpanInContext = (parent, span) => {
228
+ try {
229
+ return trace.setSpan(parent, span);
230
+ } catch {
231
+ return;
232
+ }
233
+ };
234
+ const resolveTracer = (options) => {
235
+ const hasTracer = options.tracer !== void 0;
236
+ const hasProvider = options.provider !== void 0;
237
+ if (hasTracer === hasProvider) throw new TypeError("OpenTelemetryRuntimeObserver requires exactly one tracer or provider");
238
+ if (hasTracer) {
239
+ if (!isTracer(options.tracer)) throw new TypeError("OpenTelemetryRuntimeObserver requires a tracer with startSpan");
240
+ return options.tracer;
241
+ }
242
+ if (hasProvider) {
243
+ const tracerName = options.tracerName ?? DEFAULT_TRACER_NAME;
244
+ const tracer = options.provider.getTracer(tracerName, options.tracerVersion);
245
+ if (!isTracer(tracer)) throw new TypeError("OpenTelemetry provider did not return a tracer with startSpan");
246
+ return tracer;
247
+ }
248
+ throw new TypeError("OpenTelemetryRuntimeObserver requires an existing tracer or provider");
249
+ };
250
+ /**
251
+ * Best-effort RuntimeObserver adapter for an existing OpenTelemetry tracer.
252
+ *
253
+ * The adapter never installs a global provider, SDK, context manager, or
254
+ * exporter. All observer hooks are synchronous and isolate tracer failures.
255
+ */
256
+ var OpenTelemetryRuntimeObserver = class OpenTelemetryRuntimeObserver {
257
+ tracer;
258
+ options;
259
+ limits;
260
+ executionSpans = /* @__PURE__ */ new Map();
261
+ serviceResolution;
262
+ recordFailures;
263
+ disposed = false;
264
+ constructor(options) {
265
+ this.tracer = resolveTracer(options);
266
+ this.options = options;
267
+ this.limits = normalizeLimits(options);
268
+ this.serviceResolution = options.serviceResolution ?? "off";
269
+ this.recordFailures = options.recordFailures === true;
270
+ if (![
271
+ "off",
272
+ "events",
273
+ "spans"
274
+ ].includes(this.serviceResolution)) throw new TypeError("serviceResolution must be off, events, or spans");
275
+ }
276
+ static make(options) {
277
+ return new OpenTelemetryRuntimeObserver(options);
278
+ }
279
+ onExecutionStart = (event) => {
280
+ this.safely(() => this.startExecution(event));
281
+ };
282
+ onExecutionEnd = (event) => {
283
+ this.safely(() => this.endExecution(event));
284
+ };
285
+ onServiceResolve = (event) => {
286
+ this.safely(() => this.observeService("better-effect.service.resolve", event));
287
+ };
288
+ onServiceAcquire = (event) => {
289
+ this.safely(() => this.observeService("better-effect.service.acquire", event));
290
+ };
291
+ onResourceRelease = (event) => {
292
+ this.safely(() => this.observeService("better-effect.service.release", event));
293
+ };
294
+ /** End any spans whose malformed or missing end event left them pending. */
295
+ dispose() {
296
+ if (this.disposed) return;
297
+ this.disposed = true;
298
+ const pending = [...this.executionSpans.values()];
299
+ for (const execution of pending) {
300
+ const span = execution.span;
301
+ if (span === void 0) continue;
302
+ this.endExecutionSpan(execution, span);
303
+ }
304
+ }
305
+ [Symbol.dispose]() {
306
+ this.dispose();
307
+ }
308
+ async [Symbol.asyncDispose]() {
309
+ this.dispose();
310
+ }
311
+ safely(operation) {
312
+ if (this.disposed) return;
313
+ try {
314
+ operation();
315
+ } catch {}
316
+ }
317
+ startSpan(name, attributes, parentContext) {
318
+ try {
319
+ const span = parentContext === void 0 ? this.tracer.startSpan(name, { attributes }) : this.tracer.startSpan(name, { attributes }, parentContext);
320
+ if (!isSpan(span)) {
321
+ ignoreRejection(span);
322
+ return;
323
+ }
324
+ return span;
325
+ } catch {
326
+ return;
327
+ }
328
+ }
329
+ ownsExecutionSpan(execution, span) {
330
+ return this.executionSpans.get(execution.executionId) === execution && execution.span === span;
331
+ }
332
+ claimExecutionSpan(execution, span) {
333
+ if (this.executionSpans.get(execution.executionId) !== execution || execution.span !== span) return false;
334
+ this.executionSpans.delete(execution.executionId);
335
+ return true;
336
+ }
337
+ endExecutionSpan(execution, span) {
338
+ if (this.claimExecutionSpan(execution, span)) callSpan(() => span.end());
339
+ }
340
+ finishPendingExecution(execution) {
341
+ if (execution.pendingEnd !== void 0 && isActiveExecutionSpan(execution)) this.finishExecution(execution, execution.pendingEnd);
342
+ }
343
+ startExecution(event) {
344
+ if (this.executionSpans.has(event.executionId)) return;
345
+ const execution = {
346
+ executionId: event.executionId,
347
+ ended: false
348
+ };
349
+ this.executionSpans.set(event.executionId, execution);
350
+ const parentContext = readActiveContext() ?? ROOT_CONTEXT;
351
+ let span;
352
+ try {
353
+ span = this.startSpan(boundedNonEmptyString(event.name, this.limits.maxAttributeLength) ?? DEFAULT_EXECUTION_SPAN_NAME, makeExecutionSpanAttributes(event, this.options, this.limits), parentContext);
354
+ } catch {
355
+ this.claimExecutionSpan(execution, void 0);
356
+ return;
357
+ }
358
+ if (span === void 0) {
359
+ this.claimExecutionSpan(execution, void 0);
360
+ return;
361
+ }
362
+ execution.span = span;
363
+ if (this.disposed) {
364
+ this.endExecutionSpan(execution, span);
365
+ return;
366
+ }
367
+ if (!this.ownsExecutionSpan(execution, span)) return;
368
+ const executionContext = setSpanInContext(parentContext, span);
369
+ if (executionContext === void 0) {
370
+ const pendingEnd = execution.pendingEnd;
371
+ if (pendingEnd !== void 0 && isStartedExecutionSpan(execution)) this.finishExecution(execution, pendingEnd);
372
+ else this.endExecutionSpan(execution, span);
373
+ return;
374
+ }
375
+ execution.executionContext = executionContext;
376
+ this.finishPendingExecution(execution);
377
+ }
378
+ endExecution(event) {
379
+ const execution = this.executionSpans.get(event.executionId);
380
+ if (execution === void 0 || execution.ended) return;
381
+ execution.ended = true;
382
+ if (!isActiveExecutionSpan(execution)) {
383
+ execution.pendingEnd = event;
384
+ return;
385
+ }
386
+ this.finishExecution(execution, event);
387
+ }
388
+ finishExecution(execution, event) {
389
+ const span = execution.span;
390
+ if (!this.claimExecutionSpan(execution, span)) return;
391
+ try {
392
+ const status = executionEventStatus(event);
393
+ callSpan(() => span.setAttribute(OUTCOME_ATTRIBUTE, status));
394
+ callSpan(() => span.setStatus({ code: status === "failure" ? SpanStatusCode.ERROR : SpanStatusCode.OK }));
395
+ if (status === "failure" && this.recordFailures) {
396
+ const cause = event.outcome.status === "failure" ? event.outcome.cause : event.cleanupFailure;
397
+ const details = makeFailureDetails(cause, this.options.sanitizeFailure, this.limits);
398
+ if (details !== void 0) addFailureDetails(span, details);
399
+ }
400
+ } finally {
401
+ callSpan(() => span.end());
402
+ }
403
+ }
404
+ findCorrelatedExecution(event) {
405
+ if (event.executionId === void 0) return;
406
+ const execution = this.executionSpans.get(event.executionId);
407
+ return execution !== void 0 && isActiveExecutionSpan(execution) ? execution : void 0;
408
+ }
409
+ observeService(name, event) {
410
+ if (this.serviceResolution === "off") return;
411
+ const execution = this.findCorrelatedExecution(event);
412
+ const executionId = execution === void 0 ? void 0 : this.findExecutionId(execution);
413
+ const attributes = makeServiceEventAttributes(event, this.limits, executionId);
414
+ let failureCause;
415
+ if ("error" in event && event.error !== void 0) failureCause = event.error;
416
+ else if (event.outcome.status === "failure") failureCause = event.outcome.cause;
417
+ const details = serviceEventStatus(event) === "failure" && this.recordFailures ? makeFailureDetails(failureCause, this.options.sanitizeFailure, this.limits) : void 0;
418
+ if (details?.attributes !== void 0) mergeAdditionalAttributes(attributes, details.attributes, this.limits.maxAttributeCount);
419
+ if (execution !== void 0) {
420
+ this.observeCorrelatedService(name, event, execution, attributes, details);
421
+ return;
422
+ }
423
+ this.observeStandaloneService(name, event, attributes, details);
424
+ }
425
+ findExecutionId(execution) {
426
+ return boundedNonEmptyString(execution.executionId, this.limits.maxAttributeLength);
427
+ }
428
+ observeCorrelatedService(name, event, execution, attributes, details) {
429
+ if (this.serviceResolution === "events") {
430
+ callSpan(() => execution.span.addEvent(name, attributes));
431
+ if (details !== void 0) addFailureDetails(execution.span, details);
432
+ return;
433
+ }
434
+ const span = this.startSpan(name, attributes, execution.executionContext);
435
+ if (span === void 0) return;
436
+ this.finishServiceSpan(span, event, details);
437
+ }
438
+ observeStandaloneService(name, event, attributes, details) {
439
+ const span = this.startSpan(this.serviceResolution === "events" ? DEFAULT_SERVICE_SPAN_NAME : name, attributes, ROOT_CONTEXT);
440
+ if (span === void 0) return;
441
+ if (this.serviceResolution === "events") callSpan(() => span.addEvent(name, attributes));
442
+ this.finishServiceSpan(span, event, details);
443
+ }
444
+ finishServiceSpan(span, event, details) {
445
+ callSpan(() => span.setStatus({ code: serviceEventStatus(event) === "failure" ? SpanStatusCode.ERROR : SpanStatusCode.OK }));
446
+ if (details !== void 0) addFailureDetails(span, details);
447
+ callSpan(() => span.end());
448
+ }
449
+ };
450
+ //#endregion
451
+ export { OpenTelemetryRuntimeObserver };
452
+
453
+ //# sourceMappingURL=opentelemetry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opentelemetry.mjs","names":[],"sources":["../src/opentelemetry/index.ts"],"sourcesContent":["import {\n context,\n ROOT_CONTEXT,\n SpanStatusCode,\n trace,\n type AttributeValue,\n type Context,\n type Span,\n type Tracer,\n type TracerProvider\n} from '@opentelemetry/api'\n\nimport type {\n RuntimeExecutionAttributes,\n RuntimeExecutionEndEvent,\n RuntimeExecutionStartEvent,\n RuntimeObserver,\n RuntimeResourceReleaseEvent,\n RuntimeServiceAcquireEvent,\n RuntimeServiceResolveEvent\n} from '../runtime/observer'\n\n/** Controls how Service lifecycle events are represented in telemetry. */\nexport type ServiceTelemetryMode = 'off' | 'events' | 'spans'\n\n/** Attribute values accepted by the OpenTelemetry adapter. */\nexport type OpenTelemetryAttributes = Readonly<Record<string, AttributeValue>>\n\n/** A deliberately small, already-sanitized failure representation. */\nexport type OpenTelemetryFailure =\n | string\n | {\n readonly message?: string\n readonly attributes?: OpenTelemetryAttributes\n }\n\n/** Select safe execution attributes explicitly before they reach a span. */\nexport type OpenTelemetryExecutionAttributesSanitizer = (\n attributes: RuntimeExecutionAttributes\n) => OpenTelemetryAttributes | undefined\n\n/** Select safe failure details explicitly before they reach a span. */\nexport type OpenTelemetryFailureSanitizer = (cause: unknown) => OpenTelemetryFailure | undefined\n\ntype OpenTelemetryRuntimeObserverBaseOptions = {\n /** Use an existing tracer. The adapter never registers a global provider. */\n readonly tracer?: Tracer\n /** Resolve a tracer from an existing provider without registering it globally. */\n readonly provider?: TracerProvider\n /** Tracer name used only when `provider` is supplied. */\n readonly tracerName?: string\n /** Optional instrumentation version used only when `provider` is supplied. */\n readonly tracerVersion?: string\n /** Service resolution policy. Defaults to `off`. */\n readonly serviceResolution?: ServiceTelemetryMode\n /** Record explicitly sanitized failure details in addition to setting status. */\n readonly recordFailures?: boolean\n /** Explicitly sanitize arbitrary execution attributes. */\n readonly sanitizeExecutionAttributes?: OpenTelemetryExecutionAttributesSanitizer\n /** Copy only these execution attribute keys, dropping unsupported values. */\n readonly executionAttributeAllowlist?: readonly string[]\n /** Explicitly sanitize a failure before recording it. */\n readonly sanitizeFailure?: OpenTelemetryFailureSanitizer\n /** Maximum length for attribute keys and string values. Defaults to 256. */\n readonly maxAttributeLength?: number\n /** Maximum number of copied execution or failure attributes. Defaults to 32. */\n readonly maxAttributeCount?: number\n /** Maximum number of Service tags retained in a resolution path. Defaults to 16. */\n readonly maxResolutionPathLength?: number\n}\n\n/** Configuration for an observer backed by an existing OpenTelemetry tracer/provider. */\nexport type OpenTelemetryRuntimeObserverOptions =\n | (OpenTelemetryRuntimeObserverBaseOptions & {\n readonly tracer: Tracer\n readonly provider?: never\n })\n | (OpenTelemetryRuntimeObserverBaseOptions & {\n readonly provider: TracerProvider\n readonly tracer?: never\n })\n\nconst DEFAULT_TRACER_NAME = 'better-effect'\nconst DEFAULT_EXECUTION_SPAN_NAME = 'better-effect.execution'\nconst DEFAULT_SERVICE_SPAN_NAME = 'better-effect.service'\nconst DEFAULT_MAX_ATTRIBUTE_LENGTH = 256\nconst DEFAULT_MAX_ATTRIBUTE_COUNT = 32\nconst DEFAULT_MAX_RESOLUTION_PATH_LENGTH = 16\nconst MAX_ATTRIBUTE_LENGTH = 4096\nconst MAX_ATTRIBUTE_COUNT = 128\nconst MAX_RESOLUTION_PATH_LENGTH = 128\nconst MAX_ARRAY_LENGTH = 16\n\nconst EXECUTION_ID_ATTRIBUTE = 'better_effect.execution_id'\nconst PROGRAM_NAME_ATTRIBUTE = 'better_effect.program_name'\nconst OUTCOME_ATTRIBUTE = 'better_effect.outcome'\nconst SERVICE_TAG_ATTRIBUTE = 'better_effect.service_tag'\nconst RESOLUTION_PATH_ATTRIBUTE = 'better_effect.resolution_path'\nconst RESOLUTION_PATH_TRUNCATED_ATTRIBUTE = 'better_effect.resolution_path_truncated'\nconst LIBRARY_ATTRIBUTE = 'better_effect.library'\nconst FAILURE_EVENT_NAME = 'better-effect.failure'\n\ntype AttributeMap = Record<string, AttributeValue>\n\ntype Limits = {\n readonly maxAttributeLength: number\n readonly maxAttributeCount: number\n readonly maxResolutionPathLength: number\n}\n\ntype FailureDetails = {\n message?: string\n readonly attributes: AttributeMap\n}\n\ntype ExecutionSpan = {\n readonly executionId: string\n span?: Span\n executionContext?: Context\n pendingEnd?: RuntimeExecutionEndEvent\n ended: boolean\n}\n\ntype StartedExecutionSpan = ExecutionSpan & {\n readonly span: Span\n}\n\ntype ActiveExecutionSpan = StartedExecutionSpan & {\n readonly executionContext: Context\n}\n\ntype ServiceEvent =\n | RuntimeServiceResolveEvent\n | RuntimeServiceAcquireEvent\n | RuntimeResourceReleaseEvent\n\ntype ServiceEventStatus = 'success' | 'failure'\n\ntype ExecutionEventStatus = 'success' | 'failure'\n\nconst serviceEventStatus = (event: ServiceEvent): ServiceEventStatus =>\n 'error' in event && event.error !== undefined ? 'failure' : event.outcome.status\n\nconst executionEventStatus = (event: RuntimeExecutionEndEvent): ExecutionEventStatus =>\n event.cleanupFailure !== undefined ? 'failure' : event.outcome.status\n\nconst isStartedExecutionSpan = (execution: ExecutionSpan): execution is StartedExecutionSpan =>\n execution.span !== undefined\n\nconst isActiveExecutionSpan = (execution: ExecutionSpan): execution is ActiveExecutionSpan =>\n isStartedExecutionSpan(execution) && execution.executionContext !== undefined\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters, anti-slop/no-unsafe-dictionary-type -- This private guard validates untrusted values before scalar validation.\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Validate the external value before reading it.\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- OTel is an optional runtime peer and must be checked defensively.\nconst isSpan = (value: unknown): value is Span =>\n isRecord(value) &&\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- OTel is an optional runtime peer and must be checked defensively.\n typeof value.end === 'function'\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- OTel is an optional runtime peer and must be checked defensively.\nconst isTracer = (value: unknown): value is Tracer =>\n isRecord(value) &&\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- OTel is an optional runtime peer and must be checked defensively.\n typeof value.startSpan === 'function'\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Sanitizers and observer events are external diagnostic boundaries.\nconst boundedString = (value: unknown, limit: number): string | undefined =>\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Only primitive strings are copied into OTel attributes.\n typeof value === 'string' ? value.slice(0, limit) : undefined\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Event names and Service tags are validated at this boundary.\nconst boundedNonEmptyString = (value: unknown, limit: number): string | undefined => {\n const text = boundedString(value, limit)\n return text === undefined || text.length === 0 ? undefined : text\n}\n\nconst normalizeLimit = (\n value: number | undefined,\n fallback: number,\n maximum: number,\n name: string\n): number => {\n if (value === undefined) {\n return fallback\n }\n\n if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {\n throw new RangeError(`${name} must be an integer between 1 and ${maximum}`)\n }\n\n return value\n}\n\nconst normalizeLimits = (options: OpenTelemetryRuntimeObserverBaseOptions): Limits => ({\n maxAttributeLength: normalizeLimit(\n options.maxAttributeLength,\n DEFAULT_MAX_ATTRIBUTE_LENGTH,\n MAX_ATTRIBUTE_LENGTH,\n 'maxAttributeLength'\n ),\n maxAttributeCount: normalizeLimit(\n options.maxAttributeCount,\n DEFAULT_MAX_ATTRIBUTE_COUNT,\n MAX_ATTRIBUTE_COUNT,\n 'maxAttributeCount'\n ),\n maxResolutionPathLength: normalizeLimit(\n options.maxResolutionPathLength,\n DEFAULT_MAX_RESOLUTION_PATH_LENGTH,\n MAX_RESOLUTION_PATH_LENGTH,\n 'maxResolutionPathLength'\n )\n})\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Normalize callback output before it reaches the OTel API.\nconst normalizeStringArray = (values: readonly unknown[], limit: number): string[] | undefined => {\n const normalized: string[] = []\n const length = Math.min(values.length, MAX_ARRAY_LENGTH)\n\n for (let index = 0; index < length; index += 1) {\n const value = values[index]\n\n if (value === undefined || value === null) {\n continue\n }\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Accept only OTel string-array values.\n if (typeof value !== 'string') {\n return undefined\n }\n\n normalized.push(value.slice(0, limit))\n }\n\n return normalized\n}\n\nconst normalizeNumberArray = (values: readonly unknown[]): number[] | undefined => {\n const normalized: number[] = []\n const length = Math.min(values.length, MAX_ARRAY_LENGTH)\n normalized.length = length\n\n for (let index = 0; index < length; index += 1) {\n if (!(index in values)) {\n continue\n }\n\n const value = values[index]\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Accept only finite OTel number-array values.\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return undefined\n }\n\n normalized[index] = value\n }\n\n return normalized\n}\n\nconst normalizeBooleanArray = (values: readonly unknown[]): boolean[] | undefined => {\n const normalized: boolean[] = []\n const length = Math.min(values.length, MAX_ARRAY_LENGTH)\n normalized.length = length\n\n for (let index = 0; index < length; index += 1) {\n if (!(index in values)) {\n continue\n }\n\n const value = values[index]\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Accept only OTel boolean-array values.\n if (typeof value !== 'boolean') {\n return undefined\n }\n\n normalized[index] = value\n }\n\n return normalized\n}\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Normalize explicit sanitizer and allowlist output.\nconst normalizeAttributeValue = (\n // oxlint-disable-next-line anti-slop/no-unknown-parameters -- Normalize explicit sanitizer and allowlist output.\n value: unknown,\n maxAttributeLength: number\n): AttributeValue | undefined => {\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Copy only OTel scalar strings.\n if (typeof value === 'string') {\n return value.slice(0, maxAttributeLength)\n }\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Copy only finite OTel scalar numbers.\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value : undefined\n }\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Copy only OTel scalar booleans.\n if (typeof value === 'boolean') {\n return value\n }\n\n if (!Array.isArray(value) || value.length === 0) {\n return undefined\n }\n\n return (\n normalizeStringArray(value, maxAttributeLength) ??\n normalizeNumberArray(value) ??\n normalizeBooleanArray(value)\n )\n}\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters, anti-slop/no-known-value-widening -- Validate sanitizer output into the private OTel attribute owner.\nconst normalizeAttributes = (value: unknown, limits: Limits): AttributeMap => {\n const result: AttributeMap = {}\n let inspected = 0\n let normalizedCount = 0\n\n if (!isRecord(value)) {\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- Return the private validated OTel attribute owner.\n return result\n }\n\n for (const key in value) {\n if (inspected >= limits.maxAttributeCount) {\n break\n }\n\n inspected += 1\n\n if (!Object.prototype.propertyIsEnumerable.call(value, key)) {\n continue\n }\n\n if (key.length === 0 || key.length > limits.maxAttributeLength) {\n continue\n }\n\n let normalized: AttributeValue | undefined\n\n try {\n normalized = normalizeAttributeValue(value[key], limits.maxAttributeLength)\n } catch {\n continue\n }\n\n if (normalized !== undefined) {\n result[key] = normalized\n normalizedCount += 1\n\n if (normalizedCount >= limits.maxAttributeCount) {\n break\n }\n }\n }\n\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- Return the private validated OTel attribute owner.\n return result\n}\n\nconst selectAllowlistedAttributes = (\n source: RuntimeExecutionAttributes | undefined,\n allowlist: readonly string[] | undefined,\n limits: Limits\n): AttributeMap => {\n if (source === undefined || allowlist === undefined) {\n return {}\n }\n\n // oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- Values are parsed immediately by normalizeAttributes.\n const selected: Record<string, unknown> = {}\n const length = Math.min(allowlist.length, limits.maxAttributeCount)\n let selectedCount = 0\n\n for (let index = 0; index < length; index += 1) {\n const key = allowlist[index]\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Reject malformed JavaScript allowlist entries before reading source attributes.\n if (typeof key !== 'string' || key.length === 0 || key.length > limits.maxAttributeLength) {\n continue\n }\n\n try {\n if (!(key in selected)) {\n selectedCount += 1\n }\n\n selected[key] = source[key]\n } catch {\n // A hostile getter is an unusable optional attribute, not a Runtime failure.\n }\n\n if (selectedCount >= limits.maxAttributeCount) {\n break\n }\n }\n\n return normalizeAttributes(selected, limits)\n}\n\nconst makeExecutionAttributes = (\n event: RuntimeExecutionStartEvent,\n options: OpenTelemetryRuntimeObserverBaseOptions,\n limits: Limits\n): AttributeMap => {\n const result = selectAllowlistedAttributes(\n event.attributes,\n options.executionAttributeAllowlist,\n limits\n )\n\n if (event.attributes !== undefined && options.sanitizeExecutionAttributes !== undefined) {\n try {\n const sanitized = options.sanitizeExecutionAttributes(event.attributes)\n ignoreRejection(sanitized)\n Object.assign(result, normalizeAttributes(sanitized, limits))\n } catch {\n // Sanitizers are optional diagnostics and cannot affect execution telemetry.\n }\n }\n\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- Return the private validated OTel attribute owner.\n return result\n}\n\nconst makeFailureDetails = (\n cause: unknown,\n sanitizer: OpenTelemetryFailureSanitizer | undefined,\n limits: Limits\n): FailureDetails | undefined => {\n if (sanitizer === undefined) {\n return undefined\n }\n\n let sanitized: OpenTelemetryFailure | undefined\n\n try {\n sanitized = sanitizer(cause)\n ignoreRejection(sanitized)\n } catch {\n return undefined\n }\n\n // oxlint-disable-next-line anti-slop/no-runtime-typeof -- A sanitizer may explicitly return a safe string message.\n if (typeof sanitized === 'string') {\n return {\n message: sanitized.slice(0, limits.maxAttributeLength),\n attributes: {}\n }\n }\n\n if (!isRecord(sanitized)) {\n return undefined\n }\n\n let message: string | undefined\n let attributes: unknown\n\n try {\n message = boundedString(sanitized.message, limits.maxAttributeLength)\n attributes = sanitized.attributes\n } catch {\n return undefined\n }\n\n let normalizedAttributes: AttributeMap\n\n try {\n normalizedAttributes = normalizeAttributes(attributes, limits)\n } catch {\n return undefined\n }\n\n const details: FailureDetails = {\n attributes: normalizedAttributes\n }\n\n if (message !== undefined) {\n details.message = message\n }\n\n return details\n}\n\nconst mergeAdditionalAttributes = (\n target: AttributeMap,\n additional: AttributeMap,\n limit: number\n): void => {\n for (const [key, value] of Object.entries(additional)) {\n if (Object.keys(target).length >= limit) {\n break\n }\n\n if (!(key in target)) {\n target[key] = value\n }\n }\n}\n\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Suppress rejected thenables from optional tracer and sanitizer boundaries.\nconst ignoreRejection = (value: unknown): void => {\n try {\n void Promise.resolve(value).catch(() => {})\n } catch {\n // A malformed thenable is still outside Runtime control flow.\n }\n}\n\n// oxlint-disable-next-line anti-slop/no-unknown-returns -- Optional OTel methods are typed void but hostile implementations may return thenables.\nconst callSpan = (operation: () => unknown): void => {\n try {\n ignoreRejection(operation())\n } catch {\n // A tracer is an optional boundary and must never affect Runtime control flow.\n }\n}\n\nconst addFailureDetails = (span: Span, details: FailureDetails): void => {\n const message = details.message\n\n if (message !== undefined) {\n callSpan(() => span.recordException(message))\n }\n\n if (Object.keys(details.attributes).length > 0) {\n callSpan(() => span.addEvent(FAILURE_EVENT_NAME, details.attributes))\n }\n}\n\nconst addAttribute = (\n target: AttributeMap,\n key: string,\n value: AttributeValue,\n limit: number\n): void => {\n if (key in target || Object.keys(target).length < limit) {\n target[key] = value\n }\n}\n\nconst makeExecutionSpanAttributes = (\n event: RuntimeExecutionStartEvent,\n options: OpenTelemetryRuntimeObserverBaseOptions,\n limits: Limits\n) => {\n const attributes: AttributeMap = {}\n const executionId = boundedNonEmptyString(event.executionId, limits.maxAttributeLength)\n const name = boundedNonEmptyString(event.name, limits.maxAttributeLength)\n\n if (executionId !== undefined) {\n addAttribute(attributes, EXECUTION_ID_ATTRIBUTE, executionId, limits.maxAttributeCount)\n }\n\n if (name !== undefined) {\n addAttribute(attributes, PROGRAM_NAME_ATTRIBUTE, name, limits.maxAttributeCount)\n }\n\n addAttribute(attributes, LIBRARY_ATTRIBUTE, 'better-effect', limits.maxAttributeCount)\n mergeAdditionalAttributes(\n attributes,\n makeExecutionAttributes(event, options, limits),\n limits.maxAttributeCount\n )\n\n return attributes\n}\n\nconst makeResolutionPathAttributes = (\n path: readonly { readonly serviceTag: string }[],\n limits: Limits\n): AttributeMap => {\n const tags: string[] = []\n\n for (const service of path.slice(0, limits.maxResolutionPathLength)) {\n const tag = boundedNonEmptyString(service.serviceTag, limits.maxAttributeLength)\n\n if (tag !== undefined) {\n tags.push(tag)\n }\n }\n\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- This map owns validated Service path attributes.\n const result: AttributeMap = {}\n\n if (tags.length > 0) {\n result[RESOLUTION_PATH_ATTRIBUTE] = tags\n }\n\n if (path.length > limits.maxResolutionPathLength) {\n result[RESOLUTION_PATH_TRUNCATED_ATTRIBUTE] = true\n }\n\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- Return the private validated Service path attribute owner.\n return result\n}\n\nconst makeServiceEventAttributes = (\n event: ServiceEvent,\n limits: Limits,\n executionId: string | undefined\n): AttributeMap => {\n const serviceTag = boundedNonEmptyString(event.service.serviceTag, limits.maxAttributeLength)\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- This map owns validated Service event attributes.\n const attributes: AttributeMap = {}\n\n if (serviceTag !== undefined) {\n addAttribute(attributes, SERVICE_TAG_ATTRIBUTE, serviceTag, limits.maxAttributeCount)\n }\n\n addAttribute(attributes, OUTCOME_ATTRIBUTE, serviceEventStatus(event), limits.maxAttributeCount)\n\n if ('resolutionPath' in event) {\n mergeAdditionalAttributes(\n attributes,\n makeResolutionPathAttributes(event.resolutionPath, limits),\n limits.maxAttributeCount\n )\n }\n\n if (executionId !== undefined) {\n addAttribute(attributes, EXECUTION_ID_ATTRIBUTE, executionId, limits.maxAttributeCount)\n }\n\n // oxlint-disable-next-line anti-slop/no-known-value-widening -- Return the private validated Service event attribute owner.\n return attributes\n}\n\nconst readActiveContext = (): Context | undefined => {\n try {\n return context.active()\n } catch {\n return undefined\n }\n}\n\nconst setSpanInContext = (parent: Context, span: Span): Context | undefined => {\n try {\n return trace.setSpan(parent, span)\n } catch {\n return undefined\n }\n}\n\nconst resolveTracer = (options: OpenTelemetryRuntimeObserverOptions): Tracer => {\n const hasTracer = options.tracer !== undefined\n const hasProvider = options.provider !== undefined\n\n if (hasTracer === hasProvider) {\n throw new TypeError('OpenTelemetryRuntimeObserver requires exactly one tracer or provider')\n }\n\n if (hasTracer) {\n if (!isTracer(options.tracer)) {\n throw new TypeError('OpenTelemetryRuntimeObserver requires a tracer with startSpan')\n }\n\n return options.tracer\n }\n\n if (hasProvider) {\n const tracerName = options.tracerName ?? DEFAULT_TRACER_NAME\n const tracer = options.provider.getTracer(tracerName, options.tracerVersion)\n\n if (!isTracer(tracer)) {\n throw new TypeError('OpenTelemetry provider did not return a tracer with startSpan')\n }\n\n return tracer\n }\n\n throw new TypeError('OpenTelemetryRuntimeObserver requires an existing tracer or provider')\n}\n\n/**\n * Best-effort RuntimeObserver adapter for an existing OpenTelemetry tracer.\n *\n * The adapter never installs a global provider, SDK, context manager, or\n * exporter. All observer hooks are synchronous and isolate tracer failures.\n */\nexport class OpenTelemetryRuntimeObserver implements RuntimeObserver {\n private readonly tracer: Tracer\n\n private readonly options: OpenTelemetryRuntimeObserverBaseOptions\n\n private readonly limits: Limits\n\n private readonly executionSpans = new Map<string, ExecutionSpan>()\n\n private readonly serviceResolution: ServiceTelemetryMode\n\n private readonly recordFailures: boolean\n\n private disposed = false\n\n constructor(options: OpenTelemetryRuntimeObserverOptions) {\n this.tracer = resolveTracer(options)\n this.options = options\n this.limits = normalizeLimits(options)\n this.serviceResolution = options.serviceResolution ?? 'off'\n this.recordFailures = options.recordFailures === true\n\n if (!['off', 'events', 'spans'].includes(this.serviceResolution)) {\n throw new TypeError('serviceResolution must be off, events, or spans')\n }\n }\n\n static make(options: OpenTelemetryRuntimeObserverOptions): OpenTelemetryRuntimeObserver {\n return new OpenTelemetryRuntimeObserver(options)\n }\n\n readonly onExecutionStart = (event: RuntimeExecutionStartEvent): void => {\n this.safely(() => this.startExecution(event))\n }\n\n readonly onExecutionEnd = (event: RuntimeExecutionEndEvent): void => {\n this.safely(() => this.endExecution(event))\n }\n\n readonly onServiceResolve = (event: RuntimeServiceResolveEvent): void => {\n this.safely(() => this.observeService('better-effect.service.resolve', event))\n }\n\n readonly onServiceAcquire = (event: RuntimeServiceAcquireEvent): void => {\n this.safely(() => this.observeService('better-effect.service.acquire', event))\n }\n\n readonly onResourceRelease = (event: RuntimeResourceReleaseEvent): void => {\n this.safely(() => this.observeService('better-effect.service.release', event))\n }\n\n /** End any spans whose malformed or missing end event left them pending. */\n dispose(): void {\n if (this.disposed) {\n return\n }\n\n this.disposed = true\n const pending = [...this.executionSpans.values()]\n\n for (const execution of pending) {\n const span = execution.span\n\n if (span === undefined) {\n // An in-flight start can publish its span after this reentrant disposal returns.\n continue\n }\n\n this.endExecutionSpan(execution, span)\n }\n }\n\n [Symbol.dispose](): void {\n this.dispose()\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n this.dispose()\n }\n\n private safely(operation: () => void): void {\n if (this.disposed) {\n return\n }\n\n try {\n operation()\n } catch {\n // Keep malformed events and tracer implementations outside Runtime results.\n }\n }\n\n private startSpan(\n name: string,\n attributes: AttributeMap,\n parentContext: Context | undefined\n ): Span | undefined {\n try {\n const span =\n parentContext === undefined\n ? this.tracer.startSpan(name, { attributes })\n : this.tracer.startSpan(name, { attributes }, parentContext)\n\n if (!isSpan(span)) {\n ignoreRejection(span)\n return undefined\n }\n\n return span\n } catch {\n return undefined\n }\n }\n\n private ownsExecutionSpan(execution: ExecutionSpan, span: Span): boolean {\n return this.executionSpans.get(execution.executionId) === execution && execution.span === span\n }\n\n private claimExecutionSpan(execution: ExecutionSpan, span: Span | undefined): boolean {\n if (this.executionSpans.get(execution.executionId) !== execution || execution.span !== span) {\n return false\n }\n\n this.executionSpans.delete(execution.executionId)\n return true\n }\n\n private endExecutionSpan(execution: ExecutionSpan, span: Span): void {\n if (this.claimExecutionSpan(execution, span)) {\n callSpan(() => span.end())\n }\n }\n\n private finishPendingExecution(execution: ExecutionSpan): void {\n if (execution.pendingEnd !== undefined && isActiveExecutionSpan(execution)) {\n this.finishExecution(execution, execution.pendingEnd)\n }\n }\n\n private startExecution(event: RuntimeExecutionStartEvent): void {\n if (this.executionSpans.has(event.executionId)) {\n return\n }\n\n // Reserve the ID before calling user tracer code: startSpan may synchronously re-enter this observer.\n const execution: ExecutionSpan = { executionId: event.executionId, ended: false }\n this.executionSpans.set(event.executionId, execution)\n const parentContext = readActiveContext() ?? ROOT_CONTEXT\n let span: Span | undefined\n\n try {\n span = this.startSpan(\n boundedNonEmptyString(event.name, this.limits.maxAttributeLength) ??\n DEFAULT_EXECUTION_SPAN_NAME,\n makeExecutionSpanAttributes(event, this.options, this.limits),\n parentContext\n )\n } catch {\n this.claimExecutionSpan(execution, undefined)\n return\n }\n\n if (span === undefined) {\n this.claimExecutionSpan(execution, undefined)\n return\n }\n\n execution.span = span\n\n if (this.disposed) {\n this.endExecutionSpan(execution, span)\n return\n }\n\n if (!this.ownsExecutionSpan(execution, span)) {\n return\n }\n\n const executionContext = setSpanInContext(parentContext, span)\n\n if (executionContext === undefined) {\n const pendingEnd = execution.pendingEnd\n\n if (pendingEnd !== undefined && isStartedExecutionSpan(execution)) {\n this.finishExecution(execution, pendingEnd)\n } else {\n this.endExecutionSpan(execution, span)\n }\n\n return\n }\n\n execution.executionContext = executionContext\n this.finishPendingExecution(execution)\n }\n\n private endExecution(event: RuntimeExecutionEndEvent): void {\n const execution = this.executionSpans.get(event.executionId)\n\n if (execution === undefined || execution.ended) {\n return\n }\n\n execution.ended = true\n\n if (!isActiveExecutionSpan(execution)) {\n execution.pendingEnd = event\n return\n }\n\n this.finishExecution(execution, event)\n }\n\n private finishExecution(execution: StartedExecutionSpan, event: RuntimeExecutionEndEvent): void {\n // Claim map ownership before invoking tracer callbacks so reentrant end or dispose calls cannot\n // finalize this exact span again.\n const span = execution.span\n\n if (!this.claimExecutionSpan(execution, span)) {\n return\n }\n\n try {\n const status = executionEventStatus(event)\n callSpan(() => span.setAttribute(OUTCOME_ATTRIBUTE, status))\n callSpan(() =>\n span.setStatus({\n code: status === 'failure' ? SpanStatusCode.ERROR : SpanStatusCode.OK\n })\n )\n\n if (status === 'failure' && this.recordFailures) {\n const cause =\n event.outcome.status === 'failure' ? event.outcome.cause : event.cleanupFailure\n const details = makeFailureDetails(cause, this.options.sanitizeFailure, this.limits)\n\n if (details !== undefined) {\n addFailureDetails(span, details)\n }\n }\n } finally {\n callSpan(() => span.end())\n }\n }\n\n private findCorrelatedExecution(event: ServiceEvent): ActiveExecutionSpan | undefined {\n if (event.executionId === undefined) {\n return undefined\n }\n\n const execution = this.executionSpans.get(event.executionId)\n return execution !== undefined && isActiveExecutionSpan(execution) ? execution : undefined\n }\n\n private observeService(name: string, event: ServiceEvent): void {\n if (this.serviceResolution === 'off') {\n return\n }\n\n const execution = this.findCorrelatedExecution(event)\n const executionId = execution === undefined ? undefined : this.findExecutionId(execution)\n const attributes = makeServiceEventAttributes(event, this.limits, executionId)\n let failureCause: unknown\n\n if ('error' in event && event.error !== undefined) {\n failureCause = event.error\n } else if (event.outcome.status === 'failure') {\n failureCause = event.outcome.cause\n }\n\n const details =\n serviceEventStatus(event) === 'failure' && this.recordFailures\n ? makeFailureDetails(failureCause, this.options.sanitizeFailure, this.limits)\n : undefined\n\n if (details?.attributes !== undefined) {\n mergeAdditionalAttributes(attributes, details.attributes, this.limits.maxAttributeCount)\n }\n\n if (execution !== undefined) {\n this.observeCorrelatedService(name, event, execution, attributes, details)\n return\n }\n\n this.observeStandaloneService(name, event, attributes, details)\n }\n\n private findExecutionId(execution: ExecutionSpan): string | undefined {\n return boundedNonEmptyString(execution.executionId, this.limits.maxAttributeLength)\n }\n\n private observeCorrelatedService(\n name: string,\n event: ServiceEvent,\n execution: ActiveExecutionSpan,\n attributes: AttributeMap,\n details: FailureDetails | undefined\n ): void {\n if (this.serviceResolution === 'events') {\n callSpan(() => execution.span.addEvent(name, attributes))\n\n if (details !== undefined) {\n addFailureDetails(execution.span, details)\n }\n\n return\n }\n\n const span = this.startSpan(name, attributes, execution.executionContext)\n\n if (span === undefined) {\n return\n }\n\n this.finishServiceSpan(span, event, details)\n }\n\n private observeStandaloneService(\n name: string,\n event: ServiceEvent,\n attributes: AttributeMap,\n details: FailureDetails | undefined\n ): void {\n const span = this.startSpan(\n this.serviceResolution === 'events' ? DEFAULT_SERVICE_SPAN_NAME : name,\n attributes,\n ROOT_CONTEXT\n )\n\n if (span === undefined) {\n return\n }\n\n if (this.serviceResolution === 'events') {\n callSpan(() => span.addEvent(name, attributes))\n }\n\n this.finishServiceSpan(span, event, details)\n }\n\n private finishServiceSpan(\n span: Span,\n event: ServiceEvent,\n details: FailureDetails | undefined\n ): void {\n callSpan(() =>\n span.setStatus({\n code: serviceEventStatus(event) === 'failure' ? SpanStatusCode.ERROR : SpanStatusCode.OK\n })\n )\n\n if (details !== undefined) {\n addFailureDetails(span, details)\n }\n\n callSpan(() => span.end())\n }\n}\n"],"mappings":"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"}