better-effect 0.13.0 → 0.14.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.
- package/README.md +337 -88
- package/dist/adapters/iti.d.mts +2 -3
- package/dist/adapters/iti.d.mts.map +1 -1
- package/dist/adapters/iti.mjs +2 -2
- package/dist/bun.d.mts +75 -23
- package/dist/bun.d.mts.map +1 -1
- package/dist/bun.mjs +170 -33
- package/dist/bun.mjs.map +1 -1
- package/dist/{context-BphfFUMO.mjs → context-DSGSqcJj.mjs} +12 -3
- package/dist/context-DSGSqcJj.mjs.map +1 -0
- package/dist/{current-request-DDKNyymv.mjs → current-request-Bc1YHL0w.mjs} +3 -2
- package/dist/{current-request-DDKNyymv.mjs.map → current-request-Bc1YHL0w.mjs.map} +1 -1
- package/dist/{current-request-CIU7AHhF.d.mts → current-request-Dm-QQ3Xr.d.mts} +4 -4
- package/dist/current-request-Dm-QQ3Xr.d.mts.map +1 -0
- package/dist/{effect-CPdV7Yv8.mjs → effect-W0IQXY8P.mjs} +21 -2
- package/dist/effect-W0IQXY8P.mjs.map +1 -0
- package/dist/{errors-BKcsQ0ip.d.mts → errors-CpHMzPiP.d.mts} +6 -2
- package/dist/errors-CpHMzPiP.d.mts.map +1 -0
- package/dist/hono.d.mts +63 -27
- package/dist/hono.d.mts.map +1 -1
- package/dist/hono.mjs +111 -45
- package/dist/hono.mjs.map +1 -1
- package/dist/{index-CCi07kUw.d.mts → index-3Bkl-sxS.d.mts} +7 -2
- package/dist/{index-CCi07kUw.d.mts.map → index-3Bkl-sxS.d.mts.map} +1 -1
- package/dist/{index-cXgyMa0v.d.mts → index-C2ed9Ilq.d.mts} +32 -7
- package/dist/index-C2ed9Ilq.d.mts.map +1 -0
- package/dist/{index-BodMy6rO.d.mts → index-ClNndn2-.d.mts} +93 -63
- package/dist/index-ClNndn2-.d.mts.map +1 -0
- package/dist/{index-DAKn0qHk.d.mts → index-Cy1VsU-u.d.mts} +2 -4
- package/dist/index-Cy1VsU-u.d.mts.map +1 -0
- package/dist/index.d.mts +6 -8
- package/dist/index.mjs +8 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{internal-identity-CbXoZIfw.mjs → internal-identity-BuHBtP0k.mjs} +2 -2
- package/dist/{internal-identity-CbXoZIfw.mjs.map → internal-identity-BuHBtP0k.mjs.map} +1 -1
- package/dist/layer-2kjHVv4N.mjs +343 -0
- package/dist/layer-2kjHVv4N.mjs.map +1 -0
- package/dist/layer-C2OaqTmB.d.mts +987 -0
- package/dist/layer-C2OaqTmB.d.mts.map +1 -0
- package/dist/next.d.mts +102 -26
- package/dist/next.d.mts.map +1 -1
- package/dist/next.mjs +157 -40
- package/dist/next.mjs.map +1 -1
- package/dist/{node-context-DczjAcrX.d.mts → node-context-1YEjrU9g.d.mts} +2 -2
- package/dist/{node-context-DczjAcrX.d.mts.map → node-context-1YEjrU9g.d.mts.map} +1 -1
- package/dist/{node-context-Ca18oNSg.mjs → node-context-g3bCcY9t.mjs} +2 -2
- package/dist/{node-context-Ca18oNSg.mjs.map → node-context-g3bCcY9t.mjs.map} +1 -1
- package/dist/node.d.mts +11 -6
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +156 -10
- package/dist/node.mjs.map +1 -1
- package/dist/opentelemetry.d.mts +20 -2
- package/dist/opentelemetry.d.mts.map +1 -1
- package/dist/opentelemetry.mjs +423 -5
- package/dist/opentelemetry.mjs.map +1 -1
- package/dist/{registration-drnZk0SY.mjs → registration-Bwe2GbVu.mjs} +8 -2
- package/dist/registration-Bwe2GbVu.mjs.map +1 -0
- package/dist/runtime/explicit.d.mts +2 -2
- package/dist/runtime/explicit.mjs +1 -1
- package/dist/runtime/node.d.mts +3 -3
- package/dist/runtime/node.mjs +2 -2
- package/dist/runtime-qpcVwaz_.mjs +1680 -0
- package/dist/runtime-qpcVwaz_.mjs.map +1 -0
- package/dist/service-CO9VJQYa.mjs +74 -0
- package/dist/service-CO9VJQYa.mjs.map +1 -0
- package/dist/{signal-nfgFyNCk.mjs → signal-5Rg9bUUe.mjs} +2 -2
- package/dist/{signal-nfgFyNCk.mjs.map → signal-5Rg9bUUe.mjs.map} +1 -1
- package/dist/{standard-services-vnGIafkS.mjs → standard-services-B-iMM_W7.mjs} +38 -4
- package/dist/standard-services-B-iMM_W7.mjs.map +1 -0
- package/dist/standard-services.d.mts +4 -4
- package/dist/standard-services.mjs +3 -3
- package/dist/testing.d.mts +25 -7
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +51 -7
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-oihI-ESv.d.mts → types-BFTqCCcw.d.mts} +32 -5
- package/dist/types-BFTqCCcw.d.mts.map +1 -0
- package/dist/web-effect-B9hp9jIe.mjs +380 -0
- package/dist/web-effect-B9hp9jIe.mjs.map +1 -0
- package/dist/web.d.mts +22 -13
- package/dist/web.d.mts.map +1 -1
- package/dist/web.mjs +1 -1
- package/package.json +10 -16
- package/dist/context-BphfFUMO.mjs.map +0 -1
- package/dist/current-request-CIU7AHhF.d.mts.map +0 -1
- package/dist/disposable-CP-Q2H3l.mjs +0 -57
- package/dist/disposable-CP-Q2H3l.mjs.map +0 -1
- package/dist/effect-CPdV7Yv8.mjs.map +0 -1
- package/dist/errors-BKcsQ0ip.d.mts.map +0 -1
- package/dist/index-AiKM71-u.d.mts +0 -376
- package/dist/index-AiKM71-u.d.mts.map +0 -1
- package/dist/index-BodMy6rO.d.mts.map +0 -1
- package/dist/index-DAKn0qHk.d.mts.map +0 -1
- package/dist/index-cXgyMa0v.d.mts.map +0 -1
- package/dist/layer-D16GKxuX.mjs +0 -269
- package/dist/layer-D16GKxuX.mjs.map +0 -1
- package/dist/observer-9nhbL7Pr.d.mts +0 -68
- package/dist/observer-9nhbL7Pr.d.mts.map +0 -1
- package/dist/outcome-DI8LPIQ4.d.mts +0 -387
- package/dist/outcome-DI8LPIQ4.d.mts.map +0 -1
- package/dist/program-metadata-BxjGkT17.mjs +0 -260
- package/dist/program-metadata-BxjGkT17.mjs.map +0 -1
- package/dist/registration-drnZk0SY.mjs.map +0 -1
- package/dist/runtime-C_55ofnL.mjs +0 -791
- package/dist/runtime-C_55ofnL.mjs.map +0 -1
- package/dist/standard-services-vnGIafkS.mjs.map +0 -1
- package/dist/types-oihI-ESv.d.mts.map +0 -1
- package/dist/web-Bq3MfMrj.mjs +0 -147
- package/dist/web-Bq3MfMrj.mjs.map +0 -1
package/dist/opentelemetry.mjs
CHANGED
|
@@ -1,4 +1,354 @@
|
|
|
1
1
|
import { ROOT_CONTEXT, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
2
|
+
//#region src/opentelemetry/lifecycle.ts
|
|
3
|
+
const readMonotonicTime$1 = () => {
|
|
4
|
+
try {
|
|
5
|
+
return performance.now();
|
|
6
|
+
} catch {
|
|
7
|
+
return Date.now();
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
const eventName$1 = (phase) => `better-effect.lifecycle.${phase}`;
|
|
11
|
+
const callSpan$2 = (operation) => {
|
|
12
|
+
try {
|
|
13
|
+
Promise.resolve(operation()).catch(() => {});
|
|
14
|
+
} catch {}
|
|
15
|
+
};
|
|
16
|
+
const finishSpan$1 = (span, status, details) => {
|
|
17
|
+
callSpan$2(() => span.setStatus({ code: status === "failure" ? SpanStatusCode.ERROR : SpanStatusCode.OK }));
|
|
18
|
+
if (details !== void 0) {
|
|
19
|
+
const message = details.message;
|
|
20
|
+
if (message !== void 0) callSpan$2(() => span.recordException(message));
|
|
21
|
+
if (Object.keys(details.attributes).length > 0) callSpan$2(() => span.addEvent("better-effect.failure", details.attributes));
|
|
22
|
+
}
|
|
23
|
+
callSpan$2(() => span.end());
|
|
24
|
+
};
|
|
25
|
+
const failureCause = (event) => {
|
|
26
|
+
if (event.error !== void 0) return event.error;
|
|
27
|
+
return event.outcome.status === "failure" ? event.outcome.cause : void 0;
|
|
28
|
+
};
|
|
29
|
+
const eventStatus = (event) => "error" in event && event.error !== void 0 ? "failure" : event.outcome.status;
|
|
30
|
+
/** Internal lifecycle state machine for the OpenTelemetry Runtime observer. */
|
|
31
|
+
var LifecycleTelemetry = class {
|
|
32
|
+
options;
|
|
33
|
+
states = /* @__PURE__ */ new Map();
|
|
34
|
+
disposed = false;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.options = options;
|
|
37
|
+
}
|
|
38
|
+
onStart(event) {
|
|
39
|
+
if (this.disposed || this.options.mode === "off" || this.states.has(event.lifecycleId)) return;
|
|
40
|
+
const state = {
|
|
41
|
+
lifecycleId: event.lifecycleId,
|
|
42
|
+
startedAt: readMonotonicTime$1(),
|
|
43
|
+
parentContext: this.options.getParentContext(),
|
|
44
|
+
starting: true,
|
|
45
|
+
carrier: void 0,
|
|
46
|
+
activationSpan: void 0,
|
|
47
|
+
activationEnded: false,
|
|
48
|
+
activationReported: false,
|
|
49
|
+
pendingActivationEnd: void 0
|
|
50
|
+
};
|
|
51
|
+
this.states.set(event.lifecycleId, state);
|
|
52
|
+
const span = this.options.startSpan(this.options.mode === "events" ? "better-effect.lifecycle" : eventName$1("activate"), this.options.makeAttributes(event, "activate", void 0, void 0), state.parentContext);
|
|
53
|
+
if (!this.states.has(event.lifecycleId)) {
|
|
54
|
+
if (span !== void 0) callSpan$2(() => span.end());
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
state.starting = false;
|
|
58
|
+
if (this.options.mode === "events") {
|
|
59
|
+
state.carrier = span;
|
|
60
|
+
if (span !== void 0) callSpan$2(() => span.addEvent(eventName$1("activate"), this.options.makeAttributes(event, "activate", void 0, void 0)));
|
|
61
|
+
} else state.activationSpan = span;
|
|
62
|
+
const pendingEnd = state.pendingActivationEnd;
|
|
63
|
+
if (pendingEnd !== void 0) {
|
|
64
|
+
state.pendingActivationEnd = void 0;
|
|
65
|
+
this.onEnd(pendingEnd);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
onEnd(event) {
|
|
69
|
+
if (this.options.mode === "off") return;
|
|
70
|
+
const state = this.states.get(event.lifecycleId);
|
|
71
|
+
if (state === void 0 || state.activationReported) return;
|
|
72
|
+
const durationMs = Math.max(0, readMonotonicTime$1() - state.startedAt);
|
|
73
|
+
const status = eventStatus(event);
|
|
74
|
+
if (this.options.mode === "events") {
|
|
75
|
+
const span = state.carrier;
|
|
76
|
+
if (span === void 0) {
|
|
77
|
+
if (state.starting) {
|
|
78
|
+
state.pendingActivationEnd = event;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
state.activationReported = true;
|
|
82
|
+
if (status === "failure") this.states.delete(event.lifecycleId);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
state.activationReported = true;
|
|
86
|
+
state.activationEnded = true;
|
|
87
|
+
if (status === "failure") this.states.delete(event.lifecycleId);
|
|
88
|
+
callSpan$2(() => span.addEvent(eventName$1("activate"), this.options.makeAttributes(event, "activate", status, durationMs)));
|
|
89
|
+
if (status === "failure" && this.options.recordFailures) {
|
|
90
|
+
const details = this.options.getFailureDetails(failureCause(event));
|
|
91
|
+
if (details !== void 0) finishDetails(span, details);
|
|
92
|
+
}
|
|
93
|
+
if (status === "failure") {
|
|
94
|
+
callSpan$2(() => span.setStatus({ code: SpanStatusCode.ERROR }));
|
|
95
|
+
callSpan$2(() => span.end());
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const span = state.activationSpan;
|
|
100
|
+
if (span === void 0) {
|
|
101
|
+
if (state.starting) {
|
|
102
|
+
state.pendingActivationEnd = event;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
state.activationReported = true;
|
|
106
|
+
if (status === "failure") this.states.delete(event.lifecycleId);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
state.activationReported = true;
|
|
110
|
+
if (status === "failure") this.states.delete(event.lifecycleId);
|
|
111
|
+
state.activationEnded = true;
|
|
112
|
+
finishSpan$1(span, status, status === "failure" && this.options.recordFailures ? this.options.getFailureDetails(failureCause(event)) : void 0);
|
|
113
|
+
}
|
|
114
|
+
onRelease(event) {
|
|
115
|
+
if (this.options.mode === "off") return;
|
|
116
|
+
const state = this.states.get(event.lifecycleId);
|
|
117
|
+
if (state === void 0) return;
|
|
118
|
+
this.states.delete(event.lifecycleId);
|
|
119
|
+
const status = eventStatus(event);
|
|
120
|
+
const attributes = this.options.makeAttributes(event, "release", status, void 0);
|
|
121
|
+
const details = status === "failure" && this.options.recordFailures ? this.options.getFailureDetails(failureCause(event)) : void 0;
|
|
122
|
+
if (this.options.mode === "events") {
|
|
123
|
+
if (state.carrier === void 0) return;
|
|
124
|
+
callSpan$2(() => state.carrier.addEvent(eventName$1("release"), attributes));
|
|
125
|
+
if (details !== void 0) finishDetails(state.carrier, details);
|
|
126
|
+
finishSpan$1(state.carrier, status, void 0);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const span = this.options.startSpan(eventName$1("release"), attributes, state.parentContext);
|
|
130
|
+
if (span !== void 0) finishSpan$1(span, status, details);
|
|
131
|
+
}
|
|
132
|
+
dispose() {
|
|
133
|
+
if (this.disposed) return;
|
|
134
|
+
this.disposed = true;
|
|
135
|
+
const pending = [...this.states.values()];
|
|
136
|
+
this.states.clear();
|
|
137
|
+
for (const state of pending) {
|
|
138
|
+
if (state.carrier !== void 0) callSpan$2(() => state.carrier.end());
|
|
139
|
+
if (state.activationSpan !== void 0 && !state.activationEnded) callSpan$2(() => state.activationSpan.end());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const finishDetails = (span, details) => {
|
|
144
|
+
const message = details.message;
|
|
145
|
+
if (message !== void 0) callSpan$2(() => span.recordException(message));
|
|
146
|
+
if (Object.keys(details.attributes).length > 0) callSpan$2(() => span.addEvent("better-effect.failure", details.attributes));
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/opentelemetry/shutdown.ts
|
|
150
|
+
const callSpan$1 = (operation) => {
|
|
151
|
+
try {
|
|
152
|
+
Promise.resolve(operation()).catch(() => {});
|
|
153
|
+
} catch {}
|
|
154
|
+
};
|
|
155
|
+
const readMonotonicTime = () => {
|
|
156
|
+
try {
|
|
157
|
+
return performance.now();
|
|
158
|
+
} catch {
|
|
159
|
+
return Date.now();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const boundedDuration$1 = (value) => value !== void 0 && Number.isFinite(value) && value >= 0 ? Math.min(value, 2 ** 31 - 1) : void 0;
|
|
163
|
+
const phaseOf = (phase) => {
|
|
164
|
+
switch (phase) {
|
|
165
|
+
case "shutdown-requested": return "requested";
|
|
166
|
+
case "quiesce-start":
|
|
167
|
+
case "quiesce-end": return "quiesce";
|
|
168
|
+
case "drain-start":
|
|
169
|
+
case "drain-end": return "drain";
|
|
170
|
+
case "abort-active": return "abort";
|
|
171
|
+
case "release-start":
|
|
172
|
+
case "release-end": return "release";
|
|
173
|
+
case "shutdown-complete":
|
|
174
|
+
case "shutdown-failure": return "complete";
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const eventName = (phase) => `better-effect.shutdown.${phase}`;
|
|
178
|
+
const isStart = (phase) => phase === "quiesce-start" || phase === "drain-start" || phase === "release-start";
|
|
179
|
+
const isEnd = (phase) => phase === "quiesce-end" || phase === "drain-end" || phase === "release-end";
|
|
180
|
+
const isTerminal = (phase) => phase === "shutdown-complete" || phase === "shutdown-failure";
|
|
181
|
+
const finishSpan = (span, status, details) => {
|
|
182
|
+
callSpan$1(() => span.setStatus({ code: status === "failure" ? SpanStatusCode.ERROR : SpanStatusCode.OK }));
|
|
183
|
+
if (details !== void 0) {
|
|
184
|
+
const message = details.message;
|
|
185
|
+
if (message !== void 0) callSpan$1(() => span.recordException(message));
|
|
186
|
+
if (Object.keys(details.attributes).length > 0) callSpan$1(() => span.addEvent("better-effect.failure", details.attributes));
|
|
187
|
+
}
|
|
188
|
+
callSpan$1(() => span.end());
|
|
189
|
+
};
|
|
190
|
+
/** Internal shutdown state machine for the OpenTelemetry Runtime observer. */
|
|
191
|
+
var ShutdownTelemetry = class {
|
|
192
|
+
options;
|
|
193
|
+
states = /* @__PURE__ */ new Map();
|
|
194
|
+
disposed = false;
|
|
195
|
+
constructor(options) {
|
|
196
|
+
this.options = options;
|
|
197
|
+
}
|
|
198
|
+
onEvent(event) {
|
|
199
|
+
if (this.disposed || this.options.mode === "off") return;
|
|
200
|
+
if (event.phase === "shutdown-requested") {
|
|
201
|
+
this.start(event);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const state = this.states.get(event.reason);
|
|
205
|
+
if (state === void 0) return;
|
|
206
|
+
if (state.span === void 0 && state.starting) {
|
|
207
|
+
state.pendingEvents.push(event);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (state.span === void 0 && isTerminal(event.phase)) {
|
|
211
|
+
this.states.delete(event.reason);
|
|
212
|
+
state.pendingEvents.length = 0;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (state.span !== void 0) this.process(state, event);
|
|
216
|
+
}
|
|
217
|
+
start(event) {
|
|
218
|
+
if (this.states.has(event.reason)) return;
|
|
219
|
+
const state = {
|
|
220
|
+
reason: event.reason,
|
|
221
|
+
starting: true,
|
|
222
|
+
span: void 0,
|
|
223
|
+
phaseSpans: /* @__PURE__ */ new Map(),
|
|
224
|
+
phaseStartedAt: /* @__PURE__ */ new Map(),
|
|
225
|
+
pendingPhaseEnds: /* @__PURE__ */ new Map(),
|
|
226
|
+
pendingEvents: []
|
|
227
|
+
};
|
|
228
|
+
this.states.set(event.reason, state);
|
|
229
|
+
const span = this.options.startSpan("better-effect.shutdown", this.options.makeAttributes(event, "requested", void 0), this.options.rootContext);
|
|
230
|
+
if (!this.states.has(event.reason)) {
|
|
231
|
+
if (span !== void 0) callSpan$1(() => span.end());
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
state.starting = false;
|
|
235
|
+
state.span = span;
|
|
236
|
+
if (span !== void 0) this.addEvent(state, event);
|
|
237
|
+
const pendingEvents = state.pendingEvents.splice(0);
|
|
238
|
+
for (const pendingEvent of pendingEvents) this.process(state, pendingEvent);
|
|
239
|
+
}
|
|
240
|
+
process(state, event) {
|
|
241
|
+
const phase = phaseOf(event.phase);
|
|
242
|
+
if (isStart(event.phase)) {
|
|
243
|
+
this.startPhase(state, event, phase);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (isEnd(event.phase)) {
|
|
247
|
+
if (!state.phaseStartedAt.has(phase)) return;
|
|
248
|
+
if (this.options.mode === "spans" && !state.phaseSpans.has(phase)) {
|
|
249
|
+
state.pendingPhaseEnds.set(phase, event);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
this.finishPhase(state, event, phase);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (event.phase === "abort-active") {
|
|
256
|
+
if (state.phaseStartedAt.has("abort")) return;
|
|
257
|
+
state.phaseStartedAt.set("abort", readMonotonicTime());
|
|
258
|
+
if (this.options.mode === "events") {
|
|
259
|
+
this.addEvent(state, event);
|
|
260
|
+
state.phaseStartedAt.delete("abort");
|
|
261
|
+
} else {
|
|
262
|
+
const span = this.startPhaseSpan(state, event, "abort");
|
|
263
|
+
if (span !== void 0) {
|
|
264
|
+
state.phaseStartedAt.delete("abort");
|
|
265
|
+
finishSpan(span, "success", void 0);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (isTerminal(event.phase)) this.finish(state, event);
|
|
271
|
+
}
|
|
272
|
+
startPhase(state, event, phase) {
|
|
273
|
+
if (state.phaseStartedAt.has(phase)) return;
|
|
274
|
+
state.phaseStartedAt.set(phase, readMonotonicTime());
|
|
275
|
+
if (this.options.mode === "events") {
|
|
276
|
+
this.addEvent(state, event);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const span = this.startPhaseSpan(state, event, phase);
|
|
280
|
+
if (!this.states.has(state.reason)) {
|
|
281
|
+
if (span !== void 0) callSpan$1(() => span.end());
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (span !== void 0) {
|
|
285
|
+
state.phaseSpans.set(phase, span);
|
|
286
|
+
const pendingEnd = state.pendingPhaseEnds.get(phase);
|
|
287
|
+
if (pendingEnd !== void 0) {
|
|
288
|
+
state.pendingPhaseEnds.delete(phase);
|
|
289
|
+
this.finishPhase(state, pendingEnd, phase);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
startPhaseSpan(state, event, phase) {
|
|
294
|
+
let parent = this.options.rootContext;
|
|
295
|
+
if (state.span !== void 0) try {
|
|
296
|
+
parent = trace.setSpan(this.options.rootContext, state.span);
|
|
297
|
+
} catch {
|
|
298
|
+
parent = this.options.rootContext;
|
|
299
|
+
}
|
|
300
|
+
return this.options.startSpan(eventName(phase), this.options.makeAttributes(event, phase, void 0), parent);
|
|
301
|
+
}
|
|
302
|
+
addEvent(state, event, durationMsOverride) {
|
|
303
|
+
const span = state.span;
|
|
304
|
+
if (span === void 0) return;
|
|
305
|
+
const phase = phaseOf(event.phase);
|
|
306
|
+
const startedAt = state.phaseStartedAt.get(phase);
|
|
307
|
+
const durationMs = durationMsOverride ?? (startedAt === void 0 ? void 0 : Math.max(0, readMonotonicTime() - startedAt));
|
|
308
|
+
callSpan$1(() => span.addEvent(eventName(phase), this.options.makeAttributes(event, phase, durationMs)));
|
|
309
|
+
}
|
|
310
|
+
finishPhase(state, event, phase) {
|
|
311
|
+
const span = state.phaseSpans.get(phase);
|
|
312
|
+
const startedAt = state.phaseStartedAt.get(phase);
|
|
313
|
+
const durationMs = startedAt === void 0 ? event.durationMs : Math.max(0, readMonotonicTime() - startedAt);
|
|
314
|
+
state.phaseSpans.delete(phase);
|
|
315
|
+
state.phaseStartedAt.delete(phase);
|
|
316
|
+
state.pendingPhaseEnds.delete(phase);
|
|
317
|
+
if (this.options.mode === "events") {
|
|
318
|
+
this.addEvent(state, event, durationMs);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (span !== void 0) {
|
|
322
|
+
callSpan$1(() => span.setAttribute("better_effect.duration_ms", boundedDuration$1(durationMs) ?? 0));
|
|
323
|
+
finishSpan(span, "success", void 0);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
finish(state, event) {
|
|
327
|
+
if (this.states.get(state.reason) !== state) return;
|
|
328
|
+
this.states.delete(state.reason);
|
|
329
|
+
const status = event.phase === "shutdown-failure" ? "failure" : "success";
|
|
330
|
+
const details = status === "failure" && this.options.recordFailures ? this.options.getFailureDetails(event.error) : void 0;
|
|
331
|
+
for (const span of state.phaseSpans.values()) callSpan$1(() => span.end());
|
|
332
|
+
state.phaseSpans.clear();
|
|
333
|
+
state.phaseStartedAt.clear();
|
|
334
|
+
state.pendingPhaseEnds.clear();
|
|
335
|
+
state.pendingEvents.length = 0;
|
|
336
|
+
if (state.span === void 0) return;
|
|
337
|
+
this.addEvent(state, event);
|
|
338
|
+
finishSpan(state.span, status, details);
|
|
339
|
+
}
|
|
340
|
+
dispose() {
|
|
341
|
+
if (this.disposed) return;
|
|
342
|
+
this.disposed = true;
|
|
343
|
+
const pending = [...this.states.values()];
|
|
344
|
+
this.states.clear();
|
|
345
|
+
for (const state of pending) {
|
|
346
|
+
for (const span of state.phaseSpans.values()) callSpan$1(() => span.end());
|
|
347
|
+
if (state.span !== void 0) callSpan$1(() => state.span.end());
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
//#endregion
|
|
2
352
|
//#region src/opentelemetry/index.ts
|
|
3
353
|
const DEFAULT_TRACER_NAME = "better-effect";
|
|
4
354
|
const DEFAULT_EXECUTION_SPAN_NAME = "better-effect.execution";
|
|
@@ -17,11 +367,47 @@ const SERVICE_TAG_ATTRIBUTE = "better_effect.service_tag";
|
|
|
17
367
|
const RESOLUTION_PATH_ATTRIBUTE = "better_effect.resolution_path";
|
|
18
368
|
const RESOLUTION_PATH_TRUNCATED_ATTRIBUTE = "better_effect.resolution_path_truncated";
|
|
19
369
|
const LIBRARY_ATTRIBUTE = "better_effect.library";
|
|
370
|
+
const LIFECYCLE_NAME_ATTRIBUTE = "better_effect.lifecycle_name";
|
|
371
|
+
const LIFECYCLE_PHASE_ATTRIBUTE = "better_effect.lifecycle_phase";
|
|
372
|
+
const SHUTDOWN_PHASE_ATTRIBUTE = "better_effect.shutdown_phase";
|
|
373
|
+
const SHUTDOWN_REASON_ATTRIBUTE = "better_effect.shutdown_reason";
|
|
374
|
+
const SHUTDOWN_SIGNAL_ATTRIBUTE = "better_effect.shutdown_signal";
|
|
375
|
+
const DURATION_ATTRIBUTE = "better_effect.duration_ms";
|
|
20
376
|
const FAILURE_EVENT_NAME = "better-effect.failure";
|
|
21
377
|
const serviceEventStatus = (event) => "error" in event && event.error !== void 0 ? "failure" : event.outcome.status;
|
|
22
378
|
const executionEventStatus = (event) => event.cleanupFailure !== void 0 ? "failure" : event.outcome.status;
|
|
23
379
|
const isStartedExecutionSpan = (execution) => execution.span !== void 0;
|
|
24
380
|
const isActiveExecutionSpan = (execution) => isStartedExecutionSpan(execution) && execution.executionContext !== void 0;
|
|
381
|
+
const isTelemetryMode = (value) => value === "off" || value === "events" || value === "spans";
|
|
382
|
+
const boundedDuration = (value) => value !== void 0 && Number.isFinite(value) && value >= 0 ? Math.min(value, 2 ** 31 - 1) : void 0;
|
|
383
|
+
const addOutcomeAttribute = (attributes, outcome, limit) => {
|
|
384
|
+
if (outcome !== void 0) addAttribute(attributes, OUTCOME_ATTRIBUTE, outcome, limit);
|
|
385
|
+
};
|
|
386
|
+
const addDurationAttribute = (attributes, durationMs, limit) => {
|
|
387
|
+
const duration = boundedDuration(durationMs);
|
|
388
|
+
if (duration !== void 0) addAttribute(attributes, DURATION_ATTRIBUTE, duration, limit);
|
|
389
|
+
};
|
|
390
|
+
const makeLifecycleAttributes = (event, phase, outcome, durationMs, limits) => {
|
|
391
|
+
const attributes = {};
|
|
392
|
+
const name = boundedNonEmptyString(event.name, limits.maxAttributeLength);
|
|
393
|
+
if (name !== void 0) addAttribute(attributes, LIFECYCLE_NAME_ATTRIBUTE, name, limits.maxAttributeCount);
|
|
394
|
+
addAttribute(attributes, LIFECYCLE_PHASE_ATTRIBUTE, phase, limits.maxAttributeCount);
|
|
395
|
+
addOutcomeAttribute(attributes, outcome, limits.maxAttributeCount);
|
|
396
|
+
addDurationAttribute(attributes, durationMs, limits.maxAttributeCount);
|
|
397
|
+
return attributes;
|
|
398
|
+
};
|
|
399
|
+
const makeShutdownAttributes = (event, phase, durationMs, limits) => {
|
|
400
|
+
const attributes = {};
|
|
401
|
+
const reason = event.reason;
|
|
402
|
+
addAttribute(attributes, SHUTDOWN_PHASE_ATTRIBUTE, phase, limits.maxAttributeCount);
|
|
403
|
+
addAttribute(attributes, SHUTDOWN_REASON_ATTRIBUTE, reason.kind, limits.maxAttributeCount);
|
|
404
|
+
if (reason.signal === "SIGINT" || reason.signal === "SIGTERM") addAttribute(attributes, SHUTDOWN_SIGNAL_ATTRIBUTE, reason.signal, limits.maxAttributeCount);
|
|
405
|
+
if (event.activeExecutions !== void 0 && Number.isSafeInteger(event.activeExecutions)) addAttribute(attributes, "better_effect.active_executions", Math.max(0, event.activeExecutions), limits.maxAttributeCount);
|
|
406
|
+
addDurationAttribute(attributes, durationMs ?? event.durationMs, limits.maxAttributeCount);
|
|
407
|
+
if (event.phase === "shutdown-complete") addOutcomeAttribute(attributes, "success", limits.maxAttributeCount);
|
|
408
|
+
else if (event.phase === "shutdown-failure") addOutcomeAttribute(attributes, "failure", limits.maxAttributeCount);
|
|
409
|
+
return attributes;
|
|
410
|
+
};
|
|
25
411
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26
412
|
const isSpan = (value) => isRecord(value) && typeof value.end === "function";
|
|
27
413
|
const isTracer = (value) => isRecord(value) && typeof value.startSpan === "function";
|
|
@@ -259,6 +645,8 @@ var OpenTelemetryRuntimeObserver = class OpenTelemetryRuntimeObserver {
|
|
|
259
645
|
limits;
|
|
260
646
|
executionSpans = /* @__PURE__ */ new Map();
|
|
261
647
|
serviceResolution;
|
|
648
|
+
lifecycleTelemetry;
|
|
649
|
+
shutdownTelemetry;
|
|
262
650
|
recordFailures;
|
|
263
651
|
disposed = false;
|
|
264
652
|
constructor(options) {
|
|
@@ -267,11 +655,27 @@ var OpenTelemetryRuntimeObserver = class OpenTelemetryRuntimeObserver {
|
|
|
267
655
|
this.limits = normalizeLimits(options);
|
|
268
656
|
this.serviceResolution = options.serviceResolution ?? "off";
|
|
269
657
|
this.recordFailures = options.recordFailures === true;
|
|
270
|
-
if (!
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
658
|
+
if (!isTelemetryMode(this.serviceResolution)) throw new TypeError("serviceResolution must be off, events, or spans");
|
|
659
|
+
const lifecycle = options.lifecycle ?? "off";
|
|
660
|
+
if (!isTelemetryMode(lifecycle)) throw new TypeError("lifecycle must be off, events, or spans");
|
|
661
|
+
const shutdown = options.shutdown ?? "off";
|
|
662
|
+
if (!isTelemetryMode(shutdown)) throw new TypeError("shutdown must be off, events, or spans");
|
|
663
|
+
this.lifecycleTelemetry = new LifecycleTelemetry({
|
|
664
|
+
mode: lifecycle,
|
|
665
|
+
recordFailures: this.recordFailures,
|
|
666
|
+
getParentContext: () => readActiveContext() ?? ROOT_CONTEXT,
|
|
667
|
+
startSpan: this.startSpan.bind(this),
|
|
668
|
+
makeAttributes: (event, phase, outcome, durationMs) => makeLifecycleAttributes(event, phase, outcome, durationMs, this.limits),
|
|
669
|
+
getFailureDetails: (cause) => makeFailureDetails(cause, this.options.sanitizeFailure, this.limits)
|
|
670
|
+
});
|
|
671
|
+
this.shutdownTelemetry = new ShutdownTelemetry({
|
|
672
|
+
mode: shutdown,
|
|
673
|
+
recordFailures: this.recordFailures,
|
|
674
|
+
rootContext: ROOT_CONTEXT,
|
|
675
|
+
startSpan: this.startSpan.bind(this),
|
|
676
|
+
makeAttributes: (event, phase, durationMs) => makeShutdownAttributes(event, phase, durationMs, this.limits),
|
|
677
|
+
getFailureDetails: (cause) => makeFailureDetails(cause, this.options.sanitizeFailure, this.limits)
|
|
678
|
+
});
|
|
275
679
|
}
|
|
276
680
|
static make(options) {
|
|
277
681
|
return new OpenTelemetryRuntimeObserver(options);
|
|
@@ -291,6 +695,18 @@ var OpenTelemetryRuntimeObserver = class OpenTelemetryRuntimeObserver {
|
|
|
291
695
|
onResourceRelease = (event) => {
|
|
292
696
|
this.safely(() => this.observeService("better-effect.service.release", event));
|
|
293
697
|
};
|
|
698
|
+
onLifecycleStart = (event) => {
|
|
699
|
+
this.safely(() => this.lifecycleTelemetry.onStart(event));
|
|
700
|
+
};
|
|
701
|
+
onLifecycleEnd = (event) => {
|
|
702
|
+
this.safely(() => this.lifecycleTelemetry.onEnd(event));
|
|
703
|
+
};
|
|
704
|
+
onLifecycleRelease = (event) => {
|
|
705
|
+
this.safely(() => this.lifecycleTelemetry.onRelease(event));
|
|
706
|
+
};
|
|
707
|
+
onShutdown = (event) => {
|
|
708
|
+
this.safely(() => this.shutdownTelemetry.onEvent(event));
|
|
709
|
+
};
|
|
294
710
|
/** End any spans whose malformed or missing end event left them pending. */
|
|
295
711
|
dispose() {
|
|
296
712
|
if (this.disposed) return;
|
|
@@ -301,6 +717,8 @@ var OpenTelemetryRuntimeObserver = class OpenTelemetryRuntimeObserver {
|
|
|
301
717
|
if (span === void 0) continue;
|
|
302
718
|
this.endExecutionSpan(execution, span);
|
|
303
719
|
}
|
|
720
|
+
this.lifecycleTelemetry.dispose();
|
|
721
|
+
this.shutdownTelemetry.dispose();
|
|
304
722
|
}
|
|
305
723
|
[Symbol.dispose]() {
|
|
306
724
|
this.dispose();
|