@reproapp/node-sdk 0.0.30 → 0.0.31

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
@@ -6,6 +6,7 @@ const async_hooks_1 = require("async_hooks");
6
6
  const crypto_1 = require("crypto");
7
7
  const module_1 = require("module");
8
8
  const path_1 = require("path");
9
+ const url_1 = require("url");
9
10
  const client_1 = require("./ingest/client");
10
11
  const privacy_1 = require("./privacy");
11
12
  /**
@@ -7599,6 +7600,8 @@ function patchRedis(cfg) {
7599
7600
  exports.patchRedis = patchRedis;
7600
7601
  const mongoDbPatchConfigs = [];
7601
7602
  let mongoDbRequireHookInstalled = false;
7603
+ let mongoDbImportHookInstalled = false;
7604
+ let mongoDbImportHookReady = null;
7602
7605
  const MONGODB_PROMISE_OPS = [
7603
7606
  'insertOne',
7604
7607
  'insertMany',
@@ -7618,6 +7621,11 @@ const MONGODB_PROMISE_OPS = [
7618
7621
  ];
7619
7622
  const MONGODB_CURSOR_OPS = ['find', 'aggregate'];
7620
7623
  const MONGODB_CURSOR_TERMINALS = ['toArray', 'next', 'forEach'];
7624
+ function registerMongoDbPatchConfig(cfg) {
7625
+ if (!mongoDbPatchConfigs.includes(cfg)) {
7626
+ mongoDbPatchConfigs.push(cfg);
7627
+ }
7628
+ }
7621
7629
  function resolveMongoDbRuntimeContext(cfg) {
7622
7630
  const ctx = getCtx();
7623
7631
  const fallback = cfg.resolveContext?.() ?? {};
@@ -7627,6 +7635,18 @@ function resolveMongoDbRuntimeContext(cfg) {
7627
7635
  aid: ctx.aid ?? fallback.aid ?? null,
7628
7636
  };
7629
7637
  }
7638
+ function resolveMongoDbCaptureContext() {
7639
+ for (let i = mongoDbPatchConfigs.length - 1; i >= 0; i -= 1) {
7640
+ const cfg = mongoDbPatchConfigs[i];
7641
+ if (!cfg)
7642
+ continue;
7643
+ const { sid, aid } = resolveMongoDbRuntimeContext(cfg);
7644
+ if (sid) {
7645
+ return { cfg, sid, aid };
7646
+ }
7647
+ }
7648
+ return null;
7649
+ }
7630
7650
  function resolveMongoCollectionName(collection) {
7631
7651
  const candidates = [
7632
7652
  collection?.collectionName,
@@ -7911,10 +7931,11 @@ function patchMongoCollectionMethod(proto, op, cursorOp, cfg) {
7911
7931
  if (isNativeMongoSuppressed(collection, op)) {
7912
7932
  return original.apply(this, args);
7913
7933
  }
7914
- const { sid, aid } = resolveMongoDbRuntimeContext(cfg);
7915
- if (!sid) {
7934
+ const captureContext = resolveMongoDbCaptureContext();
7935
+ if (!captureContext) {
7916
7936
  return original.apply(this, args);
7917
7937
  }
7938
+ const { cfg: activeCfg, sid, aid } = captureContext;
7918
7939
  const spanContext = captureSpanContextFromTracer(this);
7919
7940
  const t0 = Date.now();
7920
7941
  const dbName = resolveMongoDbName(this);
@@ -7925,7 +7946,7 @@ function patchMongoCollectionMethod(proto, op, cursorOp, cfg) {
7925
7946
  if (emitted)
7926
7947
  return;
7927
7948
  emitted = true;
7928
- emitDbQuery(cfg, sid, aid ?? undefined, {
7949
+ emitDbQuery(activeCfg, sid, aid ?? undefined, {
7929
7950
  type: 'mongo',
7930
7951
  collection,
7931
7952
  op,
@@ -7943,7 +7964,7 @@ function patchMongoCollectionMethod(proto, op, cursorOp, cfg) {
7943
7964
  if (emitted)
7944
7965
  return;
7945
7966
  emitted = true;
7946
- emitDbQuery(cfg, sid, aid ?? undefined, {
7967
+ emitDbQuery(activeCfg, sid, aid ?? undefined, {
7947
7968
  type: 'mongo',
7948
7969
  collection,
7949
7970
  op,
@@ -7968,7 +7989,7 @@ function patchMongoCollectionMethod(proto, op, cursorOp, cfg) {
7968
7989
  const result = original.apply(this, actualArgs);
7969
7990
  if (cursorOp) {
7970
7991
  return patchMongoCursorInstance(result, {
7971
- cfg,
7992
+ cfg: activeCfg,
7972
7993
  sid,
7973
7994
  aid,
7974
7995
  collection,
@@ -8053,6 +8074,7 @@ function patchMongoClientPrototype(proto, cfg) {
8053
8074
  };
8054
8075
  }
8055
8076
  function patchMongoDbFromModuleExports(mongoModule, cfg) {
8077
+ registerMongoDbPatchConfig(cfg);
8056
8078
  if (!mongoModule)
8057
8079
  return;
8058
8080
  const constructors = new Set();
@@ -8108,9 +8130,46 @@ function installMongoDbRequireHook() {
8108
8130
  }
8109
8131
  catch { }
8110
8132
  }
8133
+ function installMongoDbImportHook() {
8134
+ if (mongoDbImportHookInstalled)
8135
+ return mongoDbImportHookReady ?? Promise.resolve();
8136
+ mongoDbImportHookInstalled = true;
8137
+ mongoDbImportHookReady = (async () => {
8138
+ try {
8139
+ const nodeModule = requireOptionalHostModule('node:module') ?? requireOptionalHostModule('module');
8140
+ if (typeof nodeModule?.register !== 'function')
8141
+ return;
8142
+ const iitm = requireOptionalHostModule('import-in-the-middle');
8143
+ const Hook = iitm?.Hook ?? iitm;
8144
+ if (typeof Hook !== 'function')
8145
+ return;
8146
+ let waitForAllMessagesAcknowledged;
8147
+ let registerOptions = { data: { include: ['mongodb'] } };
8148
+ if (typeof iitm?.createAddHookMessageChannel === 'function') {
8149
+ const channel = iitm.createAddHookMessageChannel();
8150
+ registerOptions = channel.registerOptions;
8151
+ waitForAllMessagesAcknowledged = channel.waitForAllMessagesAcknowledged;
8152
+ }
8153
+ nodeModule.register('import-in-the-middle/hook.mjs', (0, url_1.pathToFileURL)(__filename), registerOptions);
8154
+ // eslint-disable-next-line no-new
8155
+ new Hook(['mongodb'], (exports) => {
8156
+ for (const cfg of mongoDbPatchConfigs) {
8157
+ patchMongoDbFromModuleExports(exports, cfg);
8158
+ }
8159
+ return exports;
8160
+ });
8161
+ if (waitForAllMessagesAcknowledged) {
8162
+ await waitForAllMessagesAcknowledged();
8163
+ }
8164
+ }
8165
+ catch { }
8166
+ })();
8167
+ return mongoDbImportHookReady;
8168
+ }
8111
8169
  function patchMongoDb(cfg) {
8112
- mongoDbPatchConfigs.push(cfg);
8170
+ registerMongoDbPatchConfig(cfg);
8113
8171
  installMongoDbRequireHook();
8172
+ void installMongoDbImportHook();
8114
8173
  const mongoModules = new Set();
8115
8174
  for (const loadedMongoModule of getLoadedModuleExportsByPackage('mongodb')) {
8116
8175
  mongoModules.add(loadedMongoModule);
@@ -8628,6 +8687,7 @@ async function initRepro(cfg) {
8628
8687
  patchAmqplib(integrationCfg);
8629
8688
  patchRedis(integrationCfg);
8630
8689
  patchMongoDb(integrationCfg);
8690
+ await installMongoDbImportHook();
8631
8691
  patchSendgridMail(integrationCfg);
8632
8692
  patchMongooseIntegration(reproAutoMiddlewareConfig);
8633
8693
  await initializeRuntimePrivacyPolicyAtStartup(reproAutoMiddlewareConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
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",
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",
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
@@ -5,6 +5,7 @@ import { AsyncLocalStorage } from 'async_hooks';
5
5
  import { randomUUID } from 'crypto';
6
6
  import { createRequire } from 'module';
7
7
  import { join } from 'path';
8
+ import { pathToFileURL } from 'url';
8
9
  import {
9
10
  configureIngestQueueDrain,
10
11
  enqueueIngestEntries,
@@ -8810,6 +8811,8 @@ export type MongoDbPatchConfig = ReproMiddlewareConfig & {
8810
8811
 
8811
8812
  const mongoDbPatchConfigs: MongoDbPatchConfig[] = [];
8812
8813
  let mongoDbRequireHookInstalled = false;
8814
+ let mongoDbImportHookInstalled = false;
8815
+ let mongoDbImportHookReady: Promise<void> | null = null;
8813
8816
 
8814
8817
  const MONGODB_PROMISE_OPS = [
8815
8818
  'insertOne',
@@ -8832,6 +8835,12 @@ const MONGODB_PROMISE_OPS = [
8832
8835
  const MONGODB_CURSOR_OPS = ['find', 'aggregate'] as const;
8833
8836
  const MONGODB_CURSOR_TERMINALS = ['toArray', 'next', 'forEach'] as const;
8834
8837
 
8838
+ function registerMongoDbPatchConfig(cfg: MongoDbPatchConfig): void {
8839
+ if (!mongoDbPatchConfigs.includes(cfg)) {
8840
+ mongoDbPatchConfigs.push(cfg);
8841
+ }
8842
+ }
8843
+
8835
8844
  function resolveMongoDbRuntimeContext(cfg: MongoDbPatchConfig) {
8836
8845
  const ctx = getCtx();
8837
8846
  const fallback = cfg.resolveContext?.() ?? {};
@@ -8842,6 +8851,18 @@ function resolveMongoDbRuntimeContext(cfg: MongoDbPatchConfig) {
8842
8851
  };
8843
8852
  }
8844
8853
 
8854
+ function resolveMongoDbCaptureContext(): { cfg: MongoDbPatchConfig; sid: string; aid: string | null } | null {
8855
+ for (let i = mongoDbPatchConfigs.length - 1; i >= 0; i -= 1) {
8856
+ const cfg = mongoDbPatchConfigs[i];
8857
+ if (!cfg) continue;
8858
+ const { sid, aid } = resolveMongoDbRuntimeContext(cfg);
8859
+ if (sid) {
8860
+ return { cfg, sid, aid };
8861
+ }
8862
+ }
8863
+ return null;
8864
+ }
8865
+
8845
8866
  function resolveMongoCollectionName(collection: any): string {
8846
8867
  const candidates = [
8847
8868
  collection?.collectionName,
@@ -9142,10 +9163,11 @@ function patchMongoCollectionMethod(proto: any, op: string, cursorOp: boolean, c
9142
9163
  return original.apply(this, args);
9143
9164
  }
9144
9165
 
9145
- const { sid, aid } = resolveMongoDbRuntimeContext(cfg);
9146
- if (!sid) {
9166
+ const captureContext = resolveMongoDbCaptureContext();
9167
+ if (!captureContext) {
9147
9168
  return original.apply(this, args);
9148
9169
  }
9170
+ const { cfg: activeCfg, sid, aid } = captureContext;
9149
9171
 
9150
9172
  const spanContext = captureSpanContextFromTracer(this);
9151
9173
  const t0 = Date.now();
@@ -9157,7 +9179,7 @@ function patchMongoCollectionMethod(proto: any, op: string, cursorOp: boolean, c
9157
9179
  const emitSuccess = (result: any) => {
9158
9180
  if (emitted) return;
9159
9181
  emitted = true;
9160
- emitDbQuery(cfg, sid, aid ?? undefined, {
9182
+ emitDbQuery(activeCfg, sid, aid ?? undefined, {
9161
9183
  type: 'mongo',
9162
9184
  collection,
9163
9185
  op,
@@ -9174,7 +9196,7 @@ function patchMongoCollectionMethod(proto: any, op: string, cursorOp: boolean, c
9174
9196
  const emitFailure = (error: any) => {
9175
9197
  if (emitted) return;
9176
9198
  emitted = true;
9177
- emitDbQuery(cfg, sid, aid ?? undefined, {
9199
+ emitDbQuery(activeCfg, sid, aid ?? undefined, {
9178
9200
  type: 'mongo',
9179
9201
  collection,
9180
9202
  op,
@@ -9199,7 +9221,7 @@ function patchMongoCollectionMethod(proto: any, op: string, cursorOp: boolean, c
9199
9221
  const result = original.apply(this, actualArgs);
9200
9222
  if (cursorOp) {
9201
9223
  return patchMongoCursorInstance(result, {
9202
- cfg,
9224
+ cfg: activeCfg,
9203
9225
  sid,
9204
9226
  aid,
9205
9227
  collection,
@@ -9283,6 +9305,7 @@ function patchMongoClientPrototype(proto: any, cfg: MongoDbPatchConfig): void {
9283
9305
  }
9284
9306
 
9285
9307
  function patchMongoDbFromModuleExports(mongoModule: any, cfg: MongoDbPatchConfig): void {
9308
+ registerMongoDbPatchConfig(cfg);
9286
9309
  if (!mongoModule) return;
9287
9310
  const constructors = new Set<any>();
9288
9311
  const addCtor = (candidate: any) => {
@@ -9343,9 +9366,50 @@ function installMongoDbRequireHook(): void {
9343
9366
  } catch {}
9344
9367
  }
9345
9368
 
9369
+ function installMongoDbImportHook(): Promise<void> {
9370
+ if (mongoDbImportHookInstalled) return mongoDbImportHookReady ?? Promise.resolve();
9371
+ mongoDbImportHookInstalled = true;
9372
+
9373
+ mongoDbImportHookReady = (async () => {
9374
+ try {
9375
+ const nodeModule = requireOptionalHostModule('node:module') ?? requireOptionalHostModule('module');
9376
+ if (typeof nodeModule?.register !== 'function') return;
9377
+
9378
+ const iitm = requireOptionalHostModule('import-in-the-middle');
9379
+ const Hook = iitm?.Hook ?? iitm;
9380
+ if (typeof Hook !== 'function') return;
9381
+
9382
+ let waitForAllMessagesAcknowledged: undefined | (() => Promise<void>);
9383
+ let registerOptions: any = { data: { include: ['mongodb'] } };
9384
+ if (typeof iitm?.createAddHookMessageChannel === 'function') {
9385
+ const channel = iitm.createAddHookMessageChannel();
9386
+ registerOptions = channel.registerOptions;
9387
+ waitForAllMessagesAcknowledged = channel.waitForAllMessagesAcknowledged;
9388
+ }
9389
+
9390
+ nodeModule.register('import-in-the-middle/hook.mjs', pathToFileURL(__filename), registerOptions);
9391
+
9392
+ // eslint-disable-next-line no-new
9393
+ new Hook(['mongodb'], (exports: any) => {
9394
+ for (const cfg of mongoDbPatchConfigs) {
9395
+ patchMongoDbFromModuleExports(exports, cfg);
9396
+ }
9397
+ return exports;
9398
+ });
9399
+
9400
+ if (waitForAllMessagesAcknowledged) {
9401
+ await waitForAllMessagesAcknowledged();
9402
+ }
9403
+ } catch {}
9404
+ })();
9405
+
9406
+ return mongoDbImportHookReady;
9407
+ }
9408
+
9346
9409
  export function patchMongoDb(cfg: MongoDbPatchConfig): void {
9347
- mongoDbPatchConfigs.push(cfg);
9410
+ registerMongoDbPatchConfig(cfg);
9348
9411
  installMongoDbRequireHook();
9412
+ void installMongoDbImportHook();
9349
9413
 
9350
9414
  const mongoModules = new Set<any>();
9351
9415
  for (const loadedMongoModule of getLoadedModuleExportsByPackage('mongodb')) {
@@ -9867,6 +9931,7 @@ export async function initRepro(cfg: ReproInitConfig): Promise<void> {
9867
9931
  patchAmqplib(integrationCfg);
9868
9932
  patchRedis(integrationCfg);
9869
9933
  patchMongoDb(integrationCfg);
9934
+ await installMongoDbImportHook();
9870
9935
  patchSendgridMail(integrationCfg);
9871
9936
  patchMongooseIntegration(reproAutoMiddlewareConfig);
9872
9937
 
@@ -0,0 +1,207 @@
1
+ process.env.REPRO_INGEST_REQUEST_TIMEOUT_MS = '50';
2
+ process.env.REPRO_SDK_BACKGROUND_MAX_DEFER_MS = '25';
3
+ process.env.REPRO_SDK_BACKGROUND_IDLE_DELAY_MS = '0';
4
+ process.env.REPRO_SDK_BACKGROUND_BUSY_IDLE_DELAY_MS = '0';
5
+ process.env.TRACE_IDLE_FLUSH_MS = '10';
6
+ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
7
+
8
+ const assert = require('assert');
9
+ const { EventEmitter } = require('events');
10
+ const { pathToFileURL } = require('url');
11
+ const { join } = require('path');
12
+ const { mkdtempSync, mkdirSync, rmSync, writeFileSync } = require('fs');
13
+ const { tmpdir } = require('os');
14
+ const { initRepro, reproMiddleware, __reproTestHooks } = require('../dist');
15
+ const { flushIngestQueue } = require('../dist/ingest/client');
16
+
17
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18
+
19
+ async function waitFor(predicate, timeoutMs = 3000) {
20
+ const deadline = Date.now() + timeoutMs;
21
+ while (Date.now() < deadline) {
22
+ if (predicate()) return;
23
+ await sleep(20);
24
+ }
25
+ assert(predicate(), 'timed out waiting for expected ingest payload');
26
+ }
27
+
28
+ function flattenEvents(posts) {
29
+ return posts.flatMap((body) => Array.isArray(body?.events) ? body.events : []);
30
+ }
31
+
32
+ function dbChanges(events) {
33
+ return events
34
+ .filter((event) => event.event_type === 'db_change')
35
+ .flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
36
+ }
37
+
38
+ function makeReqRes() {
39
+ const req = new EventEmitter();
40
+ req.method = 'POST';
41
+ req.url = '/native-mongodb-esm';
42
+ req.originalUrl = '/native-mongodb-esm';
43
+ req.headers = {
44
+ 'x-bug-session-id': 'S_mongo_esm',
45
+ 'x-bug-action-id': 'A_mongo_esm',
46
+ };
47
+ req.query = {};
48
+ req.params = {};
49
+ req.body = undefined;
50
+
51
+ const res = new EventEmitter();
52
+ res.statusCode = 200;
53
+ res.getHeader = () => undefined;
54
+ res.getHeaders = () => ({});
55
+ res.write = () => true;
56
+ res.end = function end() {
57
+ this.emit('finish');
58
+ return this;
59
+ };
60
+
61
+ return { req, res };
62
+ }
63
+
64
+ function createEsmMongoFixture() {
65
+ const root = mkdtempSync(join(tmpdir(), 'repro-node-sdk-esm-mongodb-'));
66
+ const packageDir = join(root, 'node_modules', 'mongodb');
67
+ mkdirSync(packageDir, { recursive: true });
68
+ writeFileSync(join(packageDir, 'package.json'), JSON.stringify({
69
+ name: 'mongodb',
70
+ version: '99.0.0-repro-fixture',
71
+ type: 'module',
72
+ exports: './index.mjs',
73
+ }, null, 2));
74
+ writeFileSync(join(packageDir, 'index.mjs'), `
75
+ export class Collection {
76
+ constructor(dbName, collectionName) {
77
+ this.dbName = dbName;
78
+ this.collectionName = collectionName;
79
+ }
80
+
81
+ async insertOne(doc, options) {
82
+ if (options?.session?.id !== 'esm-native-session-1') {
83
+ throw new Error(\`unexpected session \${options?.session?.id}\`);
84
+ }
85
+ doc._id = doc._id ?? 'subject-esm-1';
86
+ return { acknowledged: true, insertedId: doc._id };
87
+ }
88
+ }
89
+
90
+ export class Db {
91
+ constructor(dbName) {
92
+ this.databaseName = dbName;
93
+ }
94
+
95
+ collection(collectionName) {
96
+ return new Collection(this.databaseName, collectionName);
97
+ }
98
+ }
99
+
100
+ export class MongoClient {
101
+ constructor(uri) {
102
+ this.uri = uri;
103
+ }
104
+
105
+ db(dbName) {
106
+ return new Db(dbName);
107
+ }
108
+ }
109
+ `);
110
+ const appFile = join(root, 'app.mjs');
111
+ writeFileSync(appFile, `
112
+ import { MongoClient } from 'mongodb';
113
+
114
+ const dbClient = new MongoClient('mongodb://127.0.0.1:27017/clinical');
115
+
116
+ export async function insertSubject() {
117
+ const subjectsCollection = dbClient.db('clinical').collection('subjects');
118
+ return subjectsCollection.insertOne(
119
+ { subjectNumber: '10013038', status: 'screened' },
120
+ {
121
+ session: {
122
+ id: 'esm-native-session-1',
123
+ hasEnded: false,
124
+ inTransaction: () => true,
125
+ },
126
+ },
127
+ );
128
+ }
129
+ `);
130
+ return { root, appFile };
131
+ }
132
+
133
+ async function main() {
134
+ const capturedBodies = [];
135
+ const originalFetch = global.fetch;
136
+ let fixtureRoot = null;
137
+ global.fetch = async (_url, init) => {
138
+ capturedBodies.push(JSON.parse(String(init?.body ?? '{}')));
139
+ return {
140
+ ok: true,
141
+ status: 202,
142
+ json: async () => ({ ok: true }),
143
+ text: async () => '{"ok":true}',
144
+ };
145
+ };
146
+
147
+ try {
148
+ const cfg = {
149
+ tenantId: 'TENANT_test',
150
+ appId: 'APP_test',
151
+ appSecret: 'secret',
152
+ captureHeaders: false,
153
+ privacy: { environment: 'dev', captureRaw: true },
154
+ ingestBase: 'http://127.0.0.1:65535',
155
+ };
156
+ await initRepro(cfg);
157
+
158
+ const fixture = createEsmMongoFixture();
159
+ fixtureRoot = fixture.root;
160
+ const fixtureUrl = pathToFileURL(fixture.appFile).href;
161
+ const { insertSubject } = await import(`${fixtureUrl}?run=${Date.now()}`);
162
+
163
+ const middleware = reproMiddleware(cfg);
164
+ const { req, res } = makeReqRes();
165
+ await new Promise((resolve, reject) => {
166
+ middleware(req, res, (err) => {
167
+ if (err) {
168
+ reject(err);
169
+ return;
170
+ }
171
+ Promise.resolve(insertSubject()).then(resolve, reject);
172
+ });
173
+ });
174
+ res.end();
175
+
176
+ await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
177
+ await __reproTestHooks.drainSdkBackgroundQueueForTest();
178
+ await flushIngestQueue();
179
+
180
+ const [change] = dbChanges(flattenEvents(capturedBodies));
181
+ assert.strictEqual(change.type, 'mongo');
182
+ assert.strictEqual(change.collection, 'subjects');
183
+ assert.strictEqual(change.op, 'insertOne');
184
+ assert.strictEqual(change.pk._id, 'subject-esm-1');
185
+ assert.strictEqual(change.query.driver, 'mongodb');
186
+ assert.strictEqual(change.query.db, 'clinical');
187
+ assert.strictEqual(change.query.doc.subjectNumber, '10013038');
188
+ assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
189
+ assert.strictEqual(change.query.options.session.id, 'esm-native-session-1');
190
+ assert.strictEqual(change.after.subjectNumber, '10013038');
191
+ assert.strictEqual(change.resultMeta.insertedId, 'subject-esm-1');
192
+
193
+ console.log('mongodb esm import hook OK');
194
+ } finally {
195
+ await __reproTestHooks.drainSdkBackgroundQueueForTest();
196
+ await flushIngestQueue();
197
+ if (fixtureRoot) {
198
+ try { rmSync(fixtureRoot, { recursive: true, force: true }); } catch {}
199
+ }
200
+ global.fetch = originalFetch;
201
+ }
202
+ }
203
+
204
+ main().catch((error) => {
205
+ console.error(error);
206
+ process.exit(1);
207
+ });