repro-nest 0.0.204 → 0.0.207

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/dist/index.js CHANGED
@@ -93,39 +93,76 @@ function flushQueryFinalizers(query, value, threw, error) {
93
93
  catch { }
94
94
  }
95
95
  function patchMongooseExecCapture(targetMongoose = mongoose) {
96
+ // Patch Query.prototype.exec to capture resolved results for trace return values.
96
97
  try {
97
98
  const Qp = targetMongoose?.Query?.prototype;
98
- if (!Qp || Qp.__repro_exec_patched)
99
- return;
100
- const origExec = Qp.exec;
101
- if (typeof origExec !== 'function')
102
- return;
103
- Qp.__repro_exec_patched = true;
104
- Qp.exec = function reproPatchedExec(...args) {
105
- try {
106
- this.__repro_is_query = true;
99
+ if (Qp && !Qp.__repro_exec_patched) {
100
+ const origExec = Qp.exec;
101
+ if (typeof origExec === 'function') {
102
+ Qp.__repro_exec_patched = true;
103
+ Qp.exec = function reproPatchedExec(...args) {
104
+ try {
105
+ this.__repro_is_query = true;
106
+ }
107
+ catch { }
108
+ const p = origExec.apply(this, args);
109
+ try {
110
+ if (p && typeof p.then === 'function') {
111
+ this.__repro_result_promise = p;
112
+ p.then((res) => {
113
+ try {
114
+ this.__repro_result = res;
115
+ }
116
+ catch { }
117
+ flushQueryFinalizers(this, res, false, null);
118
+ return res;
119
+ }, (err) => {
120
+ flushQueryFinalizers(this, undefined, true, err);
121
+ return err;
122
+ });
123
+ }
124
+ }
125
+ catch { }
126
+ return p;
127
+ };
107
128
  }
108
- catch { }
109
- const p = origExec.apply(this, args);
110
- try {
111
- if (p && typeof p.then === 'function') {
112
- this.__repro_result_promise = p;
113
- p.then((res) => {
114
- try {
115
- this.__repro_result = res;
129
+ }
130
+ }
131
+ catch { }
132
+ // Patch Aggregate.prototype.exec as well so aggregation pipelines surface their results in traces.
133
+ try {
134
+ const Ap = targetMongoose?.Aggregate?.prototype;
135
+ if (Ap && !Ap.__repro_agg_exec_patched) {
136
+ const origAggExec = Ap.exec;
137
+ if (typeof origAggExec === 'function') {
138
+ Ap.__repro_agg_exec_patched = true;
139
+ Ap.exec = function reproPatchedAggExec(...args) {
140
+ try {
141
+ this.__repro_is_query = true;
142
+ }
143
+ catch { }
144
+ const p = origAggExec.apply(this, args);
145
+ try {
146
+ if (p && typeof p.then === 'function') {
147
+ this.__repro_result_promise = p;
148
+ p.then((res) => {
149
+ try {
150
+ this.__repro_result = res;
151
+ }
152
+ catch { }
153
+ flushQueryFinalizers(this, res, false, null);
154
+ return res;
155
+ }, (err) => {
156
+ flushQueryFinalizers(this, undefined, true, err);
157
+ return err;
158
+ });
116
159
  }
117
- catch { }
118
- flushQueryFinalizers(this, res, false, null);
119
- return res;
120
- }, (err) => {
121
- flushQueryFinalizers(this, undefined, true, err);
122
- return err;
123
- });
124
- }
160
+ }
161
+ catch { }
162
+ return p;
163
+ };
125
164
  }
126
- catch { }
127
- return p;
128
- };
165
+ }
129
166
  }
130
167
  catch { }
131
168
  }
@@ -1290,6 +1327,9 @@ function reproMiddleware(cfg) {
1290
1327
  if (ev.args !== undefined) {
1291
1328
  evt.args = sanitizeTraceArgs(ev.args);
1292
1329
  }
1330
+ if (ev.receiver !== undefined) {
1331
+ evt.receiver = sanitizeTraceValue(ev.receiver);
1332
+ }
1293
1333
  if (ev.returnValue !== undefined) {
1294
1334
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1295
1335
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.204",
3
+ "version": "0.0.207",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -87,33 +87,68 @@ function flushQueryFinalizers(query: any, value: any, threw: boolean, error: any
87
87
  }
88
88
 
89
89
  function patchMongooseExecCapture(targetMongoose: any = mongoose) {
90
+ // Patch Query.prototype.exec to capture resolved results for trace return values.
90
91
  try {
91
92
  const Qp: any = targetMongoose?.Query?.prototype;
92
- if (!Qp || Qp.__repro_exec_patched) return;
93
- const origExec = Qp.exec;
94
- if (typeof origExec !== 'function') return;
95
- Qp.__repro_exec_patched = true;
96
- Qp.exec = function reproPatchedExec(this: any, ...args: any[]) {
97
- try { (this as any).__repro_is_query = true; } catch {}
98
- const p = origExec.apply(this, args);
99
- try {
100
- if (p && typeof p.then === 'function') {
101
- this.__repro_result_promise = p;
102
- p.then(
103
- (res: any) => {
104
- try { this.__repro_result = res; } catch {}
105
- flushQueryFinalizers(this, res, false, null);
106
- return res;
107
- },
108
- (err: any) => {
109
- flushQueryFinalizers(this, undefined, true, err);
110
- return err;
93
+ if (Qp && !Qp.__repro_exec_patched) {
94
+ const origExec = Qp.exec;
95
+ if (typeof origExec === 'function') {
96
+ Qp.__repro_exec_patched = true;
97
+ Qp.exec = function reproPatchedExec(this: any, ...args: any[]) {
98
+ try { (this as any).__repro_is_query = true; } catch {}
99
+ const p = origExec.apply(this, args);
100
+ try {
101
+ if (p && typeof p.then === 'function') {
102
+ this.__repro_result_promise = p;
103
+ p.then(
104
+ (res: any) => {
105
+ try { this.__repro_result = res; } catch {}
106
+ flushQueryFinalizers(this, res, false, null);
107
+ return res;
108
+ },
109
+ (err: any) => {
110
+ flushQueryFinalizers(this, undefined, true, err);
111
+ return err;
112
+ }
113
+ );
111
114
  }
112
- );
113
- }
114
- } catch {}
115
- return p;
116
- };
115
+ } catch {}
116
+ return p;
117
+ };
118
+ }
119
+ }
120
+ } catch {}
121
+
122
+ // Patch Aggregate.prototype.exec as well so aggregation pipelines surface their results in traces.
123
+ try {
124
+ const Ap: any = targetMongoose?.Aggregate?.prototype;
125
+ if (Ap && !Ap.__repro_agg_exec_patched) {
126
+ const origAggExec = Ap.exec;
127
+ if (typeof origAggExec === 'function') {
128
+ Ap.__repro_agg_exec_patched = true;
129
+ Ap.exec = function reproPatchedAggExec(this: any, ...args: any[]) {
130
+ try { (this as any).__repro_is_query = true; } catch {}
131
+ const p = origAggExec.apply(this, args);
132
+ try {
133
+ if (p && typeof p.then === 'function') {
134
+ this.__repro_result_promise = p;
135
+ p.then(
136
+ (res: any) => {
137
+ try { this.__repro_result = res; } catch {}
138
+ flushQueryFinalizers(this, res, false, null);
139
+ return res;
140
+ },
141
+ (err: any) => {
142
+ flushQueryFinalizers(this, undefined, true, err);
143
+ return err;
144
+ }
145
+ );
146
+ }
147
+ } catch {}
148
+ return p;
149
+ };
150
+ }
151
+ }
117
152
  } catch {}
118
153
  }
119
154
 
@@ -175,6 +210,7 @@ type TraceEventRecord = {
175
210
  spanId?: string | number | null;
176
211
  parentSpanId?: string | number | null;
177
212
  args?: any;
213
+ receiver?: any;
178
214
  returnValue?: any;
179
215
  threw?: boolean;
180
216
  error?: any;
@@ -1497,6 +1533,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1497
1533
  if (ev.args !== undefined) {
1498
1534
  evt.args = sanitizeTraceArgs(ev.args);
1499
1535
  }
1536
+ if (ev.receiver !== undefined) {
1537
+ evt.receiver = sanitizeTraceValue(ev.receiver);
1538
+ }
1500
1539
  if (ev.returnValue !== undefined) {
1501
1540
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1502
1541
  }
package/tracer/runtime.js CHANGED
@@ -132,6 +132,7 @@ const trace = {
132
132
  traceId: ctx.traceId,
133
133
  depth: ctx.depth,
134
134
  args: detail?.args,
135
+ receiver: detail?.receiver,
135
136
  spanId: span.id,
136
137
  parentSpanId: span.parentId
137
138
  });
@@ -159,7 +160,8 @@ const trace = {
159
160
  returnValue: detail?.returnValue,
160
161
  error: detail?.error,
161
162
  threw: detail?.threw === true,
162
- unawaited: detail?.unawaited === true || frameUnawaited
163
+ unawaited: detail?.unawaited === true || frameUnawaited,
164
+ receiver: detail?.receiver
163
165
  };
164
166
 
165
167
  const promiseTaggedUnawaited = !!(baseDetail.returnValue && baseDetail.returnValue[SYM_UNAWAITED]);
@@ -179,6 +181,9 @@ const trace = {
179
181
  unawaited: overrides.hasOwnProperty('unawaited')
180
182
  ? overrides.unawaited
181
183
  : forceUnawaited,
184
+ receiver: overrides.hasOwnProperty('receiver')
185
+ ? overrides.receiver
186
+ : baseDetail.receiver,
182
187
  args: overrides.hasOwnProperty('args')
183
188
  ? overrides.args
184
189
  : baseDetail.args
@@ -199,6 +204,7 @@ const trace = {
199
204
  threw: finalDetail.threw === true,
200
205
  error: finalDetail.error,
201
206
  args: finalDetail.args,
207
+ receiver: finalDetail.receiver,
202
208
  unawaited: finalDetail.unawaited === true
203
209
  });
204
210
  };
@@ -475,6 +481,24 @@ function forkAlsStoreForUnawaited(baseStore) {
475
481
  return cloned;
476
482
  }
477
483
 
484
+ // Only capture receiver/collection snapshots for common array iteration helpers to keep noise low.
485
+ const RECEIVER_METHOD_NAMES = new Set([
486
+ // Array iterators
487
+ 'map', 'forEach', 'filter', 'find', 'findIndex', 'findLast', 'findLastIndex',
488
+ 'some', 'every', 'reduce', 'reduceRight', 'flatMap', 'flat',
489
+ // Set / Map mutators
490
+ 'add', 'delete', 'clear', 'set'
491
+ ]);
492
+
493
+ function shouldCaptureReceiver(label, receiver) {
494
+ if (!label || receiver === null || receiver === undefined) return false;
495
+ const method = String(label).split('.').pop() || '';
496
+ if (!RECEIVER_METHOD_NAMES.has(method)) return false;
497
+ if (Array.isArray(receiver)) return true;
498
+ if (typeof receiver === 'object' && typeof receiver[Symbol.iterator] === 'function') return true;
499
+ return false;
500
+ }
501
+
478
502
  // ========= Generic call-site shim (used by Babel transform) =========
479
503
  // Decides whether to emit a top-level event based on callee origin tags.
480
504
  // No hardcoded library names or file paths.
@@ -596,13 +620,15 @@ if (!global.__repro_call) {
596
620
  ? (label && label.length ? label : fn.name)
597
621
  : '(anonymous)';
598
622
  const sourceFile = fn[SYM_SRC_FILE];
623
+ const receiverForTrace = shouldCaptureReceiver(label, thisArg) ? thisArg : undefined;
599
624
  const meta = {
600
- file: sourceFile || callFile || null,
601
- line: sourceFile ? null : (callLine || null),
625
+ // Prefer callsite file/line so traces point to where the function was invoked.
626
+ file: callFile || sourceFile || null,
627
+ line: callLine ?? null,
602
628
  parentSpanId: forcedParentSpanId
603
629
  };
604
630
 
605
- trace.enter(name, meta, { args });
631
+ trace.enter(name, meta, { args, receiver: receiverForTrace });
606
632
  // After entering, snapshot a clean store that captures the parent + this call’s span,
607
633
  // so callbacks invoked during the callee run are isolated per invocation.
608
634
  const snapshotForArgs = cloneStore(als.getStore());
@@ -617,7 +643,8 @@ if (!global.__repro_call) {
617
643
  const exitDetailBase = {
618
644
  returnValue: out,
619
645
  args,
620
- unawaited: shouldForceExit
646
+ unawaited: shouldForceExit,
647
+ receiver: receiverForTrace
621
648
  };
622
649
 
623
650
  if (shouldForceExit) markPromiseUnawaited(out);
@@ -659,7 +686,8 @@ if (!global.__repro_call) {
659
686
  args,
660
687
  threw,
661
688
  error,
662
- unawaited: shouldForceExit
689
+ unawaited: shouldForceExit,
690
+ receiver: receiverForTrace
663
691
  };
664
692
  runExit(detail);
665
693
  return value;
@@ -679,7 +707,7 @@ if (!global.__repro_call) {
679
707
  runExit(exitDetailBase);
680
708
  return out;
681
709
  } catch (e) {
682
- trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args });
710
+ trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args, receiver: receiverForTrace });
683
711
  throw e;
684
712
  } finally {
685
713
  if (pendingMarker) {