@mnemonica/otel 0.1.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/LICENSE +21 -0
- package/README.md +109 -0
- package/build/hooks/attach-hooks.d.ts +3 -0
- package/build/hooks/attach-hooks.d.ts.map +1 -0
- package/build/hooks/attach-hooks.js +52 -0
- package/build/index.d.ts +32 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +32 -0
- package/build/providers/async-flow.provider.d.ts +40 -0
- package/build/providers/async-flow.provider.d.ts.map +1 -0
- package/build/providers/async-flow.provider.js +128 -0
- package/build/providers/dive-otel.provider.d.ts +51 -0
- package/build/providers/dive-otel.provider.d.ts.map +1 -0
- package/build/providers/dive-otel.provider.js +238 -0
- package/build/providers/mnemonica-otel.provider.d.ts +12 -0
- package/build/providers/mnemonica-otel.provider.d.ts.map +1 -0
- package/build/providers/mnemonica-otel.provider.js +113 -0
- package/build/request-scope.d.ts +41 -0
- package/build/request-scope.d.ts.map +1 -0
- package/build/request-scope.js +35 -0
- package/build/thunderstruck/feed-from-request.d.ts +21 -0
- package/build/thunderstruck/feed-from-request.d.ts.map +1 -0
- package/build/thunderstruck/feed-from-request.js +34 -0
- package/build/thunderstruck/pre-root.d.ts +67 -0
- package/build/thunderstruck/pre-root.d.ts.map +1 -0
- package/build/thunderstruck/pre-root.js +78 -0
- package/build/unblind.d.ts +46 -0
- package/build/unblind.d.ts.map +1 -0
- package/build/unblind.js +120 -0
- package/build/utils/dive-flow.d.ts +24 -0
- package/build/utils/dive-flow.d.ts.map +1 -0
- package/build/utils/dive-flow.js +33 -0
- package/build/utils/is-mnemonica-instance.d.ts +2 -0
- package/build/utils/is-mnemonica-instance.d.ts.map +1 -0
- package/build/utils/is-mnemonica-instance.js +9 -0
- package/build-cjs/hooks/attach-hooks.js +55 -0
- package/build-cjs/index.js +52 -0
- package/build-cjs/package.json +1 -0
- package/build-cjs/providers/async-flow.provider.js +132 -0
- package/build-cjs/providers/dive-otel.provider.js +242 -0
- package/build-cjs/providers/mnemonica-otel.provider.js +117 -0
- package/build-cjs/request-scope.js +38 -0
- package/build-cjs/thunderstruck/feed-from-request.js +37 -0
- package/build-cjs/thunderstruck/pre-root.js +83 -0
- package/build-cjs/unblind.js +127 -0
- package/build-cjs/utils/dive-flow.js +37 -0
- package/build-cjs/utils/is-mnemonica-instance.js +12 -0
- package/package.json +69 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { SpanStatusCode, context as otelContext, trace } from '@opentelemetry/api';
|
|
2
|
+
import { registerHook, } from '@mnemonica/dive';
|
|
3
|
+
// Call/method/construct edges carry their callsite as `name`
|
|
4
|
+
// (`/abs/file.ts:line:col`, 1-based) — surfaced as OTEL semconv code.*
|
|
5
|
+
// attributes so Jaeger can link straight to the source (Wanted #4).
|
|
6
|
+
const CALLSITE_RE = /^(.*):(\d+):(\d+)$/;
|
|
7
|
+
// Wanted #2 (2026-09-01): the strategy push channel (an injected script,
|
|
8
|
+
// see strategy/cdp-scripts/ws-server.js) cannot see OTEL spans, so the
|
|
9
|
+
// adapter publishes edgeId → traceId on a bounded global map; the push
|
|
10
|
+
// mapper forwards it and mnemographica's Live Trace gains the "Open in
|
|
11
|
+
// Jaeger" jump. globalThis because adapter and injected script share a
|
|
12
|
+
// process, not a module graph.
|
|
13
|
+
const EDGE_TRACES_LIMIT = 10000;
|
|
14
|
+
function edgeTraceMap() {
|
|
15
|
+
const g = globalThis;
|
|
16
|
+
if (!g.__mnemonicaDiveTraceIds) {
|
|
17
|
+
g.__mnemonicaDiveTraceIds = new Map();
|
|
18
|
+
}
|
|
19
|
+
const map = g.__mnemonicaDiveTraceIds;
|
|
20
|
+
return map;
|
|
21
|
+
}
|
|
22
|
+
function recordEdgeTrace(edgeId, traceId) {
|
|
23
|
+
const map = edgeTraceMap();
|
|
24
|
+
if (map.has(edgeId)) {
|
|
25
|
+
map.delete(edgeId);
|
|
26
|
+
}
|
|
27
|
+
else if (map.size >= EDGE_TRACES_LIMIT) {
|
|
28
|
+
// FIFO: the oldest entry dies first — Map iterates insertion order
|
|
29
|
+
const oldest = map.keys().next();
|
|
30
|
+
if (!oldest.done) {
|
|
31
|
+
map.delete(oldest.value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
map.set(edgeId, traceId);
|
|
35
|
+
}
|
|
36
|
+
export class DiveOtelProvider {
|
|
37
|
+
tracer;
|
|
38
|
+
// open spans, keyed on dive edge id — drained by leave/settle
|
|
39
|
+
spans = new Map();
|
|
40
|
+
// edge id → parentId, for the root-edge walk (dive.root_edge_id span
|
|
41
|
+
// attribute). Never drained mid-flight: a completed edge can still
|
|
42
|
+
// parent later children (a create edge adopts the next wrapped call),
|
|
43
|
+
// so the map is bounded coarsely instead.
|
|
44
|
+
edgeParents = new Map();
|
|
45
|
+
static EDGE_PARENTS_LIMIT = 20000;
|
|
46
|
+
detachers = [];
|
|
47
|
+
constructor(tracer) {
|
|
48
|
+
this.tracer = tracer ?? trace.getTracer('@mnemonica/otel');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to dive's edge lifecycle. Idempotent: attaching twice would
|
|
52
|
+
* double every span. Dive's clear() wipes subscribers — re-attach after it.
|
|
53
|
+
*/
|
|
54
|
+
attach() {
|
|
55
|
+
if (this.detachers.length > 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.detachers.push(registerHook('enter', (payload) => {
|
|
59
|
+
this.onEnter(payload);
|
|
60
|
+
}), registerHook('leave', (payload) => {
|
|
61
|
+
this.onLeave(payload);
|
|
62
|
+
}), registerHook('settle', (payload) => {
|
|
63
|
+
this.onSettle(payload);
|
|
64
|
+
}), registerHook('recontext', (payload) => {
|
|
65
|
+
this.onRecontext(payload);
|
|
66
|
+
}));
|
|
67
|
+
try {
|
|
68
|
+
this.detachers.push(registerHook('create', (payload) => {
|
|
69
|
+
this.onCreate(payload);
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// The 'create' event exists since dive 0.8.0; on 0.7.x registerHook
|
|
74
|
+
// throws on the unknown event. Skipping it there preserves exactly
|
|
75
|
+
// the pre-subscription behavior (constructions stay unspanned), so
|
|
76
|
+
// the widened ^0.7.0 || ^0.8.0 peer range stays honest.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
detach() {
|
|
80
|
+
for (const detach of this.detachers) {
|
|
81
|
+
detach();
|
|
82
|
+
}
|
|
83
|
+
this.detachers = [];
|
|
84
|
+
}
|
|
85
|
+
onEnter({ edge }) {
|
|
86
|
+
const parentSpan = this.findParentSpan(edge);
|
|
87
|
+
const ctx = parentSpan
|
|
88
|
+
? trace.setSpan(otelContext.active(), parentSpan)
|
|
89
|
+
: undefined;
|
|
90
|
+
const name = `dive.${edge.kind}:${edge.name}`;
|
|
91
|
+
const span = ctx
|
|
92
|
+
? this.tracer.startSpan(name, {}, ctx)
|
|
93
|
+
: this.tracer.startSpan(name);
|
|
94
|
+
span.setAttribute('dive.edge_id', edge.id);
|
|
95
|
+
span.setAttribute('dive.kind', edge.kind);
|
|
96
|
+
span.setAttribute('dive.name', edge.name);
|
|
97
|
+
this.decorateSpan(span, edge);
|
|
98
|
+
this.spans.set(edge.id, span);
|
|
99
|
+
}
|
|
100
|
+
onLeave({ edge, result }) {
|
|
101
|
+
if (result instanceof Promise) {
|
|
102
|
+
// async work: the span closes at settle, not at the sync head —
|
|
103
|
+
// "the function returned" is not "the work is done"
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.closeSpan(edge);
|
|
107
|
+
}
|
|
108
|
+
onSettle({ edge, error }) {
|
|
109
|
+
this.closeSpan(edge, error);
|
|
110
|
+
}
|
|
111
|
+
onRecontext({ edge, previousContext, context }) {
|
|
112
|
+
// One-shot span: the ownership transfer itself, parented on the OLD
|
|
113
|
+
// context's span (the handoff edge's parentId), so the trace shows
|
|
114
|
+
// where the callback's story crossed flows.
|
|
115
|
+
const parentSpan = this.findParentSpan(edge);
|
|
116
|
+
const ctx = parentSpan
|
|
117
|
+
? trace.setSpan(otelContext.active(), parentSpan)
|
|
118
|
+
: undefined;
|
|
119
|
+
const name = `dive.${edge.kind}:${edge.name}`;
|
|
120
|
+
const span = ctx
|
|
121
|
+
? this.tracer.startSpan(name, {}, ctx)
|
|
122
|
+
: this.tracer.startSpan(name);
|
|
123
|
+
span.setAttribute('dive.edge_id', edge.id);
|
|
124
|
+
span.setAttribute('dive.kind', edge.kind);
|
|
125
|
+
span.setAttribute('dive.name', edge.name);
|
|
126
|
+
span.setAttribute('dive.handoff', true);
|
|
127
|
+
span.setAttribute('dive.handoff.had_previous', previousContext !== undefined);
|
|
128
|
+
span.setAttribute('dive.handoff.has_context', context !== undefined);
|
|
129
|
+
this.decorateSpan(span, edge);
|
|
130
|
+
span.end();
|
|
131
|
+
}
|
|
132
|
+
onCreate({ edge, error }) {
|
|
133
|
+
// One-shot span: the construction already completed when recordCreation
|
|
134
|
+
// fired (the hook moment IS the completion), so the span starts and
|
|
135
|
+
// ends here — same shape as recontext. findParentSpan adopts the
|
|
136
|
+
// wrapped call's span via edge.parentId, or the active request span at
|
|
137
|
+
// a boundary, so constructions join the request trace instead of
|
|
138
|
+
// opening a root trace of their own.
|
|
139
|
+
const parentSpan = this.findParentSpan(edge);
|
|
140
|
+
const ctx = parentSpan
|
|
141
|
+
? trace.setSpan(otelContext.active(), parentSpan)
|
|
142
|
+
: undefined;
|
|
143
|
+
const name = `dive.${edge.kind}:${edge.name}`;
|
|
144
|
+
const span = ctx
|
|
145
|
+
? this.tracer.startSpan(name, {}, ctx)
|
|
146
|
+
: this.tracer.startSpan(name);
|
|
147
|
+
span.setAttribute('dive.edge_id', edge.id);
|
|
148
|
+
span.setAttribute('dive.kind', edge.kind);
|
|
149
|
+
span.setAttribute('dive.name', edge.name);
|
|
150
|
+
span.setAttribute('dive.status', edge.status);
|
|
151
|
+
if (edge.duration !== undefined) {
|
|
152
|
+
span.setAttribute('dive.duration_ms', edge.duration);
|
|
153
|
+
}
|
|
154
|
+
this.decorateSpan(span, edge);
|
|
155
|
+
if (edge.status === 'error') {
|
|
156
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
157
|
+
if (error instanceof Error) {
|
|
158
|
+
span.recordException(error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
span.end();
|
|
162
|
+
}
|
|
163
|
+
findParentSpan(edge) {
|
|
164
|
+
if (edge.parentId !== null) {
|
|
165
|
+
const own = this.spans.get(edge.parentId);
|
|
166
|
+
if (own) {
|
|
167
|
+
return own;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Boundary (or evicted parent): adopt the active OTel span — the HTTP
|
|
171
|
+
// request span becomes the root of the dive branch.
|
|
172
|
+
const active = trace.getSpan(otelContext.active());
|
|
173
|
+
return active;
|
|
174
|
+
}
|
|
175
|
+
closeSpan(edge, error) {
|
|
176
|
+
const span = this.spans.get(edge.id);
|
|
177
|
+
if (!span) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.spans.delete(edge.id);
|
|
181
|
+
span.setAttribute('dive.status', edge.status);
|
|
182
|
+
if (edge.duration !== undefined) {
|
|
183
|
+
span.setAttribute('dive.duration_ms', edge.duration);
|
|
184
|
+
}
|
|
185
|
+
if (edge.status === 'error') {
|
|
186
|
+
// sync throws carry no error value in the leave payload — the
|
|
187
|
+
// edge's own status is the truthful signal; the exception record
|
|
188
|
+
// is available only when settle carried the rejection itself
|
|
189
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
190
|
+
if (error instanceof Error) {
|
|
191
|
+
span.recordException(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
span.end();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Cross-surface attributes every span gets, on every hook path:
|
|
198
|
+
* the edge's trace root id (Jaeger link → mnemographica's Live Trace,
|
|
199
|
+
* Wanted #1), the edgeId→traceId publication for the strategy push
|
|
200
|
+
* channel (Wanted #2), and code.filepath/line/column parsed from the
|
|
201
|
+
* callsite name (Wanted #4).
|
|
202
|
+
*/
|
|
203
|
+
decorateSpan(span, edge) {
|
|
204
|
+
span.setAttribute('dive.root_edge_id', this.rootEdgeIdOf(edge));
|
|
205
|
+
recordEdgeTrace(edge.id, span.spanContext().traceId);
|
|
206
|
+
if (edge.kind !== 'call' && edge.kind !== 'method' && edge.kind !== 'construct') {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const match = CALLSITE_RE.exec(edge.name);
|
|
210
|
+
if (!match) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
span.setAttribute('code.filepath', match[1]);
|
|
214
|
+
span.setAttribute('code.lineno', Number(match[2]));
|
|
215
|
+
span.setAttribute('code.column', Number(match[3]));
|
|
216
|
+
}
|
|
217
|
+
// Walk dive's parentage to the root edge id. Parents are recorded as
|
|
218
|
+
// edges arrive (enter fires parent-before-child), so the chain is
|
|
219
|
+
// complete for anything still in flight; an evicted/unknown parent
|
|
220
|
+
// simply ends the walk at the deepest known id.
|
|
221
|
+
rootEdgeIdOf(edge) {
|
|
222
|
+
if (this.edgeParents.size >= DiveOtelProvider.EDGE_PARENTS_LIMIT) {
|
|
223
|
+
this.edgeParents.clear();
|
|
224
|
+
}
|
|
225
|
+
this.edgeParents.set(edge.id, edge.parentId);
|
|
226
|
+
let id = edge.id;
|
|
227
|
+
let parent = edge.parentId;
|
|
228
|
+
const seen = new Set([id]);
|
|
229
|
+
while (parent !== null && !seen.has(parent)) {
|
|
230
|
+
seen.add(parent);
|
|
231
|
+
id = parent;
|
|
232
|
+
const next = this.edgeParents.get(parent);
|
|
233
|
+
parent = next === undefined ? null : next;
|
|
234
|
+
}
|
|
235
|
+
return id;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGl2ZS1vdGVsLnByb3ZpZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb3ZpZGVycy9kaXZlLW90ZWwucHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBb0JBLE9BQU8sRUFBRSxjQUFjLEVBQUUsT0FBTyxJQUFJLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNuRixPQUFPLEVBQ04sWUFBWSxHQU9aLE1BQU0saUJBQWlCLENBQUM7QUFFekIsNkRBQTZEO0FBQzdELHVFQUF1RTtBQUN2RSxvRUFBb0U7QUFDcEUsTUFBTSxXQUFXLEdBQUcsb0JBQW9CLENBQUM7QUFFekMseUVBQXlFO0FBQ3pFLHVFQUF1RTtBQUN2RSx1RUFBdUU7QUFDdkUsdUVBQXVFO0FBQ3ZFLHVFQUF1RTtBQUN2RSwrQkFBK0I7QUFDL0IsTUFBTSxpQkFBaUIsR0FBRyxLQUFLLENBQUM7QUFFaEMsU0FBUyxZQUFZO0lBQ3BCLE1BQU0sQ0FBQyxHQUFHLFVBQXdELENBQUM7SUFDbkUsSUFBSSxDQUFDLENBQUMsQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQ2hDLENBQUMsQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ3ZDLENBQUM7SUFDRCxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsdUJBQXVCLENBQUM7SUFDdEMsT0FBTyxHQUFHLENBQUM7QUFDWixDQUFDO0FBQ0QsU0FBUyxlQUFlLENBQUUsTUFBYyxFQUFFLE9BQWU7SUFDeEQsTUFBTSxHQUFHLEdBQUcsWUFBWSxFQUFFLENBQUM7SUFDM0IsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDckIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNwQixDQUFDO1NBQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxJQUFJLGlCQUFpQixFQUFFLENBQUM7UUFDMUMsbUVBQW1FO1FBQ25F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ciBmb3IgZGl2ZSdzIGVkZ2UgbGlmZWN5Y2xlIGhvb2tzLlxuICpcbiAqIFdoZXJlIE1uZW1vbmljYU90ZWxQcm92aWRlciBzcGFucyBDT05TVFJVQ1RJT05TIChtbmVtb25pY2EgaG9va3MpLCB0aGlzXG4gKiBwcm92aWRlciBzcGFucyBFVkVSWSBXUkFQUEVEIENBTEwgKGRpdmUgaG9va3MpOiBjYWxsIC8gY29uc3RydWN0IC8gbWV0aG9kIC9cbiAqIHJlY29udGV4dCBlZGdlcyBlYWNoIGJlY29tZSBhIHNwYW4sIHBhcmVudGVkIG9uIHRoZSBzcGFuIG9mIHRoZSBlZGdlJ3NcbiAqIHBhcmVudElkIOKAlCBkaXZlJ3Mgb3duIHRyYWNlIHBhcmVudGFnZSwgbm90IEFMUy4gQXQgdW53cmFwcGVkIGJvdW5kYXJpZXNcbiAqIChwYXJlbnRJZCBudWxsKSB0aGUgc3BhbiBhZG9wdHMgdGhlIGN1cnJlbnRseSBBQ1RJVkUgT1RlbCBzcGFuIGFzIHBhcmVudCxcbiAqIHNvIGFuXG4gKiBIVFRQIHJlcXVlc3Qgc3BhbiBhZG9wdHMgdGhlIHdob2xlIGRpdmUgYnJhbmNoLiAnY3JlYXRlJyBlZGdlcyAobW5lbW9uaWNhXG4gKiBjb25zdHJ1Y3Rpb25zIHJlY29yZGVkIHZpYSByZWNvcmRDcmVhdGlvbi9yZWNvcmRDcmVhdGlvbkVycm9yKSBiZWNvbWVcbiAqIG9uZS1zaG90IHNwYW5zIG9uIHRoZSBzYW1lIHBhcmVudGFnZSDigJQgdGhlIGNvbnN0cnVjdGlvbiBIQVMgY29tcGxldGVkIHdoZW5cbiAqIHRoZSBob29rIGZpcmVzLCBzbyB0aGUgc3BhbiBzdGFydHMgYW5kIGVuZHMgaW5zaWRlIHRoZSBoYW5kbGVyLlxuICpcbiAqIEFzeW5jIHRydXRoZnVsbmVzczogYSBzcGFuIGRvZXMgTk9UIGVuZCBhdCB0aGUgc3luYyBjbG9zZSB3aGVuIHRoZSB3cmFwXG4gKiBwcm9kdWNlZCBhIHRhcHBlZCBwcm9taXNlIOKAlCBpdCBlbmRzIGF0IHNldHRsZSwgd2l0aCB0aGUgY2hhaW4ncyBvdXRjb21lLlxuICogU3BhbnMgYXJlIGtleWVkIG9uIGVkZ2UgaWQ7IGV2ZXJ5IHJlY29yZGVkIGVkZ2UgZmlyZXMgbGVhdmUgKGFuZCwgd2hlblxuICogYXN5bmMsIHNldHRsZSksIHNvIHRoZSBtYXAgYWx3YXlzIGRyYWlucy5cbiAqL1xuaW1wb3J0IHR5cGUgeyBTcGFuLCBUcmFjZXIgfSBmcm9tICdAb3BlbnRlbGVtZXRyeS9hcGknO1xuaW1wb3J0IHsgU3BhblN0YXR1c0NvZGUsIGNvbnRleHQgYXMgb3RlbENvbnRleHQsIHRyYWNlIH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvYXBpJztcbmltcG9ydCB7XG5cdHJlZ2lzdGVySG9vayxcblx0dHlwZSBEaXZlQ3JlYXRlUGF5bG9hZCxcblx0dHlwZSBEaXZlRW50ZXJQYXlsb2FkLFxuXHR0eXBlIERpdmVMZWF2ZVBheWxvYWQsXG5cdHR5cGUgRGl2ZVJlY29udGV4dFBheWxvYWQsXG5cdHR5cGUgRGl2ZVNldHRsZVBheWxvYWQsXG5cdHR5cGUgRmxvd0VkZ2UsXG59IGZyb20gJ0BtbmVtb25pY2EvZGl2ZSc7XG5cbi8vIENhbGwvbWV0aG9kL2NvbnN0cnVjdCBlZGdlcyBjYXJyeSB0aGVpciBjYWxsc2l0ZSBhcyBgbmFtZWBcbi8vIChgL2Ficy9maWxlLnRzOmxpbmU6Y29sYCwgMS1iYXNlZCkg4oCUIHN1cmZhY2VkIGFzIE9URUwgc2VtY29udiBjb2RlLipcbi8vIGF0dHJpYnV0ZXMgc28gSmFlZ2VyIGNhbiBsaW5rIHN0cmFpZ2h0IHRvIHRoZSBzb3VyY2UgKFdhbnRlZCAjNCkuXG5jb25zdCBDQUxMU0lURV9SRSA9IC9eKC4qKTooXFxkKyk6KFxcZCspJC87XG5cbi8vIFdhbnRlZCAjMiAoMjAyNi0wOS0wMSk6IHRoZSBzdHJhdGVneSBwdXNoIGNoYW5uZWwgKGFuIGluamVjdGVkIHNjcmlwdCxcbi8vIHNlZSBzdHJhdGVneS9jZHAtc2NyaXB0cy93cy1zZXJ2ZXIuanMpIGNhbm5vdCBzZWUgT1RFTCBzcGFucywgc28gdGhlXG4vLyBhZGFwdGVyIHB1Ymxpc2hlcyBlZGdlSWQg4oaSIHRyYWNlSWQgb24gYSBib3VuZGVkIGdsb2JhbCBtYXA7IHRoZSBwdXNoXG4vLyBtYXBwZXIgZm9yd2FyZHMgaXQgYW5kIG1uZW1vZ3JhcGhpY2EncyBMaXZlIFRyYWNlIGdhaW5zIHRoZSBcIk9wZW4gaW5cbi8vIEphZWdlclwiIGp1bXAuIGdsb2JhbFRoaXMgYmVjYXVzZSBhZGFwdGVyIGFuZCBpbmplY3RlZCBzY3JpcHQgc2hhcmUgYVxuLy8gcHJvY2Vzcywgbm90IGEgbW9kdWxlIGdyYXBoLlxuY29uc3QgRURHRV9UUkFDRVNfTElNSVQgPSAxMDAwMDtcbnR5cGUgRWRnZVRyYWNlTWFwID0gTWFwPG51bWJlciwgc3RyaW5nPjtcbmZ1bmN0aW9uIGVkZ2VUcmFjZU1hcCAoKTogRWRnZVRyYWNlTWFwIHtcblx0Y29uc3QgZyA9IGdsb2JhbFRoaXMgYXMgeyBfX21uZW1vbmljYURpdmVUcmFjZUlkcz86IEVkZ2VUcmFjZU1hcCB9O1xuXHRpZiAoIWcuX19tbmVtb25pY2FEaXZlVHJhY2VJZHMpIHtcblx0XHRnLl9fbW5lbW9uaWNhRGl2ZVRyYWNlSWRzID0gbmV3IE1hcCgpO1xuXHR9XG5cdGNvbnN0IG1hcCA9IGcuX19tbmVtb25pY2FEaXZlVHJhY2VJZHM7XG5cdHJldHVybiBtYXA7XG59XG5mdW5jdGlvbiByZWNvcmRFZGdlVHJhY2UgKGVkZ2VJZDogbnVtYmVyLCB0cmFjZUlkOiBzdHJpbmcpOiB2b2lkIHtcblx0Y29uc3QgbWFwID0gZWRnZVRyYWNlTWFwKCk7XG5cdGlmIChtYXAuaGFzKGVkZ2VJZCkpIHtcblx0XHRtYXAuZGVsZXRlKGVkZ2VJZCk7XG5cdH0gZWxzZSBpZiAobWFwLnNpemUgPj0gRURHRV9UUkFDRVNfTElNSVQpIHtcblx0XHQvLyBGSUZPOiB0aGUgb2xkZXN0IGVudHJ5IGRpZXMgZmlyc3Qg4oCUIE1hcCBpdGVyYXRlcyBpbnNlcnRpb24gb3JkZXJcblx0XHRjb25zdCBvbGRlc3QgPSBtYXAua2V5cygpLm5leHQoKTtcblx0XHRpZiAoIW9sZGVzdC5kb25lKSB7XG5cdFx0XHRtYXAuZGVsZXRlKG9sZGVzdC52YWx1ZSk7XG5cdFx0fVxuXHR9XG5cdG1hcC5zZXQoZWRnZUlkLCB0cmFjZUlkKTtcbn1cblxuZXhwb3J0IGNsYXNzIERpdmVPdGVsUHJvdmlkZXIge1xuXHRwcml2YXRlIHRyYWNlcjogVHJhY2VyO1xuXHQvLyBvcGVuIHNwYW5zLCBrZXllZCBvbiBkaXZlIGVkZ2UgaWQg4oCUIGRyYWluZWQgYnkgbGVhdmUvc2V0dGxlXG5cdHByaXZhdGUgc3BhbnMgPSBuZXcgTWFwPG51bWJlciwgU3Bhbj4oKTtcblx0Ly8gZWRnZSBpZCDihpIgcGFyZW50SWQsIGZvciB0aGUgcm9vdC1lZGdlIHdhbGsgKGRpdmUucm9vdF9lZGdlX2lkIHNwYW5cblx0Ly8gYXR0cmlidXRlKS4gTmV2ZXIgZHJhaW5lZCBtaWQtZmxpZ2h0OiBhIGNvbXBsZXRlZCBlZGdlIGNhbiBzdGlsbFxuXHQvLyBwYXJlbnQgbGF0ZXIgY2hpbGRyZW4gKGEgY3JlYXRlIGVkZ2UgYWRvcHRzIHRoZSBuZXh0IHdyYXBwZWQgY2FsbCksXG5cdC8vIHNvIHRoZSBtYXAgaXMgYm91bmRlZCBjb2Fyc2VseSBpbnN0ZWFkLlxuXHRwcml2YXRlIGVkZ2VQYXJlbnRzID0gbmV3IE1hcDxudW1iZXIsIG51bWJlciB8IG51bGw+KCk7XG5cdHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IEVER0VfUEFSRU5UU19MSU1JVCA9IDIwMDAwO1xuXHRwcml2YXRlIGRldGFjaGVyczogQXJyYXk8KCkgPT4gdm9pZD4gPSBbXTtcblxuXHRjb25zdHJ1Y3RvciAodHJhY2VyPzogVHJhY2VyKSB7XG5cdFx0dGhpcy50cmFjZXIgPSB0cmFjZXIgPz8gdHJhY2UuZ2V0VHJhY2VyKCdAbW5lbW9uaWNhL290ZWwnKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBTdWJzY3JpYmUgdG8gZGl2ZSdzIGVkZ2UgbGlmZWN5Y2xlLiBJZGVtcG90ZW50OiBhdHRhY2hpbmcgdHdpY2Ugd291bGRcblx0ICogZG91YmxlIGV2ZXJ5IHNwYW4uIERpdmUncyBjbGVhcigpIHdpcGVzIHN1YnNjcmliZXJzIOKAlCByZS1hdHRhY2ggYWZ0ZXIgaXQuXG5cdCAqL1xuXHRhdHRhY2ggKCk6IHZvaWQge1xuXHRcdGlmICh0aGlzLmRldGFjaGVycy5sZW5ndGggPiAwKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuZGV0YWNoZXJzLnB1c2goXG5cdFx0XHRyZWdpc3Rlckhvb2soJ2VudGVyJywgKHBheWxvYWQpID0+IHtcblx0XHRcdFx0dGhpcy5vbkVudGVyKHBheWxvYWQpO1xuXHRcdFx0fSksXG5cdFx0XHRyZWdpc3Rlckhvb2soJ2xlYXZlJywgKHBheWxvYWQpID0+IHtcblx0XHRcdFx0dGhpcy5vbkxlYXZlKHBheWxvYWQpO1xuXHRcdFx0fSksXG5cdFx0XHRyZWdpc3Rlckhvb2soJ3NldHRsZScsIChwYXlsb2FkKSA9PiB7XG5cdFx0XHRcdHRoaXMub25TZXR0bGUocGF5bG9hZCk7XG5cdFx0XHR9KSxcblx0XHRcdHJlZ2lzdGVySG9vaygncmVjb250ZXh0JywgKHBheWxvYWQpID0+IHtcblx0XHRcdFx0dGhpcy5vblJlY29udGV4dChwYXlsb2FkKTtcblx0XHRcdH0pLFxuXHRcdCk7XG5cdFx0dHJ5IHtcblx0XHRcdHRoaXMuZGV0YWNoZXJzLnB1c2goXG5cdFx0XHRcdHJlZ2lzdGVySG9vaygnY3JlYXRlJywgKHBheWxvYWQpID0+IHtcblx0XHRcdFx0XHR0aGlzLm9uQ3JlYXRlKHBheWxvYWQpO1xuXHRcdFx0XHR9KSxcblx0XHRcdCk7XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHQvLyBUaGUgJ2NyZWF0ZScgZXZlbnQgZXhpc3RzIHNpbmNlIGRpdmUgMC44LjA7IG9uIDAuNy54IHJlZ2lzdGVySG9va1xuXHRcdFx0Ly8gdGhyb3dzIG9uIHRoZSB1bmtub3duIGV2ZW50LiBTa2lwcGluZyBpdCB0aGVyZSBwcmVzZXJ2ZXMgZXhhY3RseVxuXHRcdFx0Ly8gdGhlIHByZS1zdWJzY3JpcHRpb24gYmVoYXZpb3IgKGNvbnN0cnVjdGlvbnMgc3RheSB1bnNwYW5uZWQpLCBzb1xuXHRcdFx0Ly8gdGhlIHdpZGVuZWQgXjAuNy4wIHx8IF4wLjguMCBwZWVyIHJhbmdlIHN0YXlzIGhvbmVzdC5cblx0XHR9XG5cdH1cblxuXHRkZXRhY2ggKCk6IHZvaWQge1xuXHRcdGZvciAoY29uc3QgZGV0YWNoIG9mIHRoaXMuZGV0YWNoZXJzKSB7XG5cdFx0XHRkZXRhY2goKTtcblx0XHR9XG5cdFx0dGhpcy5kZXRhY2hlcnMgPSBbXTtcblx0fVxuXG5cdHByaXZhdGUgb25FbnRlciAoeyBlZGdlIH06IERpdmVFbnRlclBheWxvYWQpOiB2b2lkIHtcblx0XHRjb25zdCBwYXJlbnRTcGFuID0gdGhpcy5maW5kUGFyZW50U3BhbihlZGdlKTtcblx0XHRjb25zdCBjdHggPSBwYXJlbnRTcGFuXG5cdFx0XHQ/IHRyYWNlLnNldFNwYW4ob3RlbENvbnRleHQuYWN0aXZlKCksIHBhcmVudFNwYW4pXG5cdFx0XHQ6IHVuZGVmaW5lZDtcblx0XHRjb25zdCBuYW1lID0gYGRpdmUuJHtlZGdlLmtpbmR9OiR7ZWRnZS5uYW1lfWA7XG5cdFx0Y29uc3Qgc3BhbiA9IGN0eFxuXHRcdFx0PyB0aGlzLnRyYWNlci5zdGFydFNwYW4obmFtZSwge30sIGN0eClcblx0XHRcdDogdGhpcy50cmFjZXIuc3RhcnRTcGFuKG5hbWUpO1xuXG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUuZWRnZV9pZCcsIGVkZ2UuaWQpO1xuXHRcdHNwYW4uc2V0QXR0cmlidXRlKCdkaXZlLmtpbmQnLCBlZGdlLmtpbmQpO1xuXHRcdHNwYW4uc2V0QXR0cmlidXRlKCdkaXZlLm5hbWUnLCBlZGdlLm5hbWUpO1xuXHRcdHRoaXMuZGVjb3JhdGVTcGFuKHNwYW4sIGVkZ2UpO1xuXG5cdFx0dGhpcy5zcGFucy5zZXQoZWRnZS5pZCwgc3Bhbik7XG5cdH1cblxuXHRwcml2YXRlIG9uTGVhdmUgKHsgZWRnZSwgcmVzdWx0IH06IERpdmVMZWF2ZVBheWxvYWQpOiB2b2lkIHtcblx0XHRpZiAocmVzdWx0IGluc3RhbmNlb2YgUHJvbWlzZSkge1xuXHRcdFx0Ly8gYXN5bmMgd29yazogdGhlIHNwYW4gY2xvc2VzIGF0IHNldHRsZSwgbm90IGF0IHRoZSBzeW5jIGhlYWQg4oCUXG5cdFx0XHQvLyBcInRoZSBmdW5jdGlvbiByZXR1cm5lZFwiIGlzIG5vdCBcInRoZSB3b3JrIGlzIGRvbmVcIlxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLmNsb3NlU3BhbihlZGdlKTtcblx0fVxuXG5cdHByaXZhdGUgb25TZXR0bGUgKHsgZWRnZSwgZXJyb3IgfTogRGl2ZVNldHRsZVBheWxvYWQpOiB2b2lkIHtcblx0XHR0aGlzLmNsb3NlU3BhbihlZGdlLCBlcnJvcik7XG5cdH1cblxuXHRwcml2YXRlIG9uUmVjb250ZXh0ICh7IGVkZ2UsIHByZXZpb3VzQ29udGV4dCwgY29udGV4dCB9OiBEaXZlUmVjb250ZXh0UGF5bG9hZCk6IHZvaWQge1xuXHRcdC8vIE9uZS1zaG90IHNwYW46IHRoZSBvd25lcnNoaXAgdHJhbnNmZXIgaXRzZWxmLCBwYXJlbnRlZCBvbiB0aGUgT0xEXG5cdFx0Ly8gY29udGV4dCdzIHNwYW4gKHRoZSBoYW5kb2ZmIGVkZ2UncyBwYXJlbnRJZCksIHNvIHRoZSB0cmFjZSBzaG93c1xuXHRcdC8vIHdoZXJlIHRoZSBjYWxsYmFjaydzIHN0b3J5IGNyb3NzZWQgZmxvd3MuXG5cdFx0Y29uc3QgcGFyZW50U3BhbiA9IHRoaXMuZmluZFBhcmVudFNwYW4oZWRnZSk7XG5cdFx0Y29uc3QgY3R4ID0gcGFyZW50U3BhblxuXHRcdFx0PyB0cmFjZS5zZXRTcGFuKG90ZWxDb250ZXh0LmFjdGl2ZSgpLCBwYXJlbnRTcGFuKVxuXHRcdFx0OiB1bmRlZmluZWQ7XG5cdFx0Y29uc3QgbmFtZSA9IGBkaXZlLiR7ZWRnZS5raW5kfToke2VkZ2UubmFtZX1gO1xuXHRcdGNvbnN0IHNwYW4gPSBjdHhcblx0XHRcdD8gdGhpcy50cmFjZXIuc3RhcnRTcGFuKG5hbWUsIHt9LCBjdHgpXG5cdFx0XHQ6IHRoaXMudHJhY2VyLnN0YXJ0U3BhbihuYW1lKTtcblxuXHRcdHNwYW4uc2V0QXR0cmlidXRlKCdkaXZlLmVkZ2VfaWQnLCBlZGdlLmlkKTtcblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5raW5kJywgZWRnZS5raW5kKTtcblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5uYW1lJywgZWRnZS5uYW1lKTtcblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5oYW5kb2ZmJywgdHJ1ZSk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUuaGFuZG9mZi5oYWRfcHJldmlvdXMnLCBwcmV2aW91c0NvbnRleHQgIT09IHVuZGVmaW5lZCk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUuaGFuZG9mZi5oYXNfY29udGV4dCcsIGNvbnRleHQgIT09IHVuZGVmaW5lZCk7XG5cdFx0dGhpcy5kZWNvcmF0ZVNwYW4oc3BhbiwgZWRnZSk7XG5cdFx0c3Bhbi5lbmQoKTtcblx0fVxuXG5cdHByaXZhdGUgb25DcmVhdGUgKHsgZWRnZSwgZXJyb3IgfTogRGl2ZUNyZWF0ZVBheWxvYWQpOiB2b2lkIHtcblx0XHQvLyBPbmUtc2hvdCBzcGFuOiB0aGUgY29uc3RydWN0aW9uIGFscmVhZHkgY29tcGxldGVkIHdoZW4gcmVjb3JkQ3JlYXRpb25cblx0XHQvLyBmaXJlZCAodGhlIGhvb2sgbW9tZW50IElTIHRoZSBjb21wbGV0aW9uKSwgc28gdGhlIHNwYW4gc3RhcnRzIGFuZFxuXHRcdC8vIGVuZHMgaGVyZSDigJQgc2FtZSBzaGFwZSBhcyByZWNvbnRleHQuIGZpbmRQYXJlbnRTcGFuIGFkb3B0cyB0aGVcblx0XHQvLyB3cmFwcGVkIGNhbGwncyBzcGFuIHZpYSBlZGdlLnBhcmVudElkLCBvciB0aGUgYWN0aXZlIHJlcXVlc3Qgc3BhbiBhdFxuXHRcdC8vIGEgYm91bmRhcnksIHNvIGNvbnN0cnVjdGlvbnMgam9pbiB0aGUgcmVxdWVzdCB0cmFjZSBpbnN0ZWFkIG9mXG5cdFx0Ly8gb3BlbmluZyBhIHJvb3QgdHJhY2Ugb2YgdGhlaXIgb3duLlxuXHRcdGNvbnN0IHBhcmVudFNwYW4gPSB0aGlzLmZpbmRQYXJlbnRTcGFuKGVkZ2UpO1xuXHRcdGNvbnN0IGN0eCA9IHBhcmVudFNwYW5cblx0XHRcdD8gdHJhY2Uuc2V0U3BhbihvdGVsQ29udGV4dC5hY3RpdmUoKSwgcGFyZW50U3Bhbilcblx0XHRcdDogdW5kZWZpbmVkO1xuXHRcdGNvbnN0IG5hbWUgPSBgZGl2ZS4ke2VkZ2Uua2luZH06JHtlZGdlLm5hbWV9YDtcblx0XHRjb25zdCBzcGFuID0gY3R4XG5cdFx0XHQ/IHRoaXMudHJhY2VyLnN0YXJ0U3BhbihuYW1lLCB7fSwgY3R4KVxuXHRcdFx0OiB0aGlzLnRyYWNlci5zdGFydFNwYW4obmFtZSk7XG5cblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5lZGdlX2lkJywgZWRnZS5pZCk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUua2luZCcsIGVkZ2Uua2luZCk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUubmFtZScsIGVkZ2UubmFtZSk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUuc3RhdHVzJywgZWRnZS5zdGF0dXMpO1xuXHRcdGlmIChlZGdlLmR1cmF0aW9uICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKCdkaXZlLmR1cmF0aW9uX21zJywgZWRnZS5kdXJhdGlvbik7XG5cdFx0fVxuXHRcdHRoaXMuZGVjb3JhdGVTcGFuKHNwYW4sIGVkZ2UpO1xuXHRcdGlmIChlZGdlLnN0YXR1cyA9PT0gJ2Vycm9yJykge1xuXHRcdFx0c3Bhbi5zZXRTdGF0dXMoeyBjb2RlOiBTcGFuU3RhdHVzQ29kZS5FUlJPUiB9KTtcblx0XHRcdGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG5cdFx0XHRcdHNwYW4ucmVjb3JkRXhjZXB0aW9uKGVycm9yKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0c3Bhbi5lbmQoKTtcblx0fVxuXG5cdHByaXZhdGUgZmluZFBhcmVudFNwYW4gKGVkZ2U6IEZsb3dFZGdlKTogU3BhbiB8IHVuZGVmaW5lZCB7XHRcdGlmIChlZGdlLnBhcmVudElkICE9PSBudWxsKSB7XG5cdFx0XHRjb25zdCBvd24gPSB0aGlzLnNwYW5zLmdldChlZGdlLnBhcmVudElkKTtcblx0XHRcdGlmIChvd24pIHtcblx0XHRcdFx0cmV0dXJuIG93bjtcblx0XHRcdH1cblx0XHR9XG5cdFx0Ly8gQm91bmRhcnkgKG9yIGV2aWN0ZWQgcGFyZW50KTogYWRvcHQgdGhlIGFjdGl2ZSBPVGVsIHNwYW4g4oCUIHRoZSBIVFRQXG5cdFx0Ly8gcmVxdWVzdCBzcGFuIGJlY29tZXMgdGhlIHJvb3Qgb2YgdGhlIGRpdmUgYnJhbmNoLlxuXHRcdGNvbnN0IGFjdGl2ZSA9IHRyYWNlLmdldFNwYW4ob3RlbENvbnRleHQuYWN0aXZlKCkpO1xuXHRcdHJldHVybiBhY3RpdmU7XG5cdH1cblxuXHRwcml2YXRlIGNsb3NlU3BhbiAoZWRnZTogRmxvd0VkZ2UsIGVycm9yPzogdW5rbm93bik6IHZvaWQge1xuXHRcdGNvbnN0IHNwYW4gPSB0aGlzLnNwYW5zLmdldChlZGdlLmlkKTtcblx0XHRpZiAoIXNwYW4pIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0dGhpcy5zcGFucy5kZWxldGUoZWRnZS5pZCk7XG5cblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5zdGF0dXMnLCBlZGdlLnN0YXR1cyk7XG5cdFx0aWYgKGVkZ2UuZHVyYXRpb24gIT09IHVuZGVmaW5lZCkge1xuXHRcdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RpdmUuZHVyYXRpb25fbXMnLCBlZGdlLmR1cmF0aW9uKTtcblx0XHR9XG5cdFx0aWYgKGVkZ2Uuc3RhdHVzID09PSAnZXJyb3InKSB7XG5cdFx0XHQvLyBzeW5jIHRocm93cyBjYXJyeSBubyBlcnJvciB2YWx1ZSBpbiB0aGUgbGVhdmUgcGF5bG9hZCDigJQgdGhlXG5cdFx0XHQvLyBlZGdlJ3Mgb3duIHN0YXR1cyBpcyB0aGUgdHJ1dGhmdWwgc2lnbmFsOyB0aGUgZXhjZXB0aW9uIHJlY29yZFxuXHRcdFx0Ly8gaXMgYXZhaWxhYmxlIG9ubHkgd2hlbiBzZXR0bGUgY2FycmllZCB0aGUgcmVqZWN0aW9uIGl0c2VsZlxuXHRcdFx0c3Bhbi5zZXRTdGF0dXMoeyBjb2RlOiBTcGFuU3RhdHVzQ29kZS5FUlJPUiB9KTtcblx0XHRcdGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG5cdFx0XHRcdHNwYW4ucmVjb3JkRXhjZXB0aW9uKGVycm9yKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0c3Bhbi5lbmQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcm9zcy1zdXJmYWNlIGF0dHJpYnV0ZXMgZXZlcnkgc3BhbiBnZXRzLCBvbiBldmVyeSBob29rIHBhdGg6XG5cdCAqIHRoZSBlZGdlJ3MgdHJhY2Ugcm9vdCBpZCAoSmFlZ2VyIGxpbmsg4oaSIG1uZW1vZ3JhcGhpY2EncyBMaXZlIFRyYWNlLFxuXHQgKiBXYW50ZWQgIzEpLCB0aGUgZWRnZUlk4oaSdHJhY2VJZCBwdWJsaWNhdGlvbiBmb3IgdGhlIHN0cmF0ZWd5IHB1c2hcblx0ICogY2hhbm5lbCAoV2FudGVkICMyKSwgYW5kIGNvZGUuZmlsZXBhdGgvbGluZS9jb2x1bW4gcGFyc2VkIGZyb20gdGhlXG5cdCAqIGNhbGxzaXRlIG5hbWUgKFdhbnRlZCAjNCkuXG5cdCAqL1xuXHRwcml2YXRlIGRlY29yYXRlU3BhbiAoc3BhbjogU3BhbiwgZWRnZTogRmxvd0VkZ2UpOiB2b2lkIHtcblx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnZGl2ZS5yb290X2VkZ2VfaWQnLCB0aGlzLnJvb3RFZGdlSWRPZihlZGdlKSk7XG5cdFx0cmVjb3JkRWRnZVRyYWNlKGVkZ2UuaWQsIHNwYW4uc3BhbkNvbnRleHQoKS50cmFjZUlkKTtcblx0XHRpZiAoZWRnZS5raW5kICE9PSAnY2FsbCcgJiYgZWRnZS5raW5kICE9PSAnbWV0aG9kJyAmJiBlZGdlLmtpbmQgIT09ICdjb25zdHJ1Y3QnKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGNvbnN0IG1hdGNoID0gQ0FMTFNJVEVfUkUuZXhlYyhlZGdlLm5hbWUpO1xuXHRcdGlmICghbWF0Y2gpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2NvZGUuZmlsZXBhdGgnLCBtYXRjaFsxXSk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2NvZGUubGluZW5vJywgTnVtYmVyKG1hdGNoWzJdKSk7XG5cdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2NvZGUuY29sdW1uJywgTnVtYmVyKG1hdGNoWzNdKSk7XG5cdH1cblxuXHQvLyBXYWxrIGRpdmUncyBwYXJlbnRhZ2UgdG8gdGhlIHJvb3QgZWRnZSBpZC4gUGFyZW50cyBhcmUgcmVjb3JkZWQgYXNcblx0Ly8gZWRnZXMgYXJyaXZlIChlbnRlciBmaXJlcyBwYXJlbnQtYmVmb3JlLWNoaWxkKSwgc28gdGhlIGNoYWluIGlzXG5cdC8vIGNvbXBsZXRlIGZvciBhbnl0aGluZyBzdGlsbCBpbiBmbGlnaHQ7IGFuIGV2aWN0ZWQvdW5rbm93biBwYXJlbnRcblx0Ly8gc2ltcGx5IGVuZHMgdGhlIHdhbGsgYXQgdGhlIGRlZXBlc3Qga25vd24gaWQuXG5cdHByaXZhdGUgcm9vdEVkZ2VJZE9mIChlZGdlOiBGbG93RWRnZSk6IG51bWJlciB7XG5cdFx0aWYgKHRoaXMuZWRnZVBhcmVudHMuc2l6ZSA+PSBEaXZlT3RlbFByb3ZpZGVyLkVER0VfUEFSRU5UU19MSU1JVCkge1xuXHRcdFx0dGhpcy5lZGdlUGFyZW50cy5jbGVhcigpO1xuXHRcdH1cblx0XHR0aGlzLmVkZ2VQYXJlbnRzLnNldChlZGdlLmlkLCBlZGdlLnBhcmVudElkKTtcblx0XHRsZXQgaWQgPSBlZGdlLmlkO1xuXHRcdGxldCBwYXJlbnQgPSBlZGdlLnBhcmVudElkO1xuXHRcdGNvbnN0IHNlZW4gPSBuZXcgU2V0PG51bWJlcj4oW2lkXSk7XG5cdFx0d2hpbGUgKHBhcmVudCAhPT0gbnVsbCAmJiAhc2Vlbi5oYXMocGFyZW50KSkge1xuXHRcdFx0c2Vlbi5hZGQocGFyZW50KTtcblx0XHRcdGlkID0gcGFyZW50O1xuXHRcdFx0Y29uc3QgbmV4dCA9IHRoaXMuZWRnZVBhcmVudHMuZ2V0KHBhcmVudCk7XG5cdFx0XHRwYXJlbnQgPSBuZXh0ID09PSB1bmRlZmluZWQgPyBudWxsIDogbmV4dDtcblx0XHR9XG5cdFx0cmV0dXJuIGlkO1xuXHR9XG59XG4iXX0=
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tracer, Span } from '@opentelemetry/api';
|
|
2
|
+
import type { TypesCollection } from 'mnemonica/module';
|
|
3
|
+
export declare class MnemonicaOtelProvider {
|
|
4
|
+
private tracer;
|
|
5
|
+
private pendingSpans;
|
|
6
|
+
constructor(tracer?: Tracer);
|
|
7
|
+
getCurrentSpan(): Span | undefined;
|
|
8
|
+
runWithSpan<T>(span: Span, fn: () => T): T;
|
|
9
|
+
attachHooks(collection: TypesCollection): void;
|
|
10
|
+
private findParentSpan;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=mnemonica-otel.provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mnemonica-otel.provider.d.ts","sourceRoot":"","sources":["../../src/providers/mnemonica-otel.provider.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAa,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKnE,qBAAa,qBAAqB;IACjC,OAAO,CAAC,MAAM,CAAS;IAGvB,OAAO,CAAC,YAAY,CAA+B;gBAEtC,MAAM,CAAC,EAAE,MAAM;IAI5B,cAAc,IAAK,IAAI,GAAG,SAAS;IAInC,WAAW,CAAC,CAAC,EAAG,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAI3C,WAAW,CAAE,UAAU,EAAE,eAAe,GAAG,IAAI;IAoF/C,OAAO,CAAC,cAAc;CAsBtB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry provider for mnemonica lifecycle hooks.
|
|
3
|
+
*
|
|
4
|
+
* Creates spans for preCreation / postCreation / creationError,
|
|
5
|
+
* parents them along the prototype chain, and propagates
|
|
6
|
+
* context via AsyncLocalStorage.
|
|
7
|
+
*/
|
|
8
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
9
|
+
import { trace, context as otelContext } from '@opentelemetry/api';
|
|
10
|
+
const SymbolParentSpan = Symbol.for('mnemonica.span.parent');
|
|
11
|
+
const asyncStorage = new AsyncLocalStorage();
|
|
12
|
+
export class MnemonicaOtelProvider {
|
|
13
|
+
tracer;
|
|
14
|
+
// spans of constructions in flight, keyed on the per-call args array
|
|
15
|
+
// (the only value core guarantees identical between pre and post hooks)
|
|
16
|
+
pendingSpans = new WeakMap();
|
|
17
|
+
constructor(tracer) {
|
|
18
|
+
this.tracer = tracer ?? trace.getTracer('@mnemonica/otel');
|
|
19
|
+
}
|
|
20
|
+
getCurrentSpan() {
|
|
21
|
+
return asyncStorage.getStore();
|
|
22
|
+
}
|
|
23
|
+
runWithSpan(span, fn) {
|
|
24
|
+
return asyncStorage.run(span, fn);
|
|
25
|
+
}
|
|
26
|
+
attachHooks(collection) {
|
|
27
|
+
collection.registerHook('preCreation', (hookData) => {
|
|
28
|
+
const parentSpan = this.findParentSpan(hookData);
|
|
29
|
+
const ctx = parentSpan
|
|
30
|
+
? trace.setSpan(otelContext.active(), parentSpan)
|
|
31
|
+
: undefined;
|
|
32
|
+
const span = parentSpan
|
|
33
|
+
? this.tracer.startSpan(`mnemonica.${hookData.TypeName}`, {}, ctx)
|
|
34
|
+
: this.tracer.startSpan(`mnemonica.${hookData.TypeName}`);
|
|
35
|
+
span.setAttribute('mnemonica.type_name', hookData.TypeName);
|
|
36
|
+
span.setAttribute('mnemonica.hook', 'preCreation');
|
|
37
|
+
// Store the pending span keyed on the construction's args array:
|
|
38
|
+
// core passes the identical args reference to preCreation and to
|
|
39
|
+
// postCreation/creationError, and it is unique per construction call.
|
|
40
|
+
// Keying on the parent instance would collide when async constructions
|
|
41
|
+
// of siblings interleave (preA, preB, postB, postA — the second
|
|
42
|
+
// preCreation would overwrite the first).
|
|
43
|
+
this.pendingSpans.set(hookData.args, span);
|
|
44
|
+
});
|
|
45
|
+
collection.registerHook('postCreation', (hookData) => {
|
|
46
|
+
const newInstance = hookData.inheritedInstance;
|
|
47
|
+
if (newInstance == null || typeof newInstance !== 'object') {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Retrieve and remove the pending span for this construction
|
|
51
|
+
let span = this.pendingSpans.get(hookData.args);
|
|
52
|
+
this.pendingSpans.delete(hookData.args);
|
|
53
|
+
if (!span) {
|
|
54
|
+
// Fallback: create span here if preCreation didn't (shouldn't happen)
|
|
55
|
+
span = this.tracer.startSpan(`mnemonica.${hookData.TypeName}`);
|
|
56
|
+
span.setAttribute('mnemonica.type_name', hookData.TypeName);
|
|
57
|
+
}
|
|
58
|
+
span.setAttribute('mnemonica.hook', 'postCreation');
|
|
59
|
+
span.end();
|
|
60
|
+
// Store on new instance so its children can find the parent span
|
|
61
|
+
Object.defineProperty(newInstance, SymbolParentSpan, {
|
|
62
|
+
value: span,
|
|
63
|
+
configurable: true,
|
|
64
|
+
enumerable: false,
|
|
65
|
+
writable: true,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
collection.registerHook('creationError', (hookData) => {
|
|
69
|
+
const newInstance = hookData.inheritedInstance;
|
|
70
|
+
if (newInstance == null || typeof newInstance !== 'object') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Retrieve and remove the pending span for this construction
|
|
74
|
+
let span = this.pendingSpans.get(hookData.args);
|
|
75
|
+
this.pendingSpans.delete(hookData.args);
|
|
76
|
+
if (!span) {
|
|
77
|
+
span = this.tracer.startSpan(`mnemonica.${hookData.TypeName}`);
|
|
78
|
+
span.setAttribute('mnemonica.type_name', hookData.TypeName);
|
|
79
|
+
}
|
|
80
|
+
span.setAttribute('mnemonica.hook', 'creationError');
|
|
81
|
+
span.setAttribute('error.type', 'Error');
|
|
82
|
+
span.recordException(newInstance);
|
|
83
|
+
span.end();
|
|
84
|
+
// Store on error instance for chain tracing
|
|
85
|
+
Object.defineProperty(newInstance, SymbolParentSpan, {
|
|
86
|
+
value: span,
|
|
87
|
+
configurable: true,
|
|
88
|
+
enumerable: false,
|
|
89
|
+
writable: true,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
findParentSpan(hookData) {
|
|
94
|
+
const stored = asyncStorage.getStore();
|
|
95
|
+
if (stored) {
|
|
96
|
+
return stored;
|
|
97
|
+
}
|
|
98
|
+
const parent = hookData.existentInstance;
|
|
99
|
+
if (parent == null || typeof parent !== 'object') {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let current = parent;
|
|
103
|
+
while (current) {
|
|
104
|
+
const span = current[SymbolParentSpan];
|
|
105
|
+
if (span) {
|
|
106
|
+
return span;
|
|
107
|
+
}
|
|
108
|
+
current = Object.getPrototypeOf(current);
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW5lbW9uaWNhLW90ZWwucHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXJzL21uZW1vbmljYS1vdGVsLnByb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUNILE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVoRCxPQUFPLEVBQUUsS0FBSyxFQUFFLE9BQU8sSUFBSSxXQUFXLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUduRSxNQUFNLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsdUJBQXVCLENBQUMsQ0FBQztBQUM3RCxNQUFNLFlBQVksR0FBRyxJQUFJLGlCQUFpQixFQUFRLENBQUM7QUFFbkQsTUFBTSxPQUFPLHFCQUFxQjtJQUN6QixNQUFNLENBQVM7SUFDdkIscUVBQXFFO0lBQ3JFLHdFQUF3RTtJQUNoRSxZQUFZLEdBQUcsSUFBSSxPQUFPLEVBQWdCLENBQUM7SUFFbkQsWUFBYSxNQUFlO1FBQzNCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxJQUFJLEtBQUssQ0FBQyxTQUFTLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRUQsY0FBYztRQUNiLE9BQU8sWUFBWSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFRCxXQUFXLENBQUssSUFBVSxFQUFFLEVBQVc7UUFDdEMsT0FBTyxZQUFZLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsV0FBVyxDQUFFLFVBQTJCO1FBQ3ZDLFVBQVUsQ0FBQyxZQUFZLENBQUMsYUFBYSxFQUFFLENBQUMsUUFBbUIsRUFBRSxFQUFFO1lBQzlELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDakQsTUFBTSxHQUFHLEdBQUcsVUFBVTtnQkFDckIsQ0FBQyxD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LE1BQU0sQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUMsQ0FBQztRQUVELE9BQU8sU0FBUyxDQUFDO0lBQ2xCLENBQUM7Q0FDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3BlblRlbGVtZXRyeSBwcm92aWRlciBmb3IgbW5lbW9uaWNhIGxpZmVjeWNsZSBob29rcy5cbiAqXG4gKiBDcmVhdGVzIHNwYW5zIGZvciBwcmVDcmVhdGlvbiAvIHBvc3RDcmVhdGlvbiAvIGNyZWF0aW9uRXJyb3IsXG4gKiBwYXJlbnRzIHRoZW0gYWxvbmcgdGhlIHByb3RvdHlwZSBjaGFpbiwgYW5kIHByb3BhZ2F0ZXNcbiAqIGNvbnRleHQgdmlhIEFzeW5jTG9jYWxTdG9yYWdlLlxuICovXG5pbXBvcnQgeyBBc3luY0xvY2FsU3RvcmFnZSB9IGZyb20gJ2FzeW5jX2hvb2tzJztcbmltcG9ydCB0eXBlIHsgVHJhY2VyLCBTcGFuIH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvYXBpJztcbmltcG9ydCB7IHRyYWNlLCBjb250ZXh0IGFzIG90ZWxDb250ZXh0IH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvYXBpJztcbmltcG9ydCB0eXBlIHsgaG9va3NPcHRzLCBUeXBlc0NvbGxlY3Rpb24gfSBmcm9tICdtbmVtb25pY2EvbW9kdWxlJztcblxuY29uc3QgU3ltYm9sUGFyZW50U3BhbiA9IFN5bWJvbC5mb3IoJ21uZW1vbmljYS5zcGFuLnBhcmVudCcpO1xuY29uc3QgYXN5bmNTdG9yYWdlID0gbmV3IEFzeW5jTG9jYWxTdG9yYWdlPFNwYW4+KCk7XG5cbmV4cG9ydCBjbGFzcyBNbmVtb25pY2FPdGVsUHJvdmlkZXIge1xuXHRwcml2YXRlIHRyYWNlcjogVHJhY2VyO1xuXHQvLyBzcGFucyBvZiBjb25zdHJ1Y3Rpb25zIGluIGZsaWdodCwga2V5ZWQgb24gdGhlIHBlci1jYWxsIGFyZ3MgYXJyYXlcblx0Ly8gKHRoZSBvbmx5IHZhbHVlIGNvcmUgZ3VhcmFudGVlcyBpZGVudGljYWwgYmV0d2VlbiBwcmUgYW5kIHBvc3QgaG9va3MpXG5cdHByaXZhdGUgcGVuZGluZ1NwYW5zID0gbmV3IFdlYWtNYXA8b2JqZWN0LCBTcGFuPigpO1xuXG5cdGNvbnN0cnVjdG9yICh0cmFjZXI/OiBUcmFjZXIpIHtcblx0XHR0aGlzLnRyYWNlciA9IHRyYWNlciA/PyB0cmFjZS5nZXRUcmFjZXIoJ0BtbmVtb25pY2Evb3RlbCcpO1xuXHR9XG5cblx0Z2V0Q3VycmVudFNwYW4gKCk6IFNwYW4gfCB1bmRlZmluZWQge1xuXHRcdHJldHVybiBhc3luY1N0b3JhZ2UuZ2V0U3RvcmUoKTtcblx0fVxuXG5cdHJ1bldpdGhTcGFuPFQ+IChzcGFuOiBTcGFuLCBmbjogKCkgPT4gVCk6IFQge1xuXHRcdHJldHVybiBhc3luY1N0b3JhZ2UucnVuKHNwYW4sIGZuKTtcblx0fVxuXG5cdGF0dGFjaEhvb2tzIChjb2xsZWN0aW9uOiBUeXBlc0NvbGxlY3Rpb24pOiB2b2lkIHtcblx0XHRjb2xsZWN0aW9uLnJlZ2lzdGVySG9vaygncHJlQ3JlYXRpb24nLCAoaG9va0RhdGE6IGhvb2tzT3B0cykgPT4ge1xuXHRcdFx0Y29uc3QgcGFyZW50U3BhbiA9IHRoaXMuZmluZFBhcmVudFNwYW4oaG9va0RhdGEpO1xuXHRcdFx0Y29uc3QgY3R4ID0gcGFyZW50U3BhblxuXHRcdFx0XHQ/IHRyYWNlLnNldFNwYW4ob3RlbENvbnRleHQuYWN0aXZlKCksIHBhcmVudFNwYW4pXG5cdFx0XHRcdDogdW5kZWZpbmVkO1xuXHRcdFx0Y29uc3Qgc3BhbiA9IHBhcmVudFNwYW5cblx0XHRcdFx0PyB0aGlzLnRyYWNlci5zdGFydFNwYW4oXG5cdFx0XHRcdFx0YG1uZW1vbmljYS4ke2hvb2tEYXRhLlR5cGVOYW1lfWAsXG5cdFx0XHRcdFx0e30sXG5cdFx0XHRcdFx0Y3R4XG5cdFx0XHRcdClcblx0XHRcdFx0OiB0aGlzLnRyYWNlci5zdGFydFNwYW4oYG1uZW1vbmljYS4ke2hvb2tEYXRhLlR5cGVOYW1lfWApO1xuXG5cdFx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnbW5lbW9uaWNhLnR5cGVfbmFtZScsIGhvb2tEYXRhLlR5cGVOYW1lKTtcblx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKCdtbmVtb25pY2EuaG9vaycsICdwcmVDcmVhdGlvbicpO1xuXG5cdFx0XHQvLyBTdG9yZSB0aGUgcGVuZGluZyBzcGFuIGtleWVkIG9uIHRoZSBjb25zdHJ1Y3Rpb24ncyBhcmdzIGFycmF5OlxuXHRcdFx0Ly8gY29yZSBwYXNzZXMgdGhlIGlkZW50aWNhbCBhcmdzIHJlZmVyZW5jZSB0byBwcmVDcmVhdGlvbiBhbmQgdG9cblx0XHRcdC8vIHBvc3RDcmVhdGlvbi9jcmVhdGlvbkVycm9yLCBhbmQgaXQgaXMgdW5pcXVlIHBlciBjb25zdHJ1Y3Rpb24gY2FsbC5cblx0XHRcdC8vIEtleWluZyBvbiB0aGUgcGFyZW50IGluc3RhbmNlIHdvdWxkIGNvbGxpZGUgd2hlbiBhc3luYyBjb25zdHJ1Y3Rpb25zXG5cdFx0XHQvLyBvZiBzaWJsaW5ncyBpbnRlcmxlYXZlIChwcmVBLCBwcmVCLCBwb3N0QiwgcG9zdEEg4oCUIHRoZSBzZWNvbmRcblx0XHRcdC8vIHByZUNyZWF0aW9uIHdvdWxkIG92ZXJ3cml0ZSB0aGUgZmlyc3QpLlxuXHRcdFx0dGhpcy5wZW5kaW5nU3BhbnMuc2V0KGhvb2tEYXRhLmFyZ3MsIHNwYW4pO1xuXHRcdH0pO1xuXG5cdFx0Y29sbGVjdGlvbi5yZWdpc3Rlckhvb2soJ3Bvc3RDcmVhdGlvbicsIChob29rRGF0YTogaG9va3NPcHRzKSA9PiB7XG5cdFx0XHRjb25zdCBuZXdJbnN0YW5jZSA9IGhvb2tEYXRhLmluaGVyaXRlZEluc3RhbmNlO1xuXHRcdFx0aWYgKG5ld0luc3RhbmNlID09IG51bGwgfHwgdHlwZW9mIG5ld0luc3RhbmNlICE9PSAnb2JqZWN0Jykge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIFJldHJpZXZlIGFuZCByZW1vdmUgdGhlIHBlbmRpbmcgc3BhbiBmb3IgdGhpcyBjb25zdHJ1Y3Rpb25cblx0XHRcdGxldCBzcGFuID0gdGhpcy5wZW5kaW5nU3BhbnMuZ2V0KGhvb2tEYXRhLmFyZ3MpO1xuXHRcdFx0dGhpcy5wZW5kaW5nU3BhbnMuZGVsZXRlKGhvb2tEYXRhLmFyZ3MpO1xuXG5cdFx0XHRpZiAoIXNwYW4pIHtcblx0XHRcdFx0Ly8gRmFsbGJhY2s6IGNyZWF0ZSBzcGFuIGhlcmUgaWYgcHJlQ3JlYXRpb24gZGlkbid0IChzaG91bGRuJ3QgaGFwcGVuKVxuXHRcdFx0XHRzcGFuID0gdGhpcy50cmFjZXIuc3RhcnRTcGFuKGBtbmVtb25pY2EuJHtob29rRGF0YS5UeXBlTmFtZX1gKTtcblx0XHRcdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ21uZW1vbmljYS50eXBlX25hbWUnLCBob29rRGF0YS5UeXBlTmFtZSk7XG5cdFx0XHR9XG5cblx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKCdtbmVtb25pY2EuaG9vaycsICdwb3N0Q3JlYXRpb24nKTtcblx0XHRcdHNwYW4uZW5kKCk7XG5cblx0XHRcdC8vIFN0b3JlIG9uIG5ldyBpbnN0YW5jZSBzbyBpdHMgY2hpbGRyZW4gY2FuIGZpbmQgdGhlIHBhcmVudCBzcGFuXG5cdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobmV3SW5zdGFuY2UsIFN5bWJvbFBhcmVudFNwYW4sIHtcblx0XHRcdFx0dmFsdWUgICAgICAgOiBzcGFuLFxuXHRcdFx0XHRjb25maWd1cmFibGU6IHRydWUsXG5cdFx0XHRcdGVudW1lcmFibGUgIDogZmFsc2UsXG5cdFx0XHRcdHdyaXRhYmxlICAgIDogdHJ1ZSxcblx0XHRcdH0pO1xuXHRcdH0pO1xuXG5cdFx0Y29sbGVjdGlvbi5yZWdpc3Rlckhvb2soJ2NyZWF0aW9uRXJyb3InLCAoaG9va0RhdGE6IGhvb2tzT3B0cykgPT4ge1xuXHRcdFx0Y29uc3QgbmV3SW5zdGFuY2UgPSBob29rRGF0YS5pbmhlcml0ZWRJbnN0YW5jZTtcblx0XHRcdGlmIChuZXdJbnN0YW5jZSA9PSBudWxsIHx8IHR5cGVvZiBuZXdJbnN0YW5jZSAhPT0gJ29iamVjdCcpIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBSZXRyaWV2ZSBhbmQgcmVtb3ZlIHRoZSBwZW5kaW5nIHNwYW4gZm9yIHRoaXMgY29uc3RydWN0aW9uXG5cdFx0XHRsZXQgc3BhbiA9IHRoaXMucGVuZGluZ1NwYW5zLmdldChob29rRGF0YS5hcmdzKTtcblx0XHRcdHRoaXMucGVuZGluZ1NwYW5zLmRlbGV0ZShob29rRGF0YS5hcmdzKTtcblxuXHRcdFx0aWYgKCFzcGFuKSB7XG5cdFx0XHRcdHNwYW4gPSB0aGlzLnRyYWNlci5zdGFydFNwYW4oYG1uZW1vbmljYS4ke2hvb2tEYXRhLlR5cGVOYW1lfWApO1xuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZSgnbW5lbW9uaWNhLnR5cGVfbmFtZScsIGhvb2tEYXRhLlR5cGVOYW1lKTtcblx0XHRcdH1cblxuXHRcdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ21uZW1vbmljYS5ob29rJywgJ2NyZWF0aW9uRXJyb3InKTtcblx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKCdlcnJvci50eXBlJywgJ0Vycm9yJyk7XG5cdFx0XHRzcGFuLnJlY29yZEV4Y2VwdGlvbihuZXdJbnN0YW5jZSBhcyBFcnJvcik7XG5cdFx0XHRzcGFuLmVuZCgpO1xuXG5cdFx0XHQvLyBTdG9yZSBvbiBlcnJvciBpbnN0YW5jZSBmb3IgY2hhaW4gdHJhY2luZ1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KG5ld0luc3RhbmNlLCBTeW1ib2xQYXJlbnRTcGFuLCB7XG5cdFx0XHRcdHZhbHVlICAgICAgIDogc3Bhbixcblx0XHRcdFx0Y29uZmlndXJhYmxlOiB0cnVlLFxuXHRcdFx0XHRlbnVtZXJhYmxlICA6IGZhbHNlLFxuXHRcdFx0XHR3cml0YWJsZSAgICA6IHRydWUsXG5cdFx0XHR9KTtcblx0XHR9KTtcblx0fVxuXG5cdHByaXZhdGUgZmluZFBhcmVudFNwYW4gKGhvb2tEYXRhOiBob29rc09wdHMpOiBTcGFuIHwgdW5kZWZpbmVkIHtcblx0XHRjb25zdCBzdG9yZWQgPSBhc3luY1N0b3JhZ2UuZ2V0U3RvcmUoKTtcblx0XHRpZiAoc3RvcmVkKSB7XG5cdFx0XHRyZXR1cm4gc3RvcmVkO1xuXHRcdH1cblxuXHRcdGNvbnN0IHBhcmVudCA9IGhvb2tEYXRhLmV4aXN0ZW50SW5zdGFuY2U7XG5cdFx0aWYgKHBhcmVudCA9PSBudWxsIHx8IHR5cGVvZiBwYXJlbnQgIT09ICdvYmplY3QnKSB7XG5cdFx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHRcdH1cblxuXHRcdGxldCBjdXJyZW50OiBvYmplY3QgfCBudWxsID0gcGFyZW50O1xuXHRcdHdoaWxlIChjdXJyZW50KSB7XG5cdFx0XHRjb25zdCBzcGFuID0gKGN1cnJlbnQgYXMgUmVjb3JkPHN5bWJvbCwgdW5rbm93bj4pW1N5bWJvbFBhcmVudFNwYW5dIGFzIFNwYW4gfCB1bmRlZmluZWQ7XG5cdFx0XHRpZiAoc3Bhbikge1xuXHRcdFx0XHRyZXR1cm4gc3Bhbjtcblx0XHRcdH1cblx0XHRcdGN1cnJlbnQgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3VycmVudCk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHVuZGVmaW5lZDtcblx0fVxufVxuIl19
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP request scope — the framework-free half of the adapter's trace
|
|
3
|
+
* middleware: one OTel span per HTTP request, visible to mnemonica hooks
|
|
4
|
+
* through the provider's own ALS AND to dive spans through the OTEL global
|
|
5
|
+
* context, with the async-flow root frame outermost when provided.
|
|
6
|
+
*
|
|
7
|
+
* Framework mapping notes:
|
|
8
|
+
* - Express-style middleware: call with (req, res, deps, () => next()).
|
|
9
|
+
* - Fastify: onRequest hook — call with (request.raw, reply.raw, deps,
|
|
10
|
+
* () => done()); reply.raw is the underlying ServerResponse.
|
|
11
|
+
* - raw http: wrap the request listener; ServerResponse emits 'finish'.
|
|
12
|
+
*/
|
|
13
|
+
import type { Tracer } from '@opentelemetry/api';
|
|
14
|
+
import { MnemonicaOtelProvider } from './providers/mnemonica-otel.provider.js';
|
|
15
|
+
import { AsyncFlowProvider } from './providers/async-flow.provider.js';
|
|
16
|
+
/** Structural minimum of the request. */
|
|
17
|
+
export interface HttpRequestLike {
|
|
18
|
+
method: string;
|
|
19
|
+
url: string;
|
|
20
|
+
/** Express exposes the matched route as req.route.path */
|
|
21
|
+
route?: {
|
|
22
|
+
path?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Structural minimum of the response: a 'finish' event + statusCode. */
|
|
26
|
+
export interface HttpResponseLike {
|
|
27
|
+
statusCode: number;
|
|
28
|
+
on(event: 'finish', listener: () => void): unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface RequestScopeDeps {
|
|
31
|
+
tracer: Tracer;
|
|
32
|
+
otel: MnemonicaOtelProvider;
|
|
33
|
+
asyncFlow?: AsyncFlowProvider;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Start the request span, hang its ending on response 'finish', and run
|
|
37
|
+
* `fn` inside the triple scope (provider ALS + OTEL global context +
|
|
38
|
+
* async-flow root frame). `fn` is the framework's "continue" callback.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runInRequestScope(req: HttpRequestLike, res: HttpResponseLike, deps: RequestScopeDeps, fn: () => void): void;
|
|
41
|
+
//# sourceMappingURL=request-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-scope.d.ts","sourceRoot":"","sources":["../src/request-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAG,MAAM,CAAC;IAChB,GAAG,EAAM,MAAM,CAAC;IAChB,0DAA0D;IAC1D,KAAK,CAAC,EAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3B;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,UAAU,EAAG,MAAM,CAAC;IACpB,EAAE,CAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACpD;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAO,MAAM,CAAC;IACpB,IAAI,EAAS,qBAAqB,CAAC;IACnC,SAAS,CAAC,EAAG,iBAAiB,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,EAAI,eAAe,EACtB,GAAG,EAAI,gBAAgB,EACvB,IAAI,EAAG,gBAAgB,EACvB,EAAE,EAAK,MAAM,IAAI,GACf,IAAI,CA6BN"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { context as otelContext, trace } from '@opentelemetry/api';
|
|
2
|
+
/**
|
|
3
|
+
* Start the request span, hang its ending on response 'finish', and run
|
|
4
|
+
* `fn` inside the triple scope (provider ALS + OTEL global context +
|
|
5
|
+
* async-flow root frame). `fn` is the framework's "continue" callback.
|
|
6
|
+
*/
|
|
7
|
+
export function runInRequestScope(req, res, deps, fn) {
|
|
8
|
+
const route = req.route?.path ?? req.url;
|
|
9
|
+
const span = deps.tracer.startSpan(`HTTP ${req.method} ${route}`);
|
|
10
|
+
span.setAttribute('http.method', req.method);
|
|
11
|
+
span.setAttribute('http.url', req.url);
|
|
12
|
+
res.on('finish', () => {
|
|
13
|
+
span.setAttribute('http.status_code', res.statusCode);
|
|
14
|
+
span.end();
|
|
15
|
+
});
|
|
16
|
+
// runWithSpan covers the mnemonica-hook side (the provider's own ALS);
|
|
17
|
+
// the OTEL global context is what DiveOtelProvider reads when it looks
|
|
18
|
+
// for a parent span at wrap boundaries — without this second entry
|
|
19
|
+
// dive spans never adopt the request span and stay root traces.
|
|
20
|
+
// The async-flow root frame (when provided) is the outermost scope:
|
|
21
|
+
// every async hop of this request — wrapped or not — inherits it via
|
|
22
|
+
// ALS propagation.
|
|
23
|
+
const activeCtx = trace.setSpan(otelContext.active(), span);
|
|
24
|
+
const run = () => {
|
|
25
|
+
deps.otel.runWithSpan(span, () => {
|
|
26
|
+
otelContext.with(activeCtx, fn);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
if (deps.asyncFlow) {
|
|
30
|
+
deps.asyncFlow.runInScope(run);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
run();
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWVzdC1zY29wZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9yZXF1ZXN0LXNjb3BlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWFBLE9BQU8sRUFBRSxPQUFPLElBQUksV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBd0JuRTs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUNoQyxHQUFzQixFQUN0QixHQUF1QixFQUN2QixJQUF1QixFQUN2QixFQUFpQjtJQUVqQixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDO0lBQ3pDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsR0FBRyxDQUFDLE1BQU0sSUFBSSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ2xFLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM3QyxJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFdkMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFO1FBQ3JCLElBQUksQ0FBQyxZQUFZLENBQUMsa0JBQWtCLEVBQUUsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNaLENBQUMsQ0FBQyxDQUFDO0lBRUgsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxtRUFBbUU7SUFDbkUsZ0VBQWdFO0lBQ2hFLG9FQUFvRTtJQUNwRSxxRUFBcUU7SUFDckUsbUJBQW1CO0lBQ25CLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzVELE1BQU0sR0FBRyxHQUFHLEdBQVMsRUFBRTtRQUN0QixJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFO1lBQ2hDLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQyxDQUFDO0lBQ0YsSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0IsT0FBTztJQUNSLENBQUM7SUFDRCxHQUFHLEVBQUUsQ0FBQztBQUNQLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEhUVFAgcmVxdWVzdCBzY29wZSDigJQgdGhlIGZyYW1ld29yay1mcmVlIGhhbGYgb2YgdGhlIGFkYXB0ZXIncyB0cmFjZVxuICogbWlkZGxld2FyZTogb25lIE9UZWwgc3BhbiBwZXIgSFRUUCByZXF1ZXN0LCB2aXNpYmxlIHRvIG1uZW1vbmljYSBob29rc1xuICogdGhyb3VnaCB0aGUgcHJvdmlkZXIncyBvd24gQUxTIEFORCB0byBkaXZlIHNwYW5zIHRocm91Z2ggdGhlIE9URUwgZ2xvYmFsXG4gKiBjb250ZXh0LCB3aXRoIHRoZSBhc3luYy1mbG93IHJvb3QgZnJhbWUgb3V0ZXJtb3N0IHdoZW4gcHJvdmlkZWQuXG4gKlxuICogRnJhbWV3b3JrIG1hcHBpbmcgbm90ZXM6XG4gKiAgLSBFeHByZXNzLXN0eWxlIG1pZGRsZXdhcmU6IGNhbGwgd2l0aCAocmVxLCByZXMsIGRlcHMsICgpID0+IG5leHQoKSkuXG4gKiAgLSBGYXN0aWZ5OiBvblJlcXVlc3QgaG9vayDigJQgY2FsbCB3aXRoIChyZXF1ZXN0LnJhdywgcmVwbHkucmF3LCBkZXBzLFxuICogICAgKCkgPT4gZG9uZSgpKTsgcmVwbHkucmF3IGlzIHRoZSB1bmRlcmx5aW5nIFNlcnZlclJlc3BvbnNlLlxuICogIC0gcmF3IGh0dHA6IHdyYXAgdGhlIHJlcXVlc3QgbGlzdGVuZXI7IFNlcnZlclJlc3BvbnNlIGVtaXRzICdmaW5pc2gnLlxuICovXG5pbXBvcnQgdHlwZSB7IFRyYWNlciB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L2FwaSc7XG5pbXBvcnQgeyBjb250ZXh0IGFzIG90ZWxDb250ZXh0LCB0cmFjZSB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L2FwaSc7XG5pbXBvcnQgeyBNbmVtb25pY2FPdGVsUHJvdmlkZXIgfSBmcm9tICcuL3Byb3ZpZGVycy9tbmVtb25pY2Etb3RlbC5wcm92aWRlci5qcyc7XG5pbXBvcnQgeyBBc3luY0Zsb3dQcm92aWRlciB9IGZyb20gJy4vcHJvdmlkZXJzL2FzeW5jLWZsb3cucHJvdmlkZXIuanMnO1xuXG4vKiogU3RydWN0dXJhbCBtaW5pbXVtIG9mIHRoZSByZXF1ZXN0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBIdHRwUmVxdWVzdExpa2Uge1xuXHRtZXRob2QgOiBzdHJpbmc7XG5cdHVybCAgICA6IHN0cmluZztcblx0LyoqIEV4cHJlc3MgZXhwb3NlcyB0aGUgbWF0Y2hlZCByb3V0ZSBhcyByZXEucm91dGUucGF0aCAqL1xuXHRyb3V0ZT8gOiB7IHBhdGg/OiBzdHJpbmcgfTtcbn1cblxuLyoqIFN0cnVjdHVyYWwgbWluaW11bSBvZiB0aGUgcmVzcG9uc2U6IGEgJ2ZpbmlzaCcgZXZlbnQgKyBzdGF0dXNDb2RlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBIdHRwUmVzcG9uc2VMaWtlIHtcblx0c3RhdHVzQ29kZSA6IG51bWJlcjtcblx0b24gKGV2ZW50OiAnZmluaXNoJywgbGlzdGVuZXI6ICgpID0+IHZvaWQpOiB1bmtub3duO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlcXVlc3RTY29wZURlcHMge1xuXHR0cmFjZXIgICAgIDogVHJhY2VyO1xuXHRvdGVsICAgICAgIDogTW5lbW9uaWNhT3RlbFByb3ZpZGVyO1xuXHRhc3luY0Zsb3c/IDogQXN5bmNGbG93UHJvdmlkZXI7XG59XG5cbi8qKlxuICogU3RhcnQgdGhlIHJlcXVlc3Qgc3BhbiwgaGFuZyBpdHMgZW5kaW5nIG9uIHJlc3BvbnNlICdmaW5pc2gnLCBhbmQgcnVuXG4gKiBgZm5gIGluc2lkZSB0aGUgdHJpcGxlIHNjb3BlIChwcm92aWRlciBBTFMgKyBPVEVMIGdsb2JhbCBjb250ZXh0ICtcbiAqIGFzeW5jLWZsb3cgcm9vdCBmcmFtZSkuIGBmbmAgaXMgdGhlIGZyYW1ld29yaydzIFwiY29udGludWVcIiBjYWxsYmFjay5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJ1bkluUmVxdWVzdFNjb3BlIChcblx0cmVxICA6IEh0dHBSZXF1ZXN0TGlrZSxcblx0cmVzICA6IEh0dHBSZXNwb25zZUxpa2UsXG5cdGRlcHMgOiBSZXF1ZXN0U2NvcGVEZXBzLFxuXHRmbiAgIDogKCkgPT4gdm9pZCxcbik6IHZvaWQge1xuXHRjb25zdCByb3V0ZSA9IHJlcS5yb3V0ZT8ucGF0aCA/PyByZXEudXJsO1xuXHRjb25zdCBzcGFuID0gZGVwcy50cmFjZXIuc3RhcnRTcGFuKGBIVFRQICR7cmVxLm1ldGhvZH0gJHtyb3V0ZX1gKTtcblx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2h0dHAubWV0aG9kJywgcmVxLm1ldGhvZCk7XG5cdHNwYW4uc2V0QXR0cmlidXRlKCdodHRwLnVybCcsIHJlcS51cmwpO1xuXG5cdHJlcy5vbignZmluaXNoJywgKCkgPT4ge1xuXHRcdHNwYW4uc2V0QXR0cmlidXRlKCdodHRwLnN0YXR1c19jb2RlJywgcmVzLnN0YXR1c0NvZGUpO1xuXHRcdHNwYW4uZW5kKCk7XG5cdH0pO1xuXG5cdC8vIHJ1bldpdGhTcGFuIGNvdmVycyB0aGUgbW5lbW9uaWNhLWhvb2sgc2lkZSAodGhlIHByb3ZpZGVyJ3Mgb3duIEFMUyk7XG5cdC8vIHRoZSBPVEVMIGdsb2JhbCBjb250ZXh0IGlzIHdoYXQgRGl2ZU90ZWxQcm92aWRlciByZWFkcyB3aGVuIGl0IGxvb2tzXG5cdC8vIGZvciBhIHBhcmVudCBzcGFuIGF0IHdyYXAgYm91bmRhcmllcyDigJQgd2l0aG91dCB0aGlzIHNlY29uZCBlbnRyeVxuXHQvLyBkaXZlIHNwYW5zIG5ldmVyIGFkb3B0IHRoZSByZXF1ZXN0IHNwYW4gYW5kIHN0YXkgcm9vdCB0cmFjZXMuXG5cdC8vIFRoZSBhc3luYy1mbG93IHJvb3QgZnJhbWUgKHdoZW4gcHJvdmlkZWQpIGlzIHRoZSBvdXRlcm1vc3Qgc2NvcGU6XG5cdC8vIGV2ZXJ5IGFzeW5jIGhvcCBvZiB0aGlzIHJlcXVlc3Qg4oCUIHdyYXBwZWQgb3Igbm90IOKAlCBpbmhlcml0cyBpdCB2aWFcblx0Ly8gQUxTIHByb3BhZ2F0aW9uLlxuXHRjb25zdCBhY3RpdmVDdHggPSB0cmFjZS5zZXRTcGFuKG90ZWxDb250ZXh0LmFjdGl2ZSgpLCBzcGFuKTtcblx0Y29uc3QgcnVuID0gKCk6IHZvaWQgPT4ge1xuXHRcdGRlcHMub3RlbC5ydW5XaXRoU3BhbihzcGFuLCAoKSA9PiB7XG5cdFx0XHRvdGVsQ29udGV4dC53aXRoKGFjdGl2ZUN0eCwgZm4pO1xuXHRcdH0pO1xuXHR9O1xuXHRpZiAoZGVwcy5hc3luY0Zsb3cpIHtcblx0XHRkZXBzLmFzeW5jRmxvdy5ydW5JblNjb3BlKHJ1bik7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdHJ1bigpO1xufVxuIl19
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Structural minimum of an HTTP request any framework can satisfy. */
|
|
2
|
+
export interface RequestLike {
|
|
3
|
+
method: string;
|
|
4
|
+
url: string;
|
|
5
|
+
params?: unknown;
|
|
6
|
+
query?: unknown;
|
|
7
|
+
body?: unknown;
|
|
8
|
+
headers?: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface FeedPreRootOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Link the raw request object into the pre-root record (`raw.request`)
|
|
13
|
+
* AND stamp it as a correlation key, so getPreRoot(req) resolves from
|
|
14
|
+
* anywhere the request is reachable — e.g. an error boundary holding
|
|
15
|
+
* only the request. Retention is unchanged: the record dies with the
|
|
16
|
+
* request.
|
|
17
|
+
*/
|
|
18
|
+
storeRequest?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function feedPreRootFromRequest(req: RequestLike, options?: FeedPreRootOptions): void;
|
|
21
|
+
//# sourceMappingURL=feed-from-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feed-from-request.d.ts","sourceRoot":"","sources":["../../src/thunderstruck/feed-from-request.ts"],"names":[],"mappings":"AAoBA,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAK,MAAM,CAAC;IAClB,GAAG,EAAQ,MAAM,CAAC;IAClB,MAAM,CAAC,EAAI,OAAO,CAAC;IACnB,KAAK,CAAC,EAAK,OAAO,CAAC;IACnB,IAAI,CAAC,EAAM,OAAO,CAAC;IACnB,OAAO,CAAC,EAAG,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,sBAAsB,CAAE,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAa5F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thunderstruck boundary helper — the framework-free half of what a
|
|
3
|
+
* framework's interceptor/middleware does: shape any HTTP framework's
|
|
4
|
+
* request into the
|
|
5
|
+
* raw pre-root payload and stamp its part objects, so pipes and construct
|
|
6
|
+
* handlers correlate the payload by OBJECT IDENTITY (see pre-root.ts for
|
|
7
|
+
* the store's lifetime and correlation semantics).
|
|
8
|
+
*
|
|
9
|
+
* Framework mapping notes:
|
|
10
|
+
* - Express: pass `req` directly — method/url/params(→req.params)/
|
|
11
|
+
* query/body/headers all sit on it.
|
|
12
|
+
* - Fastify: pass the FastifyRequest — it exposes params/query/body/
|
|
13
|
+
* headers; `method`/`url` exist on it since Fastify v4 (fall back to
|
|
14
|
+
* `req.raw` if you target older majors).
|
|
15
|
+
* - raw http: `url`/`method` are on IncomingMessage; body/query/params
|
|
16
|
+
* are yours to parse first (or omit — primitives and missing parts
|
|
17
|
+
* simply don't correlate, the feed still records).
|
|
18
|
+
*/
|
|
19
|
+
import { feedPreRoot } from './pre-root.js';
|
|
20
|
+
export function feedPreRootFromRequest(req, options) {
|
|
21
|
+
const raw = {
|
|
22
|
+
method: req.method,
|
|
23
|
+
url: req.url,
|
|
24
|
+
params: req.params,
|
|
25
|
+
query: req.query,
|
|
26
|
+
body: req.body,
|
|
27
|
+
headers: req.headers,
|
|
28
|
+
};
|
|
29
|
+
if (options?.storeRequest === true) {
|
|
30
|
+
raw.request = req;
|
|
31
|
+
}
|
|
32
|
+
feedPreRoot(raw);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVlZC1mcm9tLXJlcXVlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGh1bmRlcnN0cnVjay9mZWVkLWZyb20tcmVxdWVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxPQUFPLEVBQUUsV0FBVyxFQUEwQixNQUFNLGVBQWUsQ0FBQztBQXVCcEUsTUFBTSxVQUFVLHNCQUFzQixDQUFFLEdBQWdCLEVBQUUsT0FBNEI7SUFDckYsTUFBTSxHQUFHLEdBQXNCO1FBQzlCLE1BQU0sRUFBSyxHQUFHLENBQUMsTUFBTTtRQUNyQixHQUFHLEVBQVEsR0FBRyxDQUFDLEdBQUc7UUFDbEIsTUFBTSxFQUFLLEdBQUcsQ0FBQyxNQUFNO1FBQ3JCLEtBQUssRUFBTSxHQUFHLENBQUMsS0FBSztRQUNwQixJQUFJLEVBQU8sR0FBRyxDQUFDLElBQUk7UUFDbkIsT0FBTyxFQUFJLEdBQUcsQ0FBQyxPQUFPO0tBQ3RCLENBQUM7SUFDRixJQUFJLE9BQU8sRUFBRSxZQUFZLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDcEMsR0FBRyxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUM7SUFDbkIsQ0FBQztJQUNELFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNsQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaHVuZGVyc3RydWNrIGJvdW5kYXJ5IGhlbHBlciDigJQgdGhlIGZyYW1ld29yay1mcmVlIGhhbGYgb2Ygd2hhdCBhXG4gKiBmcmFtZXdvcmsncyBpbnRlcmNlcHRvci9taWRkbGV3YXJlIGRvZXM6IHNoYXBlIGFueSBIVFRQIGZyYW1ld29yaydzXG4gKiByZXF1ZXN0IGludG8gdGhlXG4gKiByYXcgcHJlLXJvb3QgcGF5bG9hZCBhbmQgc3RhbXAgaXRzIHBhcnQgb2JqZWN0cywgc28gcGlwZXMgYW5kIGNvbnN0cnVjdFxuICogaGFuZGxlcnMgY29ycmVsYXRlIHRoZSBwYXlsb2FkIGJ5IE9CSkVDVCBJREVOVElUWSAoc2VlIHByZS1yb290LnRzIGZvclxuICogdGhlIHN0b3JlJ3MgbGlmZXRpbWUgYW5kIGNvcnJlbGF0aW9uIHNlbWFudGljcykuXG4gKlxuICogRnJhbWV3b3JrIG1hcHBpbmcgbm90ZXM6XG4gKiAgLSBFeHByZXNzOiBwYXNzIGByZXFgIGRpcmVjdGx5IOKAlCBtZXRob2QvdXJsL3BhcmFtcyjihpJyZXEucGFyYW1zKS9cbiAqICAgIHF1ZXJ5L2JvZHkvaGVhZGVycyBhbGwgc2l0IG9uIGl0LlxuICogIC0gRmFzdGlmeTogcGFzcyB0aGUgRmFzdGlmeVJlcXVlc3Qg4oCUIGl0IGV4cG9zZXMgcGFyYW1zL3F1ZXJ5L2JvZHkvXG4gKiAgICBoZWFkZXJzOyBgbWV0aG9kYC9gdXJsYCBleGlzdCBvbiBpdCBzaW5jZSBGYXN0aWZ5IHY0IChmYWxsIGJhY2sgdG9cbiAqICAgIGByZXEucmF3YCBpZiB5b3UgdGFyZ2V0IG9sZGVyIG1ham9ycykuXG4gKiAgLSByYXcgaHR0cDogYHVybGAvYG1ldGhvZGAgYXJlIG9uIEluY29taW5nTWVzc2FnZTsgYm9keS9xdWVyeS9wYXJhbXNcbiAqICAgIGFyZSB5b3VycyB0byBwYXJzZSBmaXJzdCAob3Igb21pdCDigJQgcHJpbWl0aXZlcyBhbmQgbWlzc2luZyBwYXJ0c1xuICogICAgc2ltcGx5IGRvbid0IGNvcnJlbGF0ZSwgdGhlIGZlZWQgc3RpbGwgcmVjb3JkcykuXG4gKi9cbmltcG9ydCB7IGZlZWRQcmVSb290LCB0eXBlIFJhd1ByZVJvb3RQYXlsb2FkIH0gZnJvbSAnLi9wcmUtcm9vdC5qcyc7XG5cbi8qKiBTdHJ1Y3R1cmFsIG1pbmltdW0gb2YgYW4gSFRUUCByZXF1ZXN0IGFueSBmcmFtZXdvcmsgY2FuIHNhdGlzZnkuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlcXVlc3RMaWtlIHtcblx0bWV0aG9kICAgOiBzdHJpbmc7XG5cdHVybCAgICAgIDogc3RyaW5nO1xuXHRwYXJhbXM/ICA6IHVua25vd247XG5cdHF1ZXJ5PyAgIDogdW5rbm93bjtcblx0Ym9keT8gICAgOiB1bmtub3duO1xuXHRoZWFkZXJzPyA6IHVua25vd247XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmVlZFByZVJvb3RPcHRpb25zIHtcblx0LyoqXG5cdCAqIExpbmsgdGhlIHJhdyByZXF1ZXN0IG9iamVjdCBpbnRvIHRoZSBwcmUtcm9vdCByZWNvcmQgKGByYXcucmVxdWVzdGApXG5cdCAqIEFORCBzdGFtcCBpdCBhcyBhIGNvcnJlbGF0aW9uIGtleSwgc28gZ2V0UHJlUm9vdChyZXEpIHJlc29sdmVzIGZyb21cblx0ICogYW55d2hlcmUgdGhlIHJlcXVlc3QgaXMgcmVhY2hhYmxlIOKAlCBlLmcuIGFuIGVycm9yIGJvdW5kYXJ5IGhvbGRpbmdcblx0ICogb25seSB0aGUgcmVxdWVzdC4gUmV0ZW50aW9uIGlzIHVuY2hhbmdlZDogdGhlIHJlY29yZCBkaWVzIHdpdGggdGhlXG5cdCAqIHJlcXVlc3QuXG5cdCAqL1xuXHRzdG9yZVJlcXVlc3Q/OiBib29sZWFuO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZmVlZFByZVJvb3RGcm9tUmVxdWVzdCAocmVxOiBSZXF1ZXN0TGlrZSwgb3B0aW9ucz86IEZlZWRQcmVSb290T3B0aW9ucyk6IHZvaWQge1xuXHRjb25zdCByYXc6IFJhd1ByZVJvb3RQYXlsb2FkID0ge1xuXHRcdG1ldGhvZCAgIDogcmVxLm1ldGhvZCxcblx0XHR1cmwgICAgICA6IHJlcS51cmwsXG5cdFx0cGFyYW1zICAgOiByZXEucGFyYW1zLFxuXHRcdHF1ZXJ5ICAgIDogcmVxLnF1ZXJ5LFxuXHRcdGJvZHkgICAgIDogcmVxLmJvZHksXG5cdFx0aGVhZGVycyAgOiByZXEuaGVhZGVycyxcblx0fTtcblx0aWYgKG9wdGlvbnM/LnN0b3JlUmVxdWVzdCA9PT0gdHJ1ZSkge1xuXHRcdHJhdy5yZXF1ZXN0ID0gcmVxO1xuXHR9XG5cdGZlZWRQcmVSb290KHJhdyk7XG59XG4iXX0=
|