repro-nest 0.0.203 → 0.0.205

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
@@ -92,40 +92,97 @@ function flushQueryFinalizers(query, value, threw, error) {
92
92
  }
93
93
  catch { }
94
94
  }
95
- function patchMongooseExecCapture() {
95
+ function patchMongooseExecCapture(targetMongoose = mongoose) {
96
+ // Patch Query.prototype.exec to capture resolved results for trace return values.
96
97
  try {
97
- const Qp = mongoose.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;
98
+ const Qp = targetMongoose?.Query?.prototype;
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
+ }
166
+ }
167
+ catch { }
168
+ }
169
+ function patchAllKnownMongooseInstances() {
170
+ // Patch the SDK's bundled mongoose first.
171
+ patchMongooseExecCapture(mongoose);
172
+ // Also patch any other mongoose copies the host app might have installed (e.g., different versions).
173
+ try {
174
+ const cache = require?.cache || {};
175
+ const seen = new Set();
176
+ Object.keys(cache).forEach((key) => {
177
+ if (!/node_modules[\\/](mongoose)[\\/]/i.test(key))
178
+ return;
179
+ const mod = cache[key];
180
+ const exp = mod?.exports;
181
+ if (!exp || seen.has(exp))
182
+ return;
183
+ seen.add(exp);
184
+ patchMongooseExecCapture(exp);
185
+ });
129
186
  }
130
187
  catch { }
131
188
  }
@@ -384,7 +441,9 @@ function initReproTracing(opts) {
384
441
  tracerPkg.patchHttp?.();
385
442
  applyTraceLogPreference(tracerPkg);
386
443
  __TRACER_READY = true;
387
- patchMongooseExecCapture();
444
+ patchAllKnownMongooseInstances();
445
+ // Patch again on the next tick to catch mongoose copies that load after init (different versions/copies).
446
+ setImmediate(() => patchAllKnownMongooseInstances());
388
447
  }
389
448
  catch {
390
449
  __TRACER__ = null; // SDK still works without tracer
@@ -1289,27 +1348,6 @@ function reproMiddleware(cfg) {
1289
1348
  depth: evt.depth,
1290
1349
  library: inferLibraryNameFromFile(evt.file),
1291
1350
  };
1292
- if (evt.type === 'exit') {
1293
- // If the function returned a Mongoose Query, update the exit value when the query settles.
1294
- const rv = ev.returnValue;
1295
- const setReturnValue = (val) => {
1296
- try {
1297
- evt.returnValue = sanitizeTraceValue(val);
1298
- }
1299
- catch { }
1300
- };
1301
- const qp = rv && rv.__repro_result_promise;
1302
- const hasResult = rv && Object.prototype.hasOwnProperty.call(rv, '__repro_result');
1303
- if (hasResult) {
1304
- setReturnValue(rv.__repro_result);
1305
- }
1306
- else if (qp && typeof qp.then === 'function') {
1307
- try {
1308
- qp.then((val) => setReturnValue(val), () => { });
1309
- }
1310
- catch { }
1311
- }
1312
- }
1313
1351
  const spanKey = normalizeSpanId(evt.spanId);
1314
1352
  if (evt.type === 'enter') {
1315
1353
  lastEventAt = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.203",
3
+ "version": "0.0.205",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -86,34 +86,88 @@ function flushQueryFinalizers(query: any, value: any, threw: boolean, error: any
86
86
  } catch {}
87
87
  }
88
88
 
89
- function patchMongooseExecCapture() {
89
+ function patchMongooseExecCapture(targetMongoose: any = mongoose) {
90
+ // Patch Query.prototype.exec to capture resolved results for trace return values.
90
91
  try {
91
- const Qp: any = (mongoose as any).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;
92
+ const Qp: any = targetMongoose?.Query?.prototype;
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
+ }
152
+ } catch {}
153
+ }
154
+
155
+ function patchAllKnownMongooseInstances() {
156
+ // Patch the SDK's bundled mongoose first.
157
+ patchMongooseExecCapture(mongoose as any);
158
+
159
+ // Also patch any other mongoose copies the host app might have installed (e.g., different versions).
160
+ try {
161
+ const cache = (require as any)?.cache || {};
162
+ const seen = new Set<any>();
163
+ Object.keys(cache).forEach((key) => {
164
+ if (!/node_modules[\\/](mongoose)[\\/]/i.test(key)) return;
165
+ const mod = cache[key];
166
+ const exp = mod?.exports;
167
+ if (!exp || seen.has(exp)) return;
168
+ seen.add(exp);
169
+ patchMongooseExecCapture(exp);
170
+ });
117
171
  } catch {}
118
172
  }
119
173
 
@@ -570,7 +624,9 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
570
624
  tracerPkg.patchHttp?.();
571
625
  applyTraceLogPreference(tracerPkg);
572
626
  __TRACER_READY = true;
573
- patchMongooseExecCapture();
627
+ patchAllKnownMongooseInstances();
628
+ // Patch again on the next tick to catch mongoose copies that load after init (different versions/copies).
629
+ setImmediate(() => patchAllKnownMongooseInstances());
574
630
  } catch {
575
631
  __TRACER__ = null; // SDK still works without tracer
576
632
  } finally {
@@ -1499,26 +1555,6 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1499
1555
  library: inferLibraryNameFromFile(evt.file),
1500
1556
  };
1501
1557
 
1502
- if (evt.type === 'exit') {
1503
- // If the function returned a Mongoose Query, update the exit value when the query settles.
1504
- const rv: any = ev.returnValue;
1505
- const setReturnValue = (val: any) => {
1506
- try { evt.returnValue = sanitizeTraceValue(val); } catch {}
1507
- };
1508
- const qp = rv && rv.__repro_result_promise;
1509
- const hasResult = rv && Object.prototype.hasOwnProperty.call(rv, '__repro_result');
1510
- if (hasResult) {
1511
- setReturnValue(rv.__repro_result);
1512
- } else if (qp && typeof qp.then === 'function') {
1513
- try {
1514
- qp.then(
1515
- (val: any) => setReturnValue(val),
1516
- () => {}
1517
- );
1518
- } catch {}
1519
- }
1520
- }
1521
-
1522
1558
  const spanKey = normalizeSpanId(evt.spanId);
1523
1559
  if (evt.type === 'enter') {
1524
1560
  lastEventAt = Date.now();
package/tracer/runtime.js CHANGED
@@ -645,6 +645,11 @@ if (!global.__repro_call) {
645
645
  }
646
646
  } catch {}
647
647
 
648
+ if (isQuery) {
649
+ runExit(exitDetailBase);
650
+ return out;
651
+ }
652
+
648
653
  let settled = false;
649
654
  const finalize = (value, threw, error) => {
650
655
  if (settled) return value;