@reproapp/node-sdk 0.0.31 → 0.0.32

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.d.ts CHANGED
@@ -262,6 +262,31 @@ export type ReproTracingInitOptions = TracerInitOpts & {
262
262
  export declare function initReproTracing(opts?: ReproTracingInitOptions): TracerApi | null;
263
263
  /** Optional helper if users want to check it. */
264
264
  export declare function isReproTracingEnabled(): boolean;
265
+ type Ctx = {
266
+ sid?: string;
267
+ aid?: string;
268
+ rid?: string;
269
+ clockSkewMs?: number;
270
+ excludedSpanIds?: Set<string>;
271
+ maskReq?: MaskRequestContext;
272
+ privacyTasks?: Set<Promise<void>>;
273
+ captureScope?: RequestCaptureScopeState;
274
+ nativeMongoSuppressions?: NativeMongoSuppression[];
275
+ };
276
+ type NativeMongoSuppression = {
277
+ collection?: string | null;
278
+ ops?: Set<string>;
279
+ startedAt: number;
280
+ ownerStore?: Ctx;
281
+ };
282
+ type RequestCaptureScopeState = {
283
+ requestRid: string;
284
+ transport: 'http' | 'kafka' | 'amqp' | 'background';
285
+ backendRequestCount: number;
286
+ traceBatchCount: number;
287
+ dbChangeCount: number;
288
+ emailEventCount: number;
289
+ };
265
290
  declare function sanitizeTraceValueForPrivacy(value: any): any;
266
291
  declare function sanitizeMaterializedTraceValue(value: any): any;
267
292
  export declare function __materializePendingTraceEventsForWorker(payload: TraceMaterializationWorkerPayload): Promise<PendingTraceEventRecord[]>;
@@ -382,6 +407,8 @@ export declare const __reproTestHooks: {
382
407
  }): void;
383
408
  drainSdkBackgroundQueueForTest(): Promise<void>;
384
409
  resetSdkBackgroundQueuesForTest(): void;
410
+ runWithRequestContextForTest<T>(ctx: Partial<Ctx>, fn: () => T): T;
411
+ getNativeMongoSuppressionCountForTest(): number;
385
412
  getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null;
386
413
  setRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig, policy: NormalizedRuntimePrivacyPolicy | null): void;
387
414
  recordKafkaTraceEventAsyncForTest: typeof recordKafkaTraceEventAsync;
package/dist/index.js CHANGED
@@ -835,6 +835,7 @@ function pushNativeMongoSuppression(collection, ops) {
835
835
  collection: collection == null ? null : String(collection),
836
836
  ops: normalizedOps.length ? new Set(normalizedOps) : undefined,
837
837
  startedAt: Date.now(),
838
+ ownerStore: store,
838
839
  };
839
840
  if (!Array.isArray(store.nativeMongoSuppressions)) {
840
841
  store.nativeMongoSuppressions = [];
@@ -845,13 +846,27 @@ function pushNativeMongoSuppression(collection, ops) {
845
846
  function popNativeMongoSuppression(token) {
846
847
  if (!token)
847
848
  return;
848
- const store = als.getStore();
849
+ const store = token.ownerStore ?? als.getStore();
849
850
  const suppressions = store?.nativeMongoSuppressions;
850
- if (!Array.isArray(suppressions) || suppressions.length === 0)
851
- return;
852
- const index = suppressions.indexOf(token);
853
- if (index >= 0) {
854
- suppressions.splice(index, 1);
851
+ if (Array.isArray(suppressions) && suppressions.length > 0) {
852
+ const index = suppressions.indexOf(token);
853
+ if (index >= 0) {
854
+ suppressions.splice(index, 1);
855
+ }
856
+ }
857
+ token.ownerStore = undefined;
858
+ }
859
+ function releaseNativeMongoSuppression(target) {
860
+ const token = target?.__repro_native_mongo_suppression;
861
+ popNativeMongoSuppression(token);
862
+ try {
863
+ delete target.__repro_native_mongo_suppression;
864
+ }
865
+ catch {
866
+ try {
867
+ target.__repro_native_mongo_suppression = undefined;
868
+ }
869
+ catch { }
855
870
  }
856
871
  }
857
872
  function isNativeMongoSuppressed(collection, op) {
@@ -861,9 +876,12 @@ function isNativeMongoSuppressed(collection, op) {
861
876
  const normalizedCollection = collection == null ? null : String(collection);
862
877
  const normalizedOp = normalizeNativeMongoSuppressionOp(op);
863
878
  const now = Date.now();
864
- for (const token of suppressions) {
865
- if (!token || now - token.startedAt > 60000)
879
+ for (let i = suppressions.length - 1; i >= 0; i -= 1) {
880
+ const token = suppressions[i];
881
+ if (!token || now - token.startedAt > 60000) {
882
+ suppressions.splice(i, 1);
866
883
  continue;
884
+ }
867
885
  if (token.collection && normalizedCollection && token.collection !== normalizedCollection)
868
886
  continue;
869
887
  if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp))
@@ -4634,40 +4652,44 @@ function reproMongoosePlugin(cfg) {
4634
4652
  next();
4635
4653
  });
4636
4654
  schema.post('save', { document: true }, function () {
4637
- const { sid, aid } = getCtx();
4638
- if (!sid || !aid)
4639
- return;
4640
- if (this.$isSubdocument)
4641
- return;
4642
- const meta = this.__repro_meta || {};
4643
- const before = null;
4644
- const after = this;
4645
- const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
4646
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4647
- if (!shouldCaptureDbSpan(spanContext))
4648
- return;
4649
- const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
4650
- const query = meta.wasNew
4651
- ? { op: 'insertOne', doc: after }
4652
- : {
4653
- filter: { _id: this._id },
4654
- update: buildModifiedPathUpdate(this, modifiedPaths),
4655
- options: { upsert: false },
4656
- };
4657
- emitDbQuery(cfg, sid, aid ?? undefined, {
4658
- collection,
4659
- pk: { _id: this._id },
4660
- before,
4661
- after,
4662
- op: meta.wasNew ? 'insert' : 'update',
4663
- query,
4664
- t: alignTimestamp(meta.t0 ?? Date.now()),
4665
- spanContext,
4666
- });
4667
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4655
+ try {
4656
+ const { sid, aid } = getCtx();
4657
+ if (!sid || !aid)
4658
+ return;
4659
+ if (this.$isSubdocument)
4660
+ return;
4661
+ const meta = this.__repro_meta || {};
4662
+ const before = null;
4663
+ const after = this;
4664
+ const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
4665
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4666
+ if (!shouldCaptureDbSpan(spanContext))
4667
+ return;
4668
+ const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
4669
+ const query = meta.wasNew
4670
+ ? { op: 'insertOne', doc: after }
4671
+ : {
4672
+ filter: { _id: this._id },
4673
+ update: buildModifiedPathUpdate(this, modifiedPaths),
4674
+ options: { upsert: false },
4675
+ };
4676
+ emitDbQuery(cfg, sid, aid ?? undefined, {
4677
+ collection,
4678
+ pk: { _id: this._id },
4679
+ before,
4680
+ after,
4681
+ op: meta.wasNew ? 'insert' : 'update',
4682
+ query,
4683
+ t: alignTimestamp(meta.t0 ?? Date.now()),
4684
+ spanContext,
4685
+ });
4686
+ }
4687
+ finally {
4688
+ releaseNativeMongoSuppression(this);
4689
+ }
4668
4690
  });
4669
4691
  schema.post('save', { document: true }, function (error, _doc, next) {
4670
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4692
+ releaseNativeMongoSuppression(this);
4671
4693
  next(error);
4672
4694
  });
4673
4695
  // -------- findOneAndUpdate capture (unchanged) --------
@@ -4689,34 +4711,38 @@ function reproMongoosePlugin(cfg) {
4689
4711
  next();
4690
4712
  });
4691
4713
  schema.post('findOneAndUpdate', function (res) {
4692
- const { sid, aid } = getCtx();
4693
- if (!sid || !aid)
4694
- return;
4695
- const before = null;
4696
- const after = res ?? null;
4697
- const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
4698
- const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
4699
- if (!shouldCaptureDbSpan(spanContext))
4700
- return;
4701
- const filter = this.__repro_filter;
4702
- const pk = after?._id ?? (filter && typeof filter === 'object' ? filter._id : undefined);
4703
- emitDbQuery(cfg, sid, aid ?? undefined, {
4704
- collection,
4705
- pk: { _id: pk },
4706
- before,
4707
- after,
4708
- op: 'update',
4709
- queryRaw: {
4710
- filter,
4711
- update: this.__repro_update,
4712
- },
4713
- t: alignTimestamp(this.__repro_t0 ?? Date.now()),
4714
- spanContext,
4715
- });
4716
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4714
+ try {
4715
+ const { sid, aid } = getCtx();
4716
+ if (!sid || !aid)
4717
+ return;
4718
+ const before = null;
4719
+ const after = res ?? null;
4720
+ const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
4721
+ const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
4722
+ if (!shouldCaptureDbSpan(spanContext))
4723
+ return;
4724
+ const filter = this.__repro_filter;
4725
+ const pk = after?._id ?? (filter && typeof filter === 'object' ? filter._id : undefined);
4726
+ emitDbQuery(cfg, sid, aid ?? undefined, {
4727
+ collection,
4728
+ pk: { _id: pk },
4729
+ before,
4730
+ after,
4731
+ op: 'update',
4732
+ queryRaw: {
4733
+ filter,
4734
+ update: this.__repro_update,
4735
+ },
4736
+ t: alignTimestamp(this.__repro_t0 ?? Date.now()),
4737
+ spanContext,
4738
+ });
4739
+ }
4740
+ finally {
4741
+ releaseNativeMongoSuppression(this);
4742
+ }
4717
4743
  });
4718
4744
  schema.post('findOneAndUpdate', function (error, _res, next) {
4719
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4745
+ releaseNativeMongoSuppression(this);
4720
4746
  next(error);
4721
4747
  });
4722
4748
  // -------- deleteOne capture (unchanged) --------
@@ -4736,29 +4762,33 @@ function reproMongoosePlugin(cfg) {
4736
4762
  next();
4737
4763
  });
4738
4764
  schema.post('deleteOne', { document: false, query: true }, function () {
4739
- const { sid, aid } = getCtx();
4740
- if (!sid || !aid)
4741
- return;
4742
- const before = null;
4743
- const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
4744
- const filter = this.__repro_filter ?? undefined;
4745
- const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
4746
- if (!shouldCaptureDbSpan(spanContext))
4747
- return;
4748
- emitDbQuery(cfg, sid, aid ?? undefined, {
4749
- collection,
4750
- pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: filter._id } : undefined,
4751
- before,
4752
- after: null,
4753
- op: 'delete',
4754
- queryRaw: { filter },
4755
- t: alignTimestamp(this.__repro_t0 ?? Date.now()),
4756
- spanContext,
4757
- });
4758
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4765
+ try {
4766
+ const { sid, aid } = getCtx();
4767
+ if (!sid || !aid)
4768
+ return;
4769
+ const before = null;
4770
+ const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
4771
+ const filter = this.__repro_filter ?? undefined;
4772
+ const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
4773
+ if (!shouldCaptureDbSpan(spanContext))
4774
+ return;
4775
+ emitDbQuery(cfg, sid, aid ?? undefined, {
4776
+ collection,
4777
+ pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: filter._id } : undefined,
4778
+ before,
4779
+ after: null,
4780
+ op: 'delete',
4781
+ queryRaw: { filter },
4782
+ t: alignTimestamp(this.__repro_t0 ?? Date.now()),
4783
+ spanContext,
4784
+ });
4785
+ }
4786
+ finally {
4787
+ releaseNativeMongoSuppression(this);
4788
+ }
4759
4789
  });
4760
4790
  schema.post('deleteOne', { document: false, query: true }, function (error, _res, next) {
4761
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4791
+ releaseNativeMongoSuppression(this);
4762
4792
  next(error);
4763
4793
  });
4764
4794
  // -------- NON-intrusive generic query telemetry via schema hooks -------
@@ -4814,29 +4844,33 @@ function reproMongoosePlugin(cfg) {
4814
4844
  next();
4815
4845
  });
4816
4846
  schema.post(op, function (res) {
4817
- const { sid, aid } = getCtx();
4818
- if (!sid)
4819
- return;
4820
- const meta = this.__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
4821
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4822
- emitDbQuery(cfg, sid, aid, {
4823
- collection: meta.collection,
4824
- op,
4825
- queryRaw: {
4826
- filter: meta.rawFilter,
4827
- update: meta.rawUpdate,
4828
- projection: meta.rawProjection,
4829
- options: meta.rawOptions,
4830
- },
4831
- resultMetaRaw: res,
4832
- durMs: Date.now() - meta.t0,
4833
- t: alignedNow(),
4834
- spanContext,
4835
- });
4836
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4847
+ try {
4848
+ const { sid, aid } = getCtx();
4849
+ if (!sid)
4850
+ return;
4851
+ const meta = this.__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
4852
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4853
+ emitDbQuery(cfg, sid, aid, {
4854
+ collection: meta.collection,
4855
+ op,
4856
+ queryRaw: {
4857
+ filter: meta.rawFilter,
4858
+ update: meta.rawUpdate,
4859
+ projection: meta.rawProjection,
4860
+ options: meta.rawOptions,
4861
+ },
4862
+ resultMetaRaw: res,
4863
+ durMs: Date.now() - meta.t0,
4864
+ t: alignedNow(),
4865
+ spanContext,
4866
+ });
4867
+ }
4868
+ finally {
4869
+ releaseNativeMongoSuppression(this);
4870
+ }
4837
4871
  });
4838
4872
  schema.post(op, function (error, _res, next) {
4839
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4873
+ releaseNativeMongoSuppression(this);
4840
4874
  next(error);
4841
4875
  });
4842
4876
  }
@@ -4865,24 +4899,28 @@ function reproMongoosePlugin(cfg) {
4865
4899
  next();
4866
4900
  });
4867
4901
  schema.post('insertMany', { document: false, query: false }, function (docs) {
4868
- const { sid, aid } = getCtx();
4869
- if (!sid)
4870
- return;
4871
- const meta = this.__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
4872
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4873
- emitDbQuery(cfg, sid, aid ?? undefined, {
4874
- collection: meta.collection,
4875
- op: 'insertMany',
4876
- queryRaw: { docs: meta.rawDocs ?? undefined },
4877
- resultMetaRaw: docs,
4878
- durMs: Date.now() - meta.t0,
4879
- t: alignedNow(),
4880
- spanContext,
4881
- });
4882
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4902
+ try {
4903
+ const { sid, aid } = getCtx();
4904
+ if (!sid)
4905
+ return;
4906
+ const meta = this.__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
4907
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4908
+ emitDbQuery(cfg, sid, aid ?? undefined, {
4909
+ collection: meta.collection,
4910
+ op: 'insertMany',
4911
+ queryRaw: { docs: meta.rawDocs ?? undefined },
4912
+ resultMetaRaw: docs,
4913
+ durMs: Date.now() - meta.t0,
4914
+ t: alignedNow(),
4915
+ spanContext,
4916
+ });
4917
+ }
4918
+ finally {
4919
+ releaseNativeMongoSuppression(this);
4920
+ }
4883
4921
  });
4884
4922
  schema.post('insertMany', { document: false, query: false }, function (error, _res, next) {
4885
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4923
+ releaseNativeMongoSuppression(this);
4886
4924
  next(error);
4887
4925
  });
4888
4926
  schema.pre('bulkWrite', { document: false, query: false }, function (next, ops) {
@@ -4907,24 +4945,28 @@ function reproMongoosePlugin(cfg) {
4907
4945
  next();
4908
4946
  });
4909
4947
  schema.post('bulkWrite', { document: false, query: false }, function (res) {
4910
- const { sid, aid } = getCtx();
4911
- if (!sid)
4912
- return;
4913
- const meta = this.__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
4914
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4915
- emitDbQuery(cfg, sid, aid, {
4916
- collection: meta.collection,
4917
- op: 'bulkWrite',
4918
- queryRaw: { ops: meta.rawOps ?? undefined },
4919
- resultMetaRaw: res,
4920
- durMs: Date.now() - meta.t0,
4921
- t: alignedNow(),
4922
- spanContext,
4923
- });
4924
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4948
+ try {
4949
+ const { sid, aid } = getCtx();
4950
+ if (!sid)
4951
+ return;
4952
+ const meta = this.__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
4953
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4954
+ emitDbQuery(cfg, sid, aid, {
4955
+ collection: meta.collection,
4956
+ op: 'bulkWrite',
4957
+ queryRaw: { ops: meta.rawOps ?? undefined },
4958
+ resultMetaRaw: res,
4959
+ durMs: Date.now() - meta.t0,
4960
+ t: alignedNow(),
4961
+ spanContext,
4962
+ });
4963
+ }
4964
+ finally {
4965
+ releaseNativeMongoSuppression(this);
4966
+ }
4925
4967
  });
4926
4968
  schema.post('bulkWrite', { document: false, query: false }, function (error, _res, next) {
4927
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4969
+ releaseNativeMongoSuppression(this);
4928
4970
  next(error);
4929
4971
  });
4930
4972
  // Aggregate middleware (non-intrusive)
@@ -4954,24 +4996,28 @@ function reproMongoosePlugin(cfg) {
4954
4996
  next();
4955
4997
  });
4956
4998
  schema.post('aggregate', function (res) {
4957
- const { sid, aid } = getCtx();
4958
- if (!sid)
4959
- return;
4960
- const meta = this.__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
4961
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
4962
- emitDbQuery(cfg, sid, aid, {
4963
- collection: meta.collection,
4964
- op: 'aggregate',
4965
- queryRaw: { pipeline: meta.rawPipeline },
4966
- resultMetaRaw: res,
4967
- durMs: Date.now() - meta.t0,
4968
- t: alignedNow(),
4969
- spanContext,
4970
- });
4971
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
4999
+ try {
5000
+ const { sid, aid } = getCtx();
5001
+ if (!sid)
5002
+ return;
5003
+ const meta = this.__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
5004
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5005
+ emitDbQuery(cfg, sid, aid, {
5006
+ collection: meta.collection,
5007
+ op: 'aggregate',
5008
+ queryRaw: { pipeline: meta.rawPipeline },
5009
+ resultMetaRaw: res,
5010
+ durMs: Date.now() - meta.t0,
5011
+ t: alignedNow(),
5012
+ spanContext,
5013
+ });
5014
+ }
5015
+ finally {
5016
+ releaseNativeMongoSuppression(this);
5017
+ }
4972
5018
  });
4973
5019
  schema.post('aggregate', function (error, _res, next) {
4974
- popNativeMongoSuppression(this.__repro_native_mongo_suppression);
5020
+ releaseNativeMongoSuppression(this);
4975
5021
  next(error);
4976
5022
  });
4977
5023
  };
@@ -8721,6 +8767,16 @@ exports.__reproTestHooks = {
8721
8767
  lastClientActivityAt = 0;
8722
8768
  sdkBackgroundQuietUntil = 0;
8723
8769
  },
8770
+ runWithRequestContextForTest(ctx, fn) {
8771
+ return als.run({
8772
+ ...ctx,
8773
+ excludedSpanIds: ctx.excludedSpanIds ?? new Set(),
8774
+ }, fn);
8775
+ },
8776
+ getNativeMongoSuppressionCountForTest() {
8777
+ const suppressions = getCtx().nativeMongoSuppressions;
8778
+ return Array.isArray(suppressions) ? suppressions.length : 0;
8779
+ },
8724
8780
  getRuntimePrivacyPolicyForTest(cfg) {
8725
8781
  return getRuntimePrivacyState(cfg).policy;
8726
8782
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "build": "tsc -p tsconfig.json",
13
13
  "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
14
14
  "prepublishOnly": "npm run build",
15
- "test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js",
15
+ "test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js",
16
16
  "test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js"
17
17
  },
18
18
  "peerDependencies": {
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,12 +1248,22 @@ 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
 
@@ -1261,8 +1273,12 @@ function isNativeMongoSuppressed(collection: any, op: string): boolean {
1261
1273
  const normalizedCollection = collection == null ? null : String(collection);
1262
1274
  const normalizedOp = normalizeNativeMongoSuppressionOp(op);
1263
1275
  const now = Date.now();
1264
- for (const token of suppressions) {
1265
- if (!token || now - token.startedAt > 60000) continue;
1276
+ for (let i = suppressions.length - 1; i >= 0; i -= 1) {
1277
+ const token = suppressions[i];
1278
+ if (!token || now - token.startedAt > 60000) {
1279
+ suppressions.splice(i, 1);
1280
+ continue;
1281
+ }
1266
1282
  if (token.collection && normalizedCollection && token.collection !== normalizedCollection) continue;
1267
1283
  if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp)) continue;
1268
1284
  return true;
@@ -5572,41 +5588,44 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5572
5588
  });
5573
5589
 
5574
5590
  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
- };
5591
+ try {
5592
+ const { sid, aid } = getCtx();
5593
+ if (!sid || !aid) return;
5594
+ if ((this as any).$isSubdocument) return;
5594
5595
 
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);
5596
+ const meta = (this as any).__repro_meta || {};
5597
+ const before = null;
5598
+ const after = this;
5599
+ const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
5600
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5601
+ if (!shouldCaptureDbSpan(spanContext)) return;
5602
+ const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
5603
+
5604
+ const query = meta.wasNew
5605
+ ? { op: 'insertOne', doc: after }
5606
+ : {
5607
+ filter: { _id: this._id },
5608
+ update: buildModifiedPathUpdate(this, modifiedPaths),
5609
+ options: { upsert: false },
5610
+ };
5611
+
5612
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5613
+ collection,
5614
+ pk: { _id: (this as any)._id },
5615
+ before,
5616
+ after,
5617
+ op: meta.wasNew ? 'insert' : 'update',
5618
+ query,
5619
+ t: alignTimestamp(meta.t0 ?? Date.now()),
5620
+ spanContext,
5621
+ });
5622
+ } finally {
5623
+ releaseNativeMongoSuppression(this);
5624
+ }
5606
5625
  });
5607
5626
 
5608
5627
  schema.post('save', { document: true }, function (error: any, _doc: any, next: Function) {
5609
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5628
+ releaseNativeMongoSuppression(this);
5610
5629
  next(error);
5611
5630
  });
5612
5631
 
@@ -5631,35 +5650,38 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5631
5650
  });
5632
5651
 
5633
5652
  schema.post<Query<any, any>>('findOneAndUpdate', function (res: any) {
5634
- const { sid, aid } = getCtx();
5635
- if (!sid || !aid) return;
5653
+ try {
5654
+ const { sid, aid } = getCtx();
5655
+ if (!sid || !aid) return;
5636
5656
 
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);
5657
+ const before = null;
5658
+ const after = res ?? null;
5659
+ const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5660
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5661
+ if (!shouldCaptureDbSpan(spanContext)) return;
5662
+ const filter = (this as any).__repro_filter;
5663
+ const pk = after?._id ?? (filter && typeof filter === 'object' ? (filter as any)._id : undefined);
5644
5664
 
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);
5665
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5666
+ collection,
5667
+ pk: { _id: pk },
5668
+ before,
5669
+ after,
5670
+ op: 'update',
5671
+ queryRaw: {
5672
+ filter,
5673
+ update: (this as any).__repro_update,
5674
+ },
5675
+ t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5676
+ spanContext,
5677
+ });
5678
+ } finally {
5679
+ releaseNativeMongoSuppression(this);
5680
+ }
5659
5681
  });
5660
5682
 
5661
5683
  schema.post<Query<any, any>>('findOneAndUpdate', function (this: any, error: any, _res: any, next: Function) {
5662
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5684
+ releaseNativeMongoSuppression(this);
5663
5685
  next(error);
5664
5686
  } as any);
5665
5687
 
@@ -5681,27 +5703,30 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5681
5703
  });
5682
5704
 
5683
5705
  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);
5706
+ try {
5707
+ const { sid, aid } = getCtx(); if (!sid || !aid) return;
5708
+ const before = null;
5709
+ const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
5710
+ const filter = (this as any).__repro_filter ?? undefined;
5711
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
5712
+ if (!shouldCaptureDbSpan(spanContext)) return;
5713
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5714
+ collection,
5715
+ pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: (filter as any)._id } : undefined,
5716
+ before,
5717
+ after: null,
5718
+ op: 'delete',
5719
+ queryRaw: { filter },
5720
+ t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
5721
+ spanContext,
5722
+ });
5723
+ } finally {
5724
+ releaseNativeMongoSuppression(this);
5725
+ }
5701
5726
  });
5702
5727
 
5703
5728
  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);
5729
+ releaseNativeMongoSuppression(this);
5705
5730
  next(error);
5706
5731
  } as any);
5707
5732
 
@@ -5766,31 +5791,34 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5766
5791
  });
5767
5792
 
5768
5793
  schema.post(op as any, function (this: any, res: any) {
5769
- const { sid, aid } = getCtx();
5770
- if (!sid) return;
5794
+ try {
5795
+ const { sid, aid } = getCtx();
5796
+ if (!sid) return;
5771
5797
 
5772
- const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
5773
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5798
+ const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
5799
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5774
5800
 
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);
5801
+ emitDbQuery(cfg, sid, aid, {
5802
+ collection: meta.collection,
5803
+ op,
5804
+ queryRaw: {
5805
+ filter: meta.rawFilter,
5806
+ update: meta.rawUpdate,
5807
+ projection: meta.rawProjection,
5808
+ options: meta.rawOptions,
5809
+ },
5810
+ resultMetaRaw: res,
5811
+ durMs: Date.now() - meta.t0,
5812
+ t: alignedNow(),
5813
+ spanContext,
5814
+ });
5815
+ } finally {
5816
+ releaseNativeMongoSuppression(this);
5817
+ }
5790
5818
  });
5791
5819
 
5792
5820
  schema.post(op as any, function (this: any, error: any, _res: any, next: Function) {
5793
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5821
+ releaseNativeMongoSuppression(this);
5794
5822
  next(error);
5795
5823
  } as any);
5796
5824
  }
@@ -5824,25 +5852,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5824
5852
  } as any);
5825
5853
 
5826
5854
  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);
5855
+ try {
5856
+ const { sid, aid } = getCtx();
5857
+ if (!sid) return;
5858
+ const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
5859
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5860
+
5861
+ emitDbQuery(cfg, sid, aid ?? undefined, {
5862
+ collection: meta.collection,
5863
+ op: 'insertMany',
5864
+ queryRaw: { docs: meta.rawDocs ?? undefined },
5865
+ resultMetaRaw: docs,
5866
+ durMs: Date.now() - meta.t0,
5867
+ t: alignedNow(),
5868
+ spanContext,
5869
+ });
5870
+ } finally {
5871
+ releaseNativeMongoSuppression(this);
5872
+ }
5842
5873
  } as any);
5843
5874
 
5844
5875
  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);
5876
+ releaseNativeMongoSuppression(this);
5846
5877
  next(error);
5847
5878
  } as any);
5848
5879
 
@@ -5871,25 +5902,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5871
5902
  } as any);
5872
5903
 
5873
5904
  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);
5905
+ try {
5906
+ const { sid, aid } = getCtx();
5907
+ if (!sid) return;
5908
+ const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
5909
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5910
+
5911
+ emitDbQuery(cfg, sid, aid, {
5912
+ collection: meta.collection,
5913
+ op: 'bulkWrite',
5914
+ queryRaw: { ops: meta.rawOps ?? undefined },
5915
+ resultMetaRaw: res,
5916
+ durMs: Date.now() - meta.t0,
5917
+ t: alignedNow(),
5918
+ spanContext,
5919
+ });
5920
+ } finally {
5921
+ releaseNativeMongoSuppression(this);
5922
+ }
5889
5923
  } as any);
5890
5924
 
5891
5925
  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);
5926
+ releaseNativeMongoSuppression(this);
5893
5927
  next(error);
5894
5928
  } as any);
5895
5929
 
@@ -5924,26 +5958,29 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
5924
5958
  });
5925
5959
 
5926
5960
  schema.post('aggregate', function (this: any, res: any[]) {
5927
- const { sid, aid } = getCtx();
5928
- if (!sid) return;
5961
+ try {
5962
+ const { sid, aid } = getCtx();
5963
+ if (!sid) return;
5929
5964
 
5930
- const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
5931
- const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5965
+ const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
5966
+ const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
5932
5967
 
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);
5968
+ emitDbQuery(cfg, sid, aid, {
5969
+ collection: meta.collection,
5970
+ op: 'aggregate',
5971
+ queryRaw: { pipeline: meta.rawPipeline },
5972
+ resultMetaRaw: res,
5973
+ durMs: Date.now() - meta.t0,
5974
+ t: alignedNow(),
5975
+ spanContext,
5976
+ });
5977
+ } finally {
5978
+ releaseNativeMongoSuppression(this);
5979
+ }
5943
5980
  });
5944
5981
 
5945
5982
  schema.post('aggregate', function (this: any, error: any, _res: any, next: Function) {
5946
- popNativeMongoSuppression((this as any).__repro_native_mongo_suppression);
5983
+ releaseNativeMongoSuppression(this);
5947
5984
  next(error);
5948
5985
  } as any);
5949
5986
  };
@@ -9966,6 +10003,16 @@ export const __reproTestHooks = {
9966
10003
  lastClientActivityAt = 0;
9967
10004
  sdkBackgroundQuietUntil = 0;
9968
10005
  },
10006
+ runWithRequestContextForTest<T>(ctx: Partial<Ctx>, fn: () => T): T {
10007
+ return als.run({
10008
+ ...ctx,
10009
+ excludedSpanIds: ctx.excludedSpanIds ?? new Set<string>(),
10010
+ }, fn);
10011
+ },
10012
+ getNativeMongoSuppressionCountForTest(): number {
10013
+ const suppressions = getCtx().nativeMongoSuppressions;
10014
+ return Array.isArray(suppressions) ? suppressions.length : 0;
10015
+ },
9969
10016
  getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null {
9970
10017
  return getRuntimePrivacyState(cfg).policy;
9971
10018
  },
@@ -0,0 +1,72 @@
1
+ const assert = require('assert');
2
+ const mongoose = require('mongoose');
3
+ const { reproMongoosePlugin, __reproTestHooks } = require('../dist');
4
+
5
+ function execPre(hooks, name, context) {
6
+ return new Promise((resolve, reject) => {
7
+ hooks.execPre(name, context, [], (error) => (error ? reject(error) : resolve()));
8
+ });
9
+ }
10
+
11
+ function execPost(hooks, name, context, args = []) {
12
+ return new Promise((resolve, reject) => {
13
+ hooks.execPost(name, context, args, {}, (error) => (error ? reject(error) : resolve()));
14
+ });
15
+ }
16
+
17
+ async function testPostSaveEarlyReturnReleasesSuppression() {
18
+ const schema = new mongoose.Schema({ name: String });
19
+ reproMongoosePlugin({
20
+ tenantId: 'TENANT_test',
21
+ appId: 'APP_test',
22
+ appSecret: 'secret',
23
+ privacy: { environment: 'dev', captureRaw: true },
24
+ ingestBase: 'http://127.0.0.1:65535',
25
+ })(schema);
26
+ const Model = mongoose.model(`ReproSuppressionCleanup${Date.now()}`, schema, 'subjects');
27
+ const doc = new Model({ name: 'subject' });
28
+
29
+ await __reproTestHooks.runWithRequestContextForTest({ sid: 'S_cleanup', aid: 'A_cleanup' }, async () => {
30
+ await execPre(schema.s.hooks, 'save', doc);
31
+ assert.equal(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 1);
32
+
33
+ // No rid/span is present, so the post hook exits through shouldCaptureDbSpan(false).
34
+ await execPost(schema.s.hooks, 'save', doc, [doc]);
35
+ assert.equal(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 0);
36
+ });
37
+ }
38
+
39
+ async function testPostSaveOutsideOriginalAlsReleasesOwnerStoreSuppression() {
40
+ const schema = new mongoose.Schema({ name: String });
41
+ reproMongoosePlugin({
42
+ tenantId: 'TENANT_test',
43
+ appId: 'APP_test',
44
+ appSecret: 'secret',
45
+ privacy: { environment: 'dev', captureRaw: true },
46
+ ingestBase: 'http://127.0.0.1:65535',
47
+ })(schema);
48
+ const Model = mongoose.model(`ReproSuppressionOwnerStore${Date.now()}`, schema, 'subjects');
49
+ const doc = new Model({ name: 'subject' });
50
+
51
+ let token;
52
+ await __reproTestHooks.runWithRequestContextForTest({ sid: 'S_owner', aid: 'A_owner', rid: 'R_owner' }, async () => {
53
+ await execPre(schema.s.hooks, 'save', doc);
54
+ assert.equal(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 1);
55
+ token = doc.__repro_native_mongo_suppression;
56
+ assert.equal(token.ownerStore.nativeMongoSuppressions.length, 1);
57
+ });
58
+
59
+ await execPost(schema.s.hooks, 'save', doc, [doc]);
60
+ assert.equal(token.ownerStore, undefined);
61
+ }
62
+
63
+ async function main() {
64
+ await testPostSaveEarlyReturnReleasesSuppression();
65
+ await testPostSaveOutsideOriginalAlsReleasesOwnerStoreSuppression();
66
+ console.log('mongodb suppression cleanup OK');
67
+ }
68
+
69
+ main().catch((error) => {
70
+ console.error(error);
71
+ process.exit(1);
72
+ });