@reproapp/node-sdk 0.0.31 → 0.0.33

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/src/index.ts CHANGED
@@ -1163,6 +1163,7 @@ type NativeMongoSuppression = {
1163
1163
  collection?: string | null;
1164
1164
  ops?: Set<string>;
1165
1165
  startedAt: number;
1166
+ ownerStore?: Ctx;
1166
1167
  };
1167
1168
 
1168
1169
  type RequestCaptureScopeState = {
@@ -1236,6 +1237,7 @@ function pushNativeMongoSuppression(collection: any, ops?: string[] | readonly s
1236
1237
  collection: collection == null ? null : String(collection),
1237
1238
  ops: normalizedOps.length ? new Set(normalizedOps) : undefined,
1238
1239
  startedAt: Date.now(),
1240
+ ownerStore: store,
1239
1241
  };
1240
1242
  if (!Array.isArray(store.nativeMongoSuppressions)) {
1241
1243
  store.nativeMongoSuppressions = [];
@@ -1246,28 +1248,64 @@ function pushNativeMongoSuppression(collection: any, ops?: string[] | readonly s
1246
1248
 
1247
1249
  function popNativeMongoSuppression(token: NativeMongoSuppression | null | undefined): void {
1248
1250
  if (!token) return;
1249
- const store = als.getStore();
1251
+ const store = token.ownerStore ?? als.getStore();
1250
1252
  const suppressions = store?.nativeMongoSuppressions;
1251
- if (!Array.isArray(suppressions) || suppressions.length === 0) return;
1252
- const index = suppressions.indexOf(token);
1253
- if (index >= 0) {
1254
- suppressions.splice(index, 1);
1253
+ if (Array.isArray(suppressions) && suppressions.length > 0) {
1254
+ const index = suppressions.indexOf(token);
1255
+ if (index >= 0) {
1256
+ suppressions.splice(index, 1);
1257
+ }
1258
+ }
1259
+ token.ownerStore = undefined;
1260
+ }
1261
+
1262
+ function releaseNativeMongoSuppression(target: any): void {
1263
+ const token = target?.__repro_native_mongo_suppression;
1264
+ popNativeMongoSuppression(token);
1265
+ try { delete target.__repro_native_mongo_suppression; } catch {
1266
+ try { target.__repro_native_mongo_suppression = undefined; } catch {}
1255
1267
  }
1256
1268
  }
1257
1269
 
1270
+ function isMongooseInternalNativeMongoCall(): boolean {
1271
+ let stack = '';
1272
+ try {
1273
+ stack = new Error().stack ?? '';
1274
+ } catch {
1275
+ stack = '';
1276
+ }
1277
+ return stack.includes('/node_modules/mongoose/') ||
1278
+ stack.includes('\\node_modules\\mongoose\\') ||
1279
+ stack.includes('mongoose/lib/');
1280
+ }
1281
+
1258
1282
  function isNativeMongoSuppressed(collection: any, op: string): boolean {
1259
1283
  const suppressions = (getCtx() as Ctx).nativeMongoSuppressions;
1260
1284
  if (!Array.isArray(suppressions) || suppressions.length === 0) return false;
1261
1285
  const normalizedCollection = collection == null ? null : String(collection);
1262
1286
  const normalizedOp = normalizeNativeMongoSuppressionOp(op);
1263
1287
  const now = Date.now();
1264
- for (const token of suppressions) {
1265
- if (!token || now - token.startedAt > 60000) continue;
1288
+ const matchingTokens: NativeMongoSuppression[] = [];
1289
+ for (let i = suppressions.length - 1; i >= 0; i -= 1) {
1290
+ const token = suppressions[i];
1291
+ if (!token || now - token.startedAt > 60000) {
1292
+ suppressions.splice(i, 1);
1293
+ continue;
1294
+ }
1266
1295
  if (token.collection && normalizedCollection && token.collection !== normalizedCollection) continue;
1267
1296
  if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp)) continue;
1268
- return true;
1297
+ matchingTokens.push(token);
1269
1298
  }
1270
- return false;
1299
+ if (matchingTokens.length === 0) return false;
1300
+
1301
+ if (!isMongooseInternalNativeMongoCall()) {
1302
+ return false;
1303
+ }
1304
+
1305
+ for (const token of matchingTokens) {
1306
+ popNativeMongoSuppression(token);
1307
+ }
1308
+ return true;
1271
1309
  }
1272
1310
 
1273
1311
  function currentClockSkewMs(): number {
@@ -5572,41 +5610,44 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5572
5610
  });
5573
5611
 
5574
5612
  schema.post('save', { document: true }, function () {
5575
- const { sid, aid } = getCtx();
5576
- if (!sid || !aid) return;
5577
- if ((this as any).$isSubdocument) return;
5578
-
5579
- const meta = (this as any).__repro_meta || {};
5580
- const before = null;
5581
- const after = this;
5582
- const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
5583
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5584
- if (!shouldCaptureDbSpan(spanContext)) return;
5585
- const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
5586
-
5587
- const query = meta.wasNew
5588
- ? { op: 'insertOne', doc: after }
5589
- : {
5590
- filter: { _id: this._id },
5591
- update: buildModifiedPathUpdate(this, modifiedPaths),
5592
- options: { upsert: false },
5593
- };
5613
+ try {
5614
+ const { sid, aid } = getCtx();
5615
+ if (!sid || !aid) return;
5616
+ if ((this as any).$isSubdocument) return;
5594
5617
 
5595
- emitDbQuery(cfg, sid, aid ?? undefined, {
5596
- collection,
5597
- pk: { _id: (this as any)._id },
5598
- before,
5599
- after,
5600
- op: meta.wasNew ? 'insert' : 'update',
5601
- query,
5602
- t: alignTimestamp(meta.t0 ?? Date.now()),
5603
- spanContext,
5604
- });
5605
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5618
+ const meta = (this as any).__repro_meta || {};
5619
+ const before = null;
5620
+ const after = this;
5621
+ const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
5622
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5623
+ if (!shouldCaptureDbSpan(spanContext)) return;
5624
+ const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
5625
+
5626
+ const query = meta.wasNew
5627
+ ? { op: 'insertOne', doc: after }
5628
+ : {
5629
+ filter: { _id: this._id },
5630
+ update: buildModifiedPathUpdate(this, modifiedPaths),
5631
+ options: { upsert: false },
5632
+ };
5633
+
5634
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5635
+ collection,
5636
+ pk: { _id: (this as any)._id },
5637
+ before,
5638
+ after,
5639
+ op: meta.wasNew ? 'insert' : 'update',
5640
+ query,
5641
+ t: alignTimestamp(meta.t0 ?? Date.now()),
5642
+ spanContext,
5643
+ });
5644
+ } finally {
5645
+ releaseNativeMongoSuppression(this);
5646
+ }
5606
5647
  });
5607
5648
 
5608
5649
  schema.post('save', { document: true }, function (error: any, _doc: any, next: Function) {
5609
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5650
+ releaseNativeMongoSuppression(this);
5610
5651
  next(error);
5611
5652
  });
5612
5653
 
@@ -5631,35 +5672,38 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5631
5672
  });
5632
5673
 
5633
5674
  schema.post<Query<any, any>>('findOneAndUpdate', function (res: any) {
5634
- const { sid, aid } = getCtx();
5635
- if (!sid || !aid) return;
5675
+ try {
5676
+ const { sid, aid } = getCtx();
5677
+ if (!sid || !aid) return;
5636
5678
 
5637
- const before = null;
5638
- const after = res ?? null;
5639
- const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5640
- const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5641
- if (!shouldCaptureDbSpan(spanContext)) return;
5642
- const filter = (this as any).__repro_filter;
5643
- const pk = after?._id ?? (filter && typeof filter === 'object' ? (filter as any)._id : undefined);
5679
+ const before = null;
5680
+ const after = res ?? null;
5681
+ const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5682
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5683
+ if (!shouldCaptureDbSpan(spanContext)) return;
5684
+ const filter = (this as any).__repro_filter;
5685
+ const pk = after?._id ?? (filter && typeof filter === 'object' ? (filter as any)._id : undefined);
5644
5686
 
5645
- emitDbQuery(cfg, sid, aid ?? undefined, {
5646
- collection,
5647
- pk: { _id: pk },
5648
- before,
5649
- after,
5650
- op: 'update',
5651
- queryRaw: {
5652
- filter,
5653
- update: (this as any).__repro_update,
5654
- },
5655
- t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5656
- spanContext,
5657
- });
5658
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5687
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5688
+ collection,
5689
+ pk: { _id: pk },
5690
+ before,
5691
+ after,
5692
+ op: 'update',
5693
+ queryRaw: {
5694
+ filter,
5695
+ update: (this as any).__repro_update,
5696
+ },
5697
+ t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5698
+ spanContext,
5699
+ });
5700
+ } finally {
5701
+ releaseNativeMongoSuppression(this);
5702
+ }
5659
5703
  });
5660
5704
 
5661
5705
  schema.post<Query<any, any>>('findOneAndUpdate', function (this: any, error: any, _res: any, next: Function) {
5662
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5706
+ releaseNativeMongoSuppression(this);
5663
5707
  next(error);
5664
5708
  } as any);
5665
5709
 
@@ -5681,27 +5725,30 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5681
5725
  });
5682
5726
 
5683
5727
  schema.post<Query<any, any>>('deleteOne', { document: false, query: true }, function () {
5684
- const { sid, aid } = getCtx(); if (!sid || !aid) return;
5685
- const before = null;
5686
- const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5687
- const filter = (this as any).__repro_filter ?? undefined;
5688
- const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5689
- if (!shouldCaptureDbSpan(spanContext)) return;
5690
- emitDbQuery(cfg, sid, aid ?? undefined, {
5691
- collection,
5692
- pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: (filter as any)._id } : undefined,
5693
- before,
5694
- after: null,
5695
- op: 'delete',
5696
- queryRaw: { filter },
5697
- t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5698
- spanContext,
5699
- });
5700
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5728
+ try {
5729
+ const { sid, aid } = getCtx(); if (!sid || !aid) return;
5730
+ const before = null;
5731
+ const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5732
+ const filter = (this as any).__repro_filter ?? undefined;
5733
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5734
+ if (!shouldCaptureDbSpan(spanContext)) return;
5735
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5736
+ collection,
5737
+ pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: (filter as any)._id } : undefined,
5738
+ before,
5739
+ after: null,
5740
+ op: 'delete',
5741
+ queryRaw: { filter },
5742
+ t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5743
+ spanContext,
5744
+ });
5745
+ } finally {
5746
+ releaseNativeMongoSuppression(this);
5747
+ }
5701
5748
  });
5702
5749
 
5703
5750
  schema.post<Query<any, any>>('deleteOne', { document: false, query: true }, function (this: any, error: any, _res: any, next: Function) {
5704
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5751
+ releaseNativeMongoSuppression(this);
5705
5752
  next(error);
5706
5753
  } as any);
5707
5754
 
@@ -5766,31 +5813,34 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5766
5813
  });
5767
5814
 
5768
5815
  schema.post(op as any, function (this: any, res: any) {
5769
- const { sid, aid } = getCtx();
5770
- if (!sid) return;
5816
+ try {
5817
+ const { sid, aid } = getCtx();
5818
+ if (!sid) return;
5771
5819
 
5772
- const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
5773
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5820
+ const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
5821
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5774
5822
 
5775
- emitDbQuery(cfg, sid, aid, {
5776
- collection: meta.collection,
5777
- op,
5778
- queryRaw: {
5779
- filter: meta.rawFilter,
5780
- update: meta.rawUpdate,
5781
- projection: meta.rawProjection,
5782
- options: meta.rawOptions,
5783
- },
5784
- resultMetaRaw: res,
5785
- durMs: Date.now() - meta.t0,
5786
- t: alignedNow(),
5787
- spanContext,
5788
- });
5789
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5823
+ emitDbQuery(cfg, sid, aid, {
5824
+ collection: meta.collection,
5825
+ op,
5826
+ queryRaw: {
5827
+ filter: meta.rawFilter,
5828
+ update: meta.rawUpdate,
5829
+ projection: meta.rawProjection,
5830
+ options: meta.rawOptions,
5831
+ },
5832
+ resultMetaRaw: res,
5833
+ durMs: Date.now() - meta.t0,
5834
+ t: alignedNow(),
5835
+ spanContext,
5836
+ });
5837
+ } finally {
5838
+ releaseNativeMongoSuppression(this);
5839
+ }
5790
5840
  });
5791
5841
 
5792
5842
  schema.post(op as any, function (this: any, error: any, _res: any, next: Function) {
5793
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5843
+ releaseNativeMongoSuppression(this);
5794
5844
  next(error);
5795
5845
  } as any);
5796
5846
  }
@@ -5824,25 +5874,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5824
5874
  } as any);
5825
5875
 
5826
5876
  schema.post<any>('insertMany' as any, { document: false, query: false } as any, function (this: any, docs: any[]) {
5827
- const { sid, aid } = getCtx();
5828
- if (!sid) return;
5829
- const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
5830
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5831
-
5832
- emitDbQuery(cfg, sid, aid ?? undefined, {
5833
- collection: meta.collection,
5834
- op: 'insertMany',
5835
- queryRaw: { docs: meta.rawDocs ?? undefined },
5836
- resultMetaRaw: docs,
5837
- durMs: Date.now() - meta.t0,
5838
- t: alignedNow(),
5839
- spanContext,
5840
- });
5841
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5877
+ try {
5878
+ const { sid, aid } = getCtx();
5879
+ if (!sid) return;
5880
+ const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
5881
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5882
+
5883
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5884
+ collection: meta.collection,
5885
+ op: 'insertMany',
5886
+ queryRaw: { docs: meta.rawDocs ?? undefined },
5887
+ resultMetaRaw: docs,
5888
+ durMs: Date.now() - meta.t0,
5889
+ t: alignedNow(),
5890
+ spanContext,
5891
+ });
5892
+ } finally {
5893
+ releaseNativeMongoSuppression(this);
5894
+ }
5842
5895
  } as any);
5843
5896
 
5844
5897
  schema.post<any>('insertMany' as any, { document: false, query: false } as any, function (this: any, error: any, _res: any, next: Function) {
5845
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5898
+ releaseNativeMongoSuppression(this);
5846
5899
  next(error);
5847
5900
  } as any);
5848
5901
 
@@ -5871,25 +5924,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5871
5924
  } as any);
5872
5925
 
5873
5926
  schema.post<any>('bulkWrite' as any, { document: false, query: false } as any, function (this: any, res: any) {
5874
- const { sid, aid } = getCtx();
5875
- if (!sid) return;
5876
- const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
5877
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5878
-
5879
- emitDbQuery(cfg, sid, aid, {
5880
- collection: meta.collection,
5881
- op: 'bulkWrite',
5882
- queryRaw: { ops: meta.rawOps ?? undefined },
5883
- resultMetaRaw: res,
5884
- durMs: Date.now() - meta.t0,
5885
- t: alignedNow(),
5886
- spanContext,
5887
- });
5888
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5927
+ try {
5928
+ const { sid, aid } = getCtx();
5929
+ if (!sid) return;
5930
+ const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
5931
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5932
+
5933
+ emitDbQuery(cfg, sid, aid, {
5934
+ collection: meta.collection,
5935
+ op: 'bulkWrite',
5936
+ queryRaw: { ops: meta.rawOps ?? undefined },
5937
+ resultMetaRaw: res,
5938
+ durMs: Date.now() - meta.t0,
5939
+ t: alignedNow(),
5940
+ spanContext,
5941
+ });
5942
+ } finally {
5943
+ releaseNativeMongoSuppression(this);
5944
+ }
5889
5945
  } as any);
5890
5946
 
5891
5947
  schema.post<any>('bulkWrite' as any, { document: false, query: false } as any, function (this: any, error: any, _res: any, next: Function) {
5892
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5948
+ releaseNativeMongoSuppression(this);
5893
5949
  next(error);
5894
5950
  } as any);
5895
5951
 
@@ -5924,26 +5980,29 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5924
5980
  });
5925
5981
 
5926
5982
  schema.post('aggregate', function (this: any, res: any[]) {
5927
- const { sid, aid } = getCtx();
5928
- if (!sid) return;
5983
+ try {
5984
+ const { sid, aid } = getCtx();
5985
+ if (!sid) return;
5929
5986
 
5930
- const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
5931
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5987
+ const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
5988
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5932
5989
 
5933
- emitDbQuery(cfg, sid, aid, {
5934
- collection: meta.collection,
5935
- op: 'aggregate',
5936
- queryRaw: { pipeline: meta.rawPipeline },
5937
- resultMetaRaw: res,
5938
- durMs: Date.now() - meta.t0,
5939
- t: alignedNow(),
5940
- spanContext,
5941
- });
5942
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5990
+ emitDbQuery(cfg, sid, aid, {
5991
+ collection: meta.collection,
5992
+ op: 'aggregate',
5993
+ queryRaw: { pipeline: meta.rawPipeline },
5994
+ resultMetaRaw: res,
5995
+ durMs: Date.now() - meta.t0,
5996
+ t: alignedNow(),
5997
+ spanContext,
5998
+ });
5999
+ } finally {
6000
+ releaseNativeMongoSuppression(this);
6001
+ }
5943
6002
  });
5944
6003
 
5945
6004
  schema.post('aggregate', function (this: any, error: any, _res: any, next: Function) {
5946
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
6005
+ releaseNativeMongoSuppression(this);
5947
6006
  next(error);
5948
6007
  } as any);
5949
6008
  };
@@ -9966,6 +10025,16 @@ export const __reproTestHooks = {
9966
10025
  lastClientActivityAt = 0;
9967
10026
  sdkBackgroundQuietUntil = 0;
9968
10027
  },
10028
+ runWithRequestContextForTest<T>(ctx: Partial<Ctx>, fn: () => T): T {
10029
+ return als.run({
10030
+ ...ctx,
10031
+ excludedSpanIds: ctx.excludedSpanIds ?? new Set<string>(),
10032
+ }, fn);
10033
+ },
10034
+ getNativeMongoSuppressionCountForTest(): number {
10035
+ const suppressions = getCtx().nativeMongoSuppressions;
10036
+ return Array.isArray(suppressions) ? suppressions.length : 0;
10037
+ },
9969
10038
  getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null {
9970
10039
  return getRuntimePrivacyState(cfg).policy;
9971
10040
  },
@@ -8,6 +8,7 @@ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
8
8
  const assert = require('assert');
9
9
  const { EventEmitter } = require('events');
10
10
  const mongodb = require('mongodb');
11
+ const mongoose = require('mongoose');
11
12
 
12
13
  const originalNativeMongoInsertOne = mongodb.Collection.prototype.insertOne;
13
14
 
@@ -18,7 +19,7 @@ mongodb.Collection.prototype.insertOne = async function reproFakeNativeMongoInse
18
19
  };
19
20
 
20
21
  const { flushIngestQueue } = require('../dist/ingest/client');
21
- const { reproMiddleware, __reproTestHooks } = require('../dist');
22
+ const { reproMiddleware, reproMongoosePlugin, __reproTestHooks } = require('../dist');
22
23
 
23
24
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
24
25
 
@@ -126,12 +127,80 @@ async function runCapturedRequest(cfg, sessionId, actionId, fn) {
126
127
  res.end();
127
128
  }
128
129
 
130
+ function execPre(hooks, name, context) {
131
+ return new Promise((resolve, reject) => {
132
+ hooks.execPre(name, context, [], (error) => (error ? reject(error) : resolve()));
133
+ });
134
+ }
135
+
129
136
  function dbChanges(events) {
130
137
  return events
131
138
  .filter((event) => event.event_type === 'db_change')
132
139
  .flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
133
140
  }
134
141
 
142
+ async function testMongooseSuppressionDoesNotHideExplicitNativeInsertOne() {
143
+ await withCapturedFetch(async (capturedBodies) => {
144
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
145
+ const sessionId = 'S_mongo_mixed_driver_insert';
146
+ const actionId = 'A_mongo_mixed_driver_insert';
147
+ const cfg = makeConfig(sessionId, actionId);
148
+
149
+ const schema = new mongoose.Schema({ subjectNumber: String, status: String });
150
+ reproMongoosePlugin(cfg)(schema);
151
+ const Model = mongoose.model(`ReproMixedNativeMongo${Date.now()}`, schema, 'subjects');
152
+ const pendingMongooseDoc = new Model({ subjectNumber: 'pre-save', status: 'pending' });
153
+
154
+ class FakeCollection {
155
+ constructor() {
156
+ this.collectionName = 'subjects';
157
+ this.dbName = 'clinical';
158
+ }
159
+
160
+ async insertOne(doc, options) {
161
+ assert.strictEqual(options.session.id, 'native-explicit-session-1');
162
+ doc._id = doc._id ?? 'subject-explicit-1';
163
+ return { acknowledged: true, insertedId: doc._id };
164
+ }
165
+ }
166
+
167
+ __reproTestHooks.patchMongoCollectionPrototypeForTest(FakeCollection.prototype, cfg);
168
+ const subjectsCollection = new FakeCollection();
169
+
170
+ await runCapturedRequest(cfg, sessionId, actionId, async () => {
171
+ await execPre(schema.s.hooks, 'save', pendingMongooseDoc);
172
+ assert.strictEqual(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 1);
173
+
174
+ const result = await subjectsCollection.insertOne(
175
+ { subjectNumber: '10013038', status: 'screened' },
176
+ {
177
+ session: {
178
+ id: 'native-explicit-session-1',
179
+ hasEnded: false,
180
+ inTransaction: () => true,
181
+ },
182
+ },
183
+ );
184
+ assert.deepStrictEqual(result, { acknowledged: true, insertedId: 'subject-explicit-1' });
185
+ });
186
+
187
+ await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
188
+ await drainSdk();
189
+
190
+ const [change] = dbChanges(flattenEvents(capturedBodies));
191
+ assert.strictEqual(change.type, 'mongo');
192
+ assert.strictEqual(change.collection, 'subjects');
193
+ assert.strictEqual(change.op, 'insertOne');
194
+ assert.strictEqual(change.pk._id, 'subject-explicit-1');
195
+ assert.strictEqual(change.query.driver, 'mongodb');
196
+ assert.strictEqual(change.query.db, 'clinical');
197
+ assert.strictEqual(change.query.doc.subjectNumber, '10013038');
198
+ assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
199
+ assert.strictEqual(change.query.options.session.id, 'native-explicit-session-1');
200
+ assert.strictEqual(change.after.subjectNumber, '10013038');
201
+ });
202
+ }
203
+
135
204
  async function testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne() {
136
205
  await withCapturedFetch(async (capturedBodies) => {
137
206
  __reproTestHooks.resetSdkBackgroundQueuesForTest();
@@ -452,6 +521,7 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
452
521
  }
453
522
 
454
523
  (async () => {
524
+ await testMongooseSuppressionDoesNotHideExplicitNativeInsertOne();
455
525
  await testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne();
456
526
  await testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport();
457
527
  await testNativeInsertOneIsCaptured();