@use-tusk/drift-node-sdk 0.1.6 → 0.1.7

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
@@ -252,7 +252,7 @@ var TdInstrumentationAbstract = class {
252
252
 
253
253
  //#endregion
254
254
  //#region package.json
255
- var version = "0.1.6";
255
+ var version = "0.1.7";
256
256
 
257
257
  //#endregion
258
258
  //#region src/version.ts
@@ -1450,7 +1450,7 @@ var JsonSchemaHelper = class JsonSchemaHelper {
1450
1450
  if (schema.decodedType === DecodedType$1.JSON && typeof decodedValue === "string") decodedValue = JSON.parse(decodedValue);
1451
1451
  decodedData[key] = decodedValue;
1452
1452
  } catch (error) {
1453
- logger.warn(`[JsonSchemaHelper] Failed to decode ${key}:`, error);
1453
+ logger.debug(`[JsonSchemaHelper] Failed to decode ${key}:`, error);
1454
1454
  decodedData[key] = data[key];
1455
1455
  }
1456
1456
  return decodedData;
@@ -1538,7 +1538,8 @@ function convertMockRequestDataToCleanSpanData(mockRequestData, tuskDrift, input
1538
1538
  status: {
1539
1539
  code: StatusCode.OK,
1540
1540
  message: "OK"
1541
- }
1541
+ },
1542
+ stackTrace: mockRequestData.stackTrace
1542
1543
  };
1543
1544
  }
1544
1545
  /**
@@ -1583,7 +1584,7 @@ let ClientRequest;
1583
1584
  * Mock ClientRequest implementation for Tusk Drift HTTP replay
1584
1585
  */
1585
1586
  var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
1586
- constructor(options, spanInfo, callback) {
1587
+ constructor(options, spanInfo, callback, stackTrace) {
1587
1588
  super();
1588
1589
  this.INSTRUMENTATION_NAME = "HttpInstrumentation";
1589
1590
  this.finished = false;
@@ -1594,6 +1595,7 @@ var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
1594
1595
  TdMockClientRequest._setupPrototype();
1595
1596
  this.tuskDrift = TuskDriftCore.getInstance();
1596
1597
  this.spanInfo = spanInfo;
1598
+ this.stackTrace = stackTrace;
1597
1599
  if (!options || Object.keys(options).length === 0) throw new Error("Making a request with empty `options` is not supported in TdMockClientRequest");
1598
1600
  this.options = {
1599
1601
  ...options,
@@ -1729,7 +1731,8 @@ var TdMockClientRequest = class TdMockClientRequest extends EventEmitter {
1729
1731
  instrumentationName: this.INSTRUMENTATION_NAME,
1730
1732
  submoduleName: rawInputValue.method,
1731
1733
  inputValue,
1732
- kind: SpanKind.CLIENT
1734
+ kind: SpanKind.CLIENT,
1735
+ stackTrace: this.stackTrace
1733
1736
  },
1734
1737
  tuskDrift: this.tuskDrift,
1735
1738
  inputValueSchemaMerges: {
@@ -1873,7 +1876,7 @@ var HttpReplayHooks = class {
1873
1876
  * Handle outbound HTTP requests in replay mode
1874
1877
  * Uses TdMockClientRequest for simplified mocking approach
1875
1878
  */
1876
- handleOutboundReplayRequest({ method, requestOptions, protocol, args, spanInfo }) {
1879
+ handleOutboundReplayRequest({ method, requestOptions, protocol, args, spanInfo, stackTrace }) {
1877
1880
  logger.debug(`[HttpReplayHooks] Handling outbound ${protocol.toUpperCase()} ${method} request in replay mode`);
1878
1881
  let callback;
1879
1882
  if (args.length > 1 && typeof args[1] === "function") callback = args[1];
@@ -1890,7 +1893,7 @@ var HttpReplayHooks = class {
1890
1893
  port: requestOptions.port ? Number(requestOptions.port) : void 0,
1891
1894
  method
1892
1895
  };
1893
- const mockRequest = new TdMockClientRequest(mockOptions, spanInfo, callback);
1896
+ const mockRequest = new TdMockClientRequest(mockOptions, spanInfo, callback, stackTrace);
1894
1897
  if (method === "GET" || method === "HEAD") process.nextTick(() => {
1895
1898
  mockRequest.end();
1896
1899
  });
@@ -1959,17 +1962,17 @@ function isWrapped$1(func) {
1959
1962
  */
1960
1963
  function wrap(target, propertyName, wrapper) {
1961
1964
  if (typeof target[propertyName] !== "function") {
1962
- logger.warn(`Cannot wrap non-function property: ${propertyName}`);
1965
+ logger.debug(`Cannot wrap non-function property: ${propertyName}`);
1963
1966
  return;
1964
1967
  }
1965
1968
  if (isWrapped$1(target[propertyName])) {
1966
- logger.warn(`Property ${propertyName} is already wrapped`);
1969
+ logger.debug(`Property ${propertyName} is already wrapped`);
1967
1970
  return;
1968
1971
  }
1969
1972
  const original = target[propertyName];
1970
1973
  const wrapped = wrapper(original);
1971
1974
  if (typeof wrapped !== "function") {
1972
- logger.warn(`Wrapper must return a function for property: ${propertyName}`);
1975
+ logger.debug(`Wrapper must return a function for property: ${propertyName}`);
1973
1976
  return;
1974
1977
  }
1975
1978
  wrapped._isWrapped = true;
@@ -1979,6 +1982,45 @@ function wrap(target, propertyName, wrapper) {
1979
1982
  return wrapped;
1980
1983
  }
1981
1984
 
1985
+ //#endregion
1986
+ //#region src/instrumentation/core/utils/stackTraceUtils.ts
1987
+ /**
1988
+ * Helper functions for capturing stack traces in replay mode
1989
+ *
1990
+ * TODO: Consider using a structured format for stack frames:
1991
+ *
1992
+ * {
1993
+ * "frames": [
1994
+ * {
1995
+ * "fileName": "file.js",
1996
+ * "lineNumber": 10,
1997
+ * "columnNumber": 20,
1998
+ * "functionName": "functionName"
1999
+ * }
2000
+ * ]
2001
+ * }
2002
+ *
2003
+ * This would allow for more efficient matching and filtering of stack frames.
2004
+ * It would also allow for more accurate stack trace reconstruction in replay mode.
2005
+ */
2006
+ /**
2007
+ *
2008
+ * @param excludeClassNames - Class names to exclude from the stack trace
2009
+ * @returns The stack trace as a string
2010
+ */
2011
+ function captureStackTrace(excludeClassNames = []) {
2012
+ const originalStackTraceLimit = Error.stackTraceLimit;
2013
+ Error.stackTraceLimit = 100;
2014
+ const s = (/* @__PURE__ */ new Error()).stack || "";
2015
+ Error.stackTraceLimit = originalStackTraceLimit;
2016
+ const allExcludes = [...[
2017
+ "drift-node-sdk/src/instrumentation",
2018
+ "drift-node-sdk/src/core",
2019
+ "node_modules/@use-tusk"
2020
+ ], ...excludeClassNames];
2021
+ return s.split("\n").slice(2).filter((l) => !allExcludes.some((exclude) => l.includes(exclude))).join("\n");
2022
+ }
2023
+
1982
2024
  //#endregion
1983
2025
  //#region src/instrumentation/libraries/http/HttpTransformEngine.ts
1984
2026
  /**
@@ -2927,37 +2969,40 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2927
2969
  } else requestOptions = args[0] || {};
2928
2970
  const method = requestOptions.method || "GET";
2929
2971
  const requestProtocol = self._normalizeProtocol(requestOptions.protocol || void 0, protocol);
2930
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
2931
- const headers = normalizeHeaders(requestOptions.headers || {});
2932
- const inputValue = {
2933
- method,
2934
- path: requestOptions.path || void 0,
2935
- headers,
2936
- protocol: requestProtocol,
2937
- hostname: requestOptions.hostname || requestOptions.host || void 0,
2938
- port: requestOptions.port ? Number(requestOptions.port) : void 0,
2939
- timeout: requestOptions.timeout || void 0
2940
- };
2941
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalRequest.apply(this, args), {
2942
- name: requestOptions.path || `${requestProtocol.toUpperCase()} ${method}`,
2943
- kind: SpanKind.CLIENT,
2944
- packageName: requestProtocol,
2945
- packageType: PackageType.HTTP,
2946
- instrumentationName: self.INSTRUMENTATION_NAME,
2947
- submodule: method,
2948
- inputValue,
2949
- isPreAppStart: false
2950
- }, (spanInfo) => {
2951
- return self.replayHooks.handleOutboundReplayRequest({
2972
+ if (self.mode === TuskDriftMode.REPLAY) {
2973
+ const stackTrace = captureStackTrace(["HttpInstrumentation"]);
2974
+ return handleReplayMode({ replayModeHandler: () => {
2975
+ const headers = normalizeHeaders(requestOptions.headers || {});
2976
+ const inputValue = {
2952
2977
  method,
2953
- requestOptions,
2978
+ path: requestOptions.path || void 0,
2979
+ headers,
2954
2980
  protocol: requestProtocol,
2955
- args,
2956
- spanInfo
2981
+ hostname: requestOptions.hostname || requestOptions.host || void 0,
2982
+ port: requestOptions.port ? Number(requestOptions.port) : void 0,
2983
+ timeout: requestOptions.timeout || void 0
2984
+ };
2985
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalRequest.apply(this, args), {
2986
+ name: requestOptions.path || `${requestProtocol.toUpperCase()} ${method}`,
2987
+ kind: SpanKind.CLIENT,
2988
+ packageName: requestProtocol,
2989
+ packageType: PackageType.HTTP,
2990
+ instrumentationName: self.INSTRUMENTATION_NAME,
2991
+ submodule: method,
2992
+ inputValue,
2993
+ isPreAppStart: false
2994
+ }, (spanInfo) => {
2995
+ return self.replayHooks.handleOutboundReplayRequest({
2996
+ method,
2997
+ requestOptions,
2998
+ protocol: requestProtocol,
2999
+ args,
3000
+ spanInfo,
3001
+ stackTrace
3002
+ });
2957
3003
  });
2958
- });
2959
- } });
2960
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
3004
+ } });
3005
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
2961
3006
  originalFunctionCall: () => originalRequest.apply(this, args),
2962
3007
  recordModeHandler: ({ isPreAppStart }) => {
2963
3008
  const headers = normalizeHeaders(requestOptions.headers || {});
@@ -3412,6 +3457,7 @@ var TdPgClientMock = class extends EventEmitter {
3412
3457
  }
3413
3458
  query(...args) {
3414
3459
  logger.debug(`[TdPgClientMock] Mock pool client query intercepted in REPLAY mode`);
3460
+ const stackTrace = captureStackTrace(["TdPgClientMock"]);
3415
3461
  const queryConfig = this.pgInstrumentation.parseQueryArgs(args);
3416
3462
  if (!queryConfig || !queryConfig.text) {
3417
3463
  logger.debug(`[TdPgClientMock] Could not parse mock client query, returning empty result`);
@@ -3426,7 +3472,7 @@ var TdPgClientMock = class extends EventEmitter {
3426
3472
  clientType: "client"
3427
3473
  };
3428
3474
  const inputValue = createMockInputValue(rawInputValue);
3429
- return this.pgInstrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
3475
+ return this.pgInstrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
3430
3476
  }
3431
3477
  release() {
3432
3478
  this.emit("end");
@@ -3529,23 +3575,25 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3529
3575
  values: queryConfig.values || [],
3530
3576
  clientType
3531
3577
  };
3532
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
3533
- const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
3534
- const spanName = inputValue.clientType === "pool" ? "pg-pool.query" : "pg.query";
3535
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
3536
- name: spanName,
3537
- kind: SpanKind.CLIENT,
3538
- submodule: "query",
3539
- packageType: PackageType.PG,
3540
- packageName,
3541
- instrumentationName: self.INSTRUMENTATION_NAME,
3542
- inputValue,
3543
- isPreAppStart: false
3544
- }, (spanInfo) => {
3545
- return self.handleReplayQuery(queryConfig, inputValue, spanInfo);
3546
- });
3547
- } });
3548
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
3578
+ if (self.mode === TuskDriftMode.REPLAY) {
3579
+ const stackTrace = captureStackTrace(["PgInstrumentation"]);
3580
+ return handleReplayMode({ replayModeHandler: () => {
3581
+ const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
3582
+ const spanName = inputValue.clientType === "pool" ? "pg-pool.query" : "pg.query";
3583
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
3584
+ name: spanName,
3585
+ kind: SpanKind.CLIENT,
3586
+ submodule: "query",
3587
+ packageType: PackageType.PG,
3588
+ packageName,
3589
+ instrumentationName: self.INSTRUMENTATION_NAME,
3590
+ inputValue,
3591
+ isPreAppStart: false
3592
+ }, (spanInfo) => {
3593
+ return self.handleReplayQuery(queryConfig, inputValue, spanInfo, stackTrace);
3594
+ });
3595
+ } });
3596
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
3549
3597
  originalFunctionCall: () => originalQuery.apply(this, args),
3550
3598
  recordModeHandler: ({ isPreAppStart }) => {
3551
3599
  const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
@@ -3691,7 +3739,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3691
3739
  throw error;
3692
3740
  });
3693
3741
  }
3694
- async handleReplayQuery(queryConfig, inputValue, spanInfo) {
3742
+ async handleReplayQuery(queryConfig, inputValue, spanInfo, stackTrace) {
3695
3743
  logger.debug(`[PgInstrumentation] Replaying PG query`);
3696
3744
  const packageName = inputValue.clientType === "pool" ? "pg-pool" : "pg";
3697
3745
  const spanName = inputValue.clientType === "pool" ? "pg-pool.query" : "pg.query";
@@ -3704,7 +3752,8 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3704
3752
  packageName,
3705
3753
  instrumentationName: this.INSTRUMENTATION_NAME,
3706
3754
  submoduleName: "query",
3707
- kind: SpanKind.CLIENT
3755
+ kind: SpanKind.CLIENT,
3756
+ stackTrace
3708
3757
  },
3709
3758
  tuskDrift: this.tuskDrift
3710
3759
  });
@@ -4172,26 +4221,29 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4172
4221
  query: query.trim(),
4173
4222
  parameters: values
4174
4223
  };
4175
- if (this.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
4176
- return SpanUtils.createAndExecuteSpan(this.mode, () => originalSql.call(this, strings, ...values), {
4177
- name: "postgres.query",
4178
- kind: SpanKind.CLIENT,
4179
- submodule: "query",
4180
- packageType: PackageType.PG,
4181
- packageName: "postgres",
4182
- instrumentationName: this.INSTRUMENTATION_NAME,
4183
- inputValue,
4184
- isPreAppStart: false
4185
- }, (spanInfo) => {
4186
- return this.handleReplaySqlQuery({
4187
- inputValue,
4188
- spanInfo,
4224
+ if (this.mode === TuskDriftMode.REPLAY) {
4225
+ const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
4226
+ return handleReplayMode({ replayModeHandler: () => {
4227
+ return SpanUtils.createAndExecuteSpan(this.mode, () => originalSql.call(this, strings, ...values), {
4228
+ name: "postgres.query",
4229
+ kind: SpanKind.CLIENT,
4189
4230
  submodule: "query",
4190
- name: "postgres.query"
4231
+ packageType: PackageType.PG,
4232
+ packageName: "postgres",
4233
+ instrumentationName: this.INSTRUMENTATION_NAME,
4234
+ inputValue,
4235
+ isPreAppStart: false
4236
+ }, (spanInfo) => {
4237
+ return this.handleReplaySqlQuery({
4238
+ inputValue,
4239
+ spanInfo,
4240
+ submodule: "query",
4241
+ name: "postgres.query",
4242
+ stackTrace
4243
+ });
4191
4244
  });
4192
- });
4193
- } });
4194
- else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4245
+ } });
4246
+ } else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4195
4247
  originalFunctionCall: () => originalSql.call(this, strings, ...values),
4196
4248
  recordModeHandler: ({ isPreAppStart }) => {
4197
4249
  return SpanUtils.createAndExecuteSpan(this.mode, () => originalSql.call(this, strings, ...values), {
@@ -4222,28 +4274,31 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4222
4274
  parameters: parameters || [],
4223
4275
  options: queryOptions
4224
4276
  };
4225
- if (this.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
4226
- return this._createPendingQueryWrapper(() => {
4227
- return SpanUtils.createAndExecuteSpan(this.mode, () => executeUnsafe(), {
4228
- name: "postgres.unsafe",
4229
- kind: SpanKind.CLIENT,
4230
- submodule: "unsafe",
4231
- packageType: PackageType.PG,
4232
- packageName: "postgres",
4233
- instrumentationName: this.INSTRUMENTATION_NAME,
4234
- inputValue,
4235
- isPreAppStart: false
4236
- }, (spanInfo) => {
4237
- return this.handleReplayUnsafeQuery({
4238
- inputValue,
4239
- spanInfo,
4277
+ if (this.mode === TuskDriftMode.REPLAY) {
4278
+ const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
4279
+ return handleReplayMode({ replayModeHandler: () => {
4280
+ return this._createPendingQueryWrapper(() => {
4281
+ return SpanUtils.createAndExecuteSpan(this.mode, () => executeUnsafe(), {
4282
+ name: "postgres.unsafe",
4283
+ kind: SpanKind.CLIENT,
4240
4284
  submodule: "unsafe",
4241
- name: "postgres.unsafe"
4285
+ packageType: PackageType.PG,
4286
+ packageName: "postgres",
4287
+ instrumentationName: this.INSTRUMENTATION_NAME,
4288
+ inputValue,
4289
+ isPreAppStart: false
4290
+ }, (spanInfo) => {
4291
+ return this.handleReplayUnsafeQuery({
4292
+ inputValue,
4293
+ spanInfo,
4294
+ submodule: "unsafe",
4295
+ name: "postgres.unsafe",
4296
+ stackTrace
4297
+ });
4242
4298
  });
4243
4299
  });
4244
- });
4245
- } });
4246
- else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4300
+ } });
4301
+ } else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4247
4302
  originalFunctionCall: executeUnsafe,
4248
4303
  recordModeHandler: ({ isPreAppStart }) => {
4249
4304
  return SpanUtils.createAndExecuteSpan(this.mode, executeUnsafe, {
@@ -4273,21 +4328,23 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4273
4328
  else if (transactionCallback) return originalBegin.call(sqlInstance, transactionCallback);
4274
4329
  else return originalBegin.call(sqlInstance, options || void 0);
4275
4330
  };
4276
- if (this.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
4277
- return SpanUtils.createAndExecuteSpan(this.mode, () => executeBegin(), {
4278
- name: "postgres.begin",
4279
- kind: SpanKind.CLIENT,
4280
- submodule: "transaction",
4281
- packageType: PackageType.PG,
4282
- packageName: "postgres",
4283
- instrumentationName: this.INSTRUMENTATION_NAME,
4284
- inputValue,
4285
- isPreAppStart: false
4286
- }, (spanInfo) => {
4287
- return this._handleReplayBeginTransaction(spanInfo, options);
4288
- });
4289
- } });
4290
- else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4331
+ if (this.mode === TuskDriftMode.REPLAY) {
4332
+ const stackTrace = captureStackTrace(["PostgresInstrumentation"]);
4333
+ return handleReplayMode({ replayModeHandler: () => {
4334
+ return SpanUtils.createAndExecuteSpan(this.mode, () => executeBegin(), {
4335
+ name: "postgres.begin",
4336
+ kind: SpanKind.CLIENT,
4337
+ submodule: "transaction",
4338
+ packageType: PackageType.PG,
4339
+ packageName: "postgres",
4340
+ instrumentationName: this.INSTRUMENTATION_NAME,
4341
+ inputValue,
4342
+ isPreAppStart: false
4343
+ }, (spanInfo) => {
4344
+ return this._handleReplayBeginTransaction(spanInfo, options, stackTrace);
4345
+ });
4346
+ } });
4347
+ } else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
4291
4348
  originalFunctionCall: executeBegin,
4292
4349
  recordModeHandler: ({ isPreAppStart }) => {
4293
4350
  return SpanUtils.createAndExecuteSpan(this.mode, executeBegin, {
@@ -4381,7 +4438,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4381
4438
  });
4382
4439
  return promise;
4383
4440
  }
4384
- async _handleReplayBeginTransaction(spanInfo, options) {
4441
+ async _handleReplayBeginTransaction(spanInfo, options, stackTrace) {
4385
4442
  logger.debug(`[PostgresInstrumentation] Replaying Postgres transaction`);
4386
4443
  const mockData = await findMockResponseAsync({
4387
4444
  mockRequestData: {
@@ -4395,7 +4452,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4395
4452
  packageName: "postgres",
4396
4453
  instrumentationName: this.INSTRUMENTATION_NAME,
4397
4454
  submoduleName: "transaction",
4398
- kind: SpanKind.CLIENT
4455
+ kind: SpanKind.CLIENT,
4456
+ stackTrace
4399
4457
  },
4400
4458
  tuskDrift: this.tuskDrift
4401
4459
  });
@@ -4450,7 +4508,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4450
4508
  throw error;
4451
4509
  }
4452
4510
  }
4453
- async handleReplaySqlQuery({ inputValue, spanInfo, submodule, name }) {
4511
+ async handleReplaySqlQuery({ inputValue, spanInfo, submodule, name, stackTrace }) {
4454
4512
  logger.debug(`[PostgresInstrumentation] Replaying Postgres sql query`);
4455
4513
  const mockData = await findMockResponseAsync({
4456
4514
  mockRequestData: {
@@ -4461,7 +4519,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4461
4519
  packageName: "postgres",
4462
4520
  instrumentationName: this.INSTRUMENTATION_NAME,
4463
4521
  submoduleName: submodule,
4464
- kind: SpanKind.CLIENT
4522
+ kind: SpanKind.CLIENT,
4523
+ stackTrace
4465
4524
  },
4466
4525
  tuskDrift: this.tuskDrift
4467
4526
  });
@@ -4480,7 +4539,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4480
4539
  count: isResultObject ? processedResult.count : void 0
4481
4540
  });
4482
4541
  }
4483
- async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name }) {
4542
+ async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name, stackTrace }) {
4484
4543
  logger.debug(`[PostgresInstrumentation] Replaying Postgres unsafe query`);
4485
4544
  const mockData = await findMockResponseAsync({
4486
4545
  mockRequestData: {
@@ -4491,7 +4550,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4491
4550
  packageName: "postgres",
4492
4551
  instrumentationName: this.INSTRUMENTATION_NAME,
4493
4552
  submoduleName: submodule,
4494
- kind: SpanKind.CLIENT
4553
+ kind: SpanKind.CLIENT,
4554
+ stackTrace
4495
4555
  },
4496
4556
  tuskDrift: this.tuskDrift
4497
4557
  });
@@ -4603,6 +4663,7 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
4603
4663
  }
4604
4664
  query(...args) {
4605
4665
  logger.debug(`[TdMysql2ConnectionMock] Mock connection query intercepted in REPLAY mode`);
4666
+ const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
4606
4667
  const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
4607
4668
  if (!queryConfig || !queryConfig.sql) {
4608
4669
  logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection query, returning empty result`);
@@ -4622,10 +4683,11 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
4622
4683
  clientType: this.clientType
4623
4684
  };
4624
4685
  const inputValue = createMockInputValue(rawInputValue);
4625
- return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
4686
+ return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
4626
4687
  }
4627
4688
  execute(...args) {
4628
4689
  logger.debug(`[TdMysql2ConnectionMock] Mock connection execute intercepted in REPLAY mode`);
4690
+ const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
4629
4691
  const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
4630
4692
  if (!queryConfig || !queryConfig.sql) {
4631
4693
  logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection execute, returning empty result`);
@@ -4645,7 +4707,7 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
4645
4707
  clientType: this.clientType
4646
4708
  };
4647
4709
  const inputValue = createMockInputValue(rawInputValue);
4648
- return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo);
4710
+ return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
4649
4711
  }
4650
4712
  release() {
4651
4713
  this.emit("end");
@@ -4739,20 +4801,33 @@ var TdMysql2QueryMock = class {
4739
4801
  /**
4740
4802
  * Handle replay of a MySQL2 query (query or execute)
4741
4803
  */
4742
- handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query") {
4804
+ handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query", stackTrace) {
4743
4805
  logger.debug(`[Mysql2Instrumentation] Replaying MySQL2 query`);
4744
4806
  const spanName = `mysql2.${inputValue.clientType}.${submoduleName}`;
4745
- return this._handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName);
4807
+ return this._handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName, stackTrace);
4746
4808
  }
4747
4809
  /**
4748
4810
  * Handle query - always returns an EventEmitter (like mysql2 does)
4749
4811
  * This handles both callback and streaming modes
4812
+ * The EventEmitter is also thenable (has a .then() method) to support await/Promise usage
4750
4813
  */
4751
- _handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName) {
4814
+ _handleQuery(queryConfig, inputValue, spanInfo, spanName, submoduleName, stackTrace) {
4752
4815
  const emitter = new EventEmitter();
4816
+ let storedRows = null;
4817
+ let storedFields = null;
4818
+ emitter.then = function(onResolve, onReject) {
4819
+ return new Promise((resolve, reject) => {
4820
+ emitter.once("end", () => {
4821
+ resolve([storedRows, storedFields]);
4822
+ });
4823
+ emitter.once("error", (error) => {
4824
+ reject(error);
4825
+ });
4826
+ }).then(onResolve, onReject);
4827
+ };
4753
4828
  (async () => {
4754
4829
  try {
4755
- const mockData = await this._fetchMockData(inputValue, spanInfo, spanName, submoduleName);
4830
+ const mockData = await this._fetchMockData(inputValue, spanInfo, spanName, submoduleName, stackTrace);
4756
4831
  if (!mockData) {
4757
4832
  const sql = queryConfig.sql || inputValue.sql || "UNKNOWN_QUERY";
4758
4833
  logger.warn(`[Mysql2Instrumentation] No mock data found for MySQL2 query: ${sql}`);
@@ -4764,6 +4839,8 @@ var TdMysql2QueryMock = class {
4764
4839
  return;
4765
4840
  }
4766
4841
  const processedResult = this._convertMysql2Types(mockData.result);
4842
+ storedRows = processedResult.rows;
4843
+ storedFields = processedResult.fields;
4767
4844
  process.nextTick(() => {
4768
4845
  if (processedResult.fields) emitter.emit("fields", processedResult.fields);
4769
4846
  if (queryConfig.callback) queryConfig.callback(null, processedResult.rows, processedResult.fields);
@@ -4783,7 +4860,7 @@ var TdMysql2QueryMock = class {
4783
4860
  /**
4784
4861
  * Fetch mock data from CLI
4785
4862
  */
4786
- async _fetchMockData(inputValue, spanInfo, spanName, submoduleName) {
4863
+ async _fetchMockData(inputValue, spanInfo, spanName, submoduleName, stackTrace) {
4787
4864
  return await findMockResponseAsync({
4788
4865
  mockRequestData: {
4789
4866
  traceId: spanInfo.traceId,
@@ -4793,7 +4870,8 @@ var TdMysql2QueryMock = class {
4793
4870
  packageName: "mysql2",
4794
4871
  instrumentationName: this.INSTRUMENTATION_NAME,
4795
4872
  submoduleName,
4796
- kind: SpanKind.CLIENT
4873
+ kind: SpanKind.CLIENT,
4874
+ stackTrace
4797
4875
  },
4798
4876
  tuskDrift: this.tuskDrift
4799
4877
  });
@@ -4817,6 +4895,52 @@ var TdMysql2QueryMock = class {
4817
4895
  }
4818
4896
  };
4819
4897
 
4898
+ //#endregion
4899
+ //#region src/instrumentation/libraries/mysql2/mocks/TdMysql2ConnectionEventMock.ts
4900
+ /**
4901
+ * Mock for MySQL2 connection events (connect/error)
4902
+ * Handles replay of recorded connection establishment events in REPLAY mode
4903
+ * Recording happens through normal SpanUtils flow in RECORD mode
4904
+ */
4905
+ var TdMysql2ConnectionEventMock = class {
4906
+ constructor(spanInfo) {
4907
+ this.INSTRUMENTATION_NAME = "Mysql2Instrumentation";
4908
+ this.spanInfo = spanInfo;
4909
+ this.tuskDrift = TuskDriftCore.getInstance();
4910
+ }
4911
+ /**
4912
+ * Get recorded connection event for replay
4913
+ * Returns { output } for success, or throws error if connection failed
4914
+ * The connection events are recorded automatically via SpanUtils in record mode
4915
+ */
4916
+ async getReplayedConnectionEvent(inputValue) {
4917
+ logger.debug(`[TdMysql2ConnectionEventMock] Retrieving recorded connection event`);
4918
+ try {
4919
+ const mockData = await findMockResponseAsync({
4920
+ mockRequestData: {
4921
+ traceId: this.spanInfo.traceId,
4922
+ spanId: this.spanInfo.spanId,
4923
+ name: "mysql2.connection.create",
4924
+ inputValue,
4925
+ packageName: "mysql2",
4926
+ instrumentationName: this.INSTRUMENTATION_NAME,
4927
+ submoduleName: "connectEvent",
4928
+ kind: SpanKind.CLIENT
4929
+ },
4930
+ tuskDrift: this.tuskDrift
4931
+ });
4932
+ if (!mockData) {
4933
+ logger.warn(`[TdMysql2ConnectionEventMock] No mock data found, using default success`);
4934
+ return { output: {} };
4935
+ }
4936
+ return { output: mockData.result || {} };
4937
+ } catch (error) {
4938
+ logger.error(`[TdMysql2ConnectionEventMock] Error getting replay value:`, error);
4939
+ return { output: {} };
4940
+ }
4941
+ }
4942
+ };
4943
+
4820
4944
  //#endregion
4821
4945
  //#region src/instrumentation/libraries/mysql2/Instrumentation.ts
4822
4946
  const COMPLETE_SUPPORTED_VERSIONS = ">=2.3.3 <4.0.0";
@@ -4868,16 +4992,6 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
4868
4992
  name: "mysql2/lib/pool_connection.js",
4869
4993
  supportedVersions: [V3_11_5_TO_4_0],
4870
4994
  patch: (moduleExports) => this._patchPoolConnectionV3(moduleExports)
4871
- }),
4872
- new TdInstrumentationNodeModuleFile({
4873
- name: "mysql2/lib/create_connection.js",
4874
- supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
4875
- patch: (moduleExports) => this._patchCreateConnectionFile(moduleExports)
4876
- }),
4877
- new TdInstrumentationNodeModuleFile({
4878
- name: "mysql2/lib/create_pool.js",
4879
- supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
4880
- patch: (moduleExports) => this._patchCreatePoolFile(moduleExports)
4881
4995
  })
4882
4996
  ]
4883
4997
  })];
@@ -4929,13 +5043,14 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
4929
5043
  return ConnectionClass;
4930
5044
  }
4931
5045
  this._patchConnectionPrototypes(ConnectionClass);
5046
+ const patchedConnectionClass = this._getPatchedConnectionClass(ConnectionClass);
4932
5047
  this.markModuleAsPatched(ConnectionClass);
4933
5048
  logger.debug(`[Mysql2Instrumentation] Connection class (v2) patching complete`);
4934
- return ConnectionClass;
5049
+ return patchedConnectionClass;
4935
5050
  }
4936
5051
  _patchConnectionV3(ConnectionClass) {
4937
- logger.debug(`[Mysql2Instrumentation] Connection class (v3) - skipping (base patched)`);
4938
- return ConnectionClass;
5052
+ logger.debug(`[Mysql2Instrumentation] Connection class (v3) - wrapping constructor only`);
5053
+ return this._getPatchedConnectionClass(ConnectionClass);
4939
5054
  }
4940
5055
  _patchConnectionPrototypes(ConnectionClass) {
4941
5056
  if (ConnectionClass.prototype && ConnectionClass.prototype.query) {
@@ -5044,22 +5159,24 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
5044
5159
  values: queryConfig.values || [],
5045
5160
  clientType
5046
5161
  };
5047
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5048
- const spanName = `mysql2.${clientType}.query`;
5049
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
5050
- name: spanName,
5051
- kind: SpanKind.CLIENT,
5052
- submodule: "query",
5053
- packageType: PackageType.MYSQL,
5054
- packageName: "mysql2",
5055
- instrumentationName: self.INSTRUMENTATION_NAME,
5056
- inputValue,
5057
- isPreAppStart: false
5058
- }, (spanInfo) => {
5059
- return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "query");
5060
- });
5061
- } });
5062
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5162
+ if (self.mode === TuskDriftMode.REPLAY) {
5163
+ const stackTrace = captureStackTrace(["Mysql2Instrumentation"]);
5164
+ return handleReplayMode({ replayModeHandler: () => {
5165
+ const spanName = `mysql2.${clientType}.query`;
5166
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalQuery.apply(this, args), {
5167
+ name: spanName,
5168
+ kind: SpanKind.CLIENT,
5169
+ submodule: "query",
5170
+ packageType: PackageType.MYSQL,
5171
+ packageName: "mysql2",
5172
+ instrumentationName: self.INSTRUMENTATION_NAME,
5173
+ inputValue,
5174
+ isPreAppStart: false
5175
+ }, (spanInfo) => {
5176
+ return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "query", stackTrace);
5177
+ });
5178
+ } });
5179
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5063
5180
  originalFunctionCall: () => originalQuery.apply(this, args),
5064
5181
  recordModeHandler: ({ isPreAppStart }) => {
5065
5182
  const spanName = `mysql2.${clientType}.query`;
@@ -5101,22 +5218,24 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
5101
5218
  values: queryConfig.values || [],
5102
5219
  clientType
5103
5220
  };
5104
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5105
- const spanName = `mysql2.${clientType}.execute`;
5106
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalExecute.apply(this, args), {
5107
- name: spanName,
5108
- kind: SpanKind.CLIENT,
5109
- submodule: "execute",
5110
- packageType: PackageType.MYSQL,
5111
- packageName: "mysql2",
5112
- instrumentationName: self.INSTRUMENTATION_NAME,
5113
- inputValue,
5114
- isPreAppStart: false
5115
- }, (spanInfo) => {
5116
- return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "execute");
5117
- });
5118
- } });
5119
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5221
+ if (self.mode === TuskDriftMode.REPLAY) {
5222
+ const stackTrace = captureStackTrace(["Mysql2Instrumentation"]);
5223
+ return handleReplayMode({ replayModeHandler: () => {
5224
+ const spanName = `mysql2.${clientType}.execute`;
5225
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalExecute.apply(this, args), {
5226
+ name: spanName,
5227
+ kind: SpanKind.CLIENT,
5228
+ submodule: "execute",
5229
+ packageType: PackageType.MYSQL,
5230
+ packageName: "mysql2",
5231
+ instrumentationName: self.INSTRUMENTATION_NAME,
5232
+ inputValue,
5233
+ isPreAppStart: false
5234
+ }, (spanInfo) => {
5235
+ return self.handleReplayQuery(queryConfig, inputValue, spanInfo, "execute", stackTrace);
5236
+ });
5237
+ } });
5238
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5120
5239
  originalFunctionCall: () => originalExecute.apply(this, args),
5121
5240
  recordModeHandler: ({ isPreAppStart }) => {
5122
5241
  const spanName = `mysql2.${clientType}.execute`;
@@ -5464,8 +5583,8 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
5464
5583
  return result;
5465
5584
  }
5466
5585
  }
5467
- handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query") {
5468
- return this.queryMock.handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName);
5586
+ handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName = "query", stackTrace) {
5587
+ return this.queryMock.handleReplayQuery(queryConfig, inputValue, spanInfo, submoduleName, stackTrace);
5469
5588
  }
5470
5589
  _handleRecordPoolGetConnectionInSpan(spanInfo, originalGetConnection, callback, context$1) {
5471
5590
  if (callback) {
@@ -5528,117 +5647,137 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
5528
5647
  return;
5529
5648
  } else return Promise.resolve(mockConnection);
5530
5649
  }
5531
- _addOutputAttributesToSpan(spanInfo, result, fields) {
5532
- if (!result) return;
5533
- let outputValue = {};
5534
- if (Array.isArray(result)) outputValue = {
5535
- rowCount: result.length,
5536
- rows: result,
5537
- fields: fields || []
5538
- };
5539
- else if (result.affectedRows !== void 0) outputValue = {
5540
- affectedRows: result.affectedRows,
5541
- insertId: result.insertId,
5542
- warningCount: result.warningCount
5543
- };
5544
- else outputValue = result;
5545
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
5546
- }
5547
- _patchCreateConnectionFile(createConnectionFn) {
5548
- logger.debug(`[Mysql2Instrumentation] Patching create_connection.js file`);
5650
+ /**
5651
+ * Creates a patched Connection class that intercepts the constructor
5652
+ * to handle connection event recording/replay.
5653
+ *
5654
+ * Wrap the Connection constructor to:
5655
+ * - In RECORD mode: listen for 'connect'/'error' events and record them
5656
+ * - In REPLAY mode: create a MockConnection that fakes the connection and emits recorded events
5657
+ */
5658
+ _getPatchedConnectionClass(OriginalConnection) {
5549
5659
  const self = this;
5550
- const wrappedFn = function(...args) {
5660
+ function TdPatchedConnection(...args) {
5551
5661
  const inputValue = { method: "createConnection" };
5552
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5553
- return SpanUtils.createAndExecuteSpan(self.mode, () => createConnectionFn.apply(this, args), {
5554
- name: `mysql2.createConnection`,
5555
- kind: SpanKind.CLIENT,
5556
- submodule: "createConnection",
5557
- packageName: "mysql2",
5558
- packageType: PackageType.MYSQL,
5559
- instrumentationName: self.INSTRUMENTATION_NAME,
5560
- inputValue,
5561
- isPreAppStart: false
5562
- }, (spanInfo) => {
5563
- const connection = createConnectionFn.apply(this, args);
5564
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { created: true } });
5565
- SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
5566
- return connection;
5567
- });
5568
- } });
5569
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5570
- originalFunctionCall: () => createConnectionFn.apply(this, args),
5662
+ if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5663
+ originalFunctionCall: () => new OriginalConnection(...args),
5571
5664
  recordModeHandler: ({ isPreAppStart }) => {
5572
- return SpanUtils.createAndExecuteSpan(self.mode, () => createConnectionFn.apply(this, args), {
5573
- name: `mysql2.createConnection`,
5665
+ return SpanUtils.createAndExecuteSpan(self.mode, () => new OriginalConnection(...args), {
5666
+ name: `mysql2.connection.create`,
5574
5667
  kind: SpanKind.CLIENT,
5575
- submodule: "createConnection",
5576
- packageName: "mysql2",
5668
+ submodule: "connectEvent",
5577
5669
  packageType: PackageType.MYSQL,
5670
+ packageName: "mysql2",
5578
5671
  instrumentationName: self.INSTRUMENTATION_NAME,
5579
5672
  inputValue,
5580
5673
  isPreAppStart
5581
5674
  }, (spanInfo) => {
5582
- const connection = createConnectionFn.apply(this, args);
5583
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { created: true } });
5584
- SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
5675
+ const connection = new OriginalConnection(...args);
5676
+ connection.on("connect", (connectionObj) => {
5677
+ try {
5678
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
5679
+ connected: true,
5680
+ connectionObj
5681
+ } });
5682
+ SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
5683
+ } catch {
5684
+ logger.error(`[Mysql2Instrumentation] error adding span attributes:`);
5685
+ }
5686
+ });
5687
+ connection.on("error", (err) => {
5688
+ try {
5689
+ logger.debug(`[Mysql2Instrumentation] Connection error, recording: ${err.message}`);
5690
+ SpanUtils.endSpan(spanInfo.span, {
5691
+ code: SpanStatusCode.ERROR,
5692
+ message: err.message
5693
+ });
5694
+ } catch {
5695
+ logger.error(`[Mysql2Instrumentation] error ending span`);
5696
+ }
5697
+ });
5585
5698
  return connection;
5586
5699
  });
5587
5700
  },
5588
5701
  spanKind: SpanKind.CLIENT
5589
5702
  });
5590
- else return createConnectionFn.apply(this, args);
5591
- };
5592
- logger.debug(`[Mysql2Instrumentation] Patched create_connection.js file`);
5593
- return wrappedFn;
5594
- }
5595
- _patchCreatePoolFile(createPoolFn) {
5596
- logger.debug(`[Mysql2Instrumentation] Patching create_pool.js file`);
5597
- const self = this;
5598
- const wrappedFn = function(...args) {
5599
- const inputValue = { method: "createPool" };
5600
5703
  if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5601
- return SpanUtils.createAndExecuteSpan(self.mode, () => createPoolFn.apply(this, args), {
5602
- name: `mysql2.createPool`,
5704
+ return SpanUtils.createAndExecuteSpan(self.mode, () => new OriginalConnection(...args), {
5705
+ name: `mysql2.connection.create`,
5603
5706
  kind: SpanKind.CLIENT,
5604
- submodule: "createPool",
5605
- packageName: "mysql2",
5707
+ submodule: "connectEvent",
5606
5708
  packageType: PackageType.MYSQL,
5709
+ packageName: "mysql2",
5607
5710
  instrumentationName: self.INSTRUMENTATION_NAME,
5608
5711
  inputValue,
5609
5712
  isPreAppStart: false
5610
5713
  }, (spanInfo) => {
5611
- const pool = createPoolFn.apply(this, args);
5612
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { created: true } });
5714
+ class MockConnection extends OriginalConnection {
5715
+ constructor(...mockConnectionArgs) {
5716
+ const clonedArgs = JSON.parse(JSON.stringify(mockConnectionArgs));
5717
+ if (clonedArgs[0] && clonedArgs[0].config) {
5718
+ clonedArgs[0].config.host = "127.0.0.1";
5719
+ clonedArgs[0].config.port = 127;
5720
+ } else if (clonedArgs[0]) {
5721
+ clonedArgs[0].host = "127.0.0.1";
5722
+ clonedArgs[0].port = 127;
5723
+ }
5724
+ super(...clonedArgs);
5725
+ this._isConnectOrErrorEmitted = false;
5726
+ this._connectEventMock = new TdMysql2ConnectionEventMock(spanInfo);
5727
+ }
5728
+ on(event, listener) {
5729
+ if (!this._connectEventMock) return super.on(event, listener);
5730
+ if (event === "connect" && !this._isConnectOrErrorEmitted) {
5731
+ this._connectEventMock.getReplayedConnectionEvent(inputValue).then(({ output }) => {
5732
+ if (output !== void 0) process.nextTick(() => {
5733
+ listener.call(this, output);
5734
+ this._isConnectOrErrorEmitted = true;
5735
+ });
5736
+ }).catch((err) => {
5737
+ logger.error(`[Mysql2Instrumentation] Error replaying connection event:`, err);
5738
+ });
5739
+ return this;
5740
+ }
5741
+ if (event === "error" && !this._isConnectOrErrorEmitted) return this;
5742
+ return super.on(event, listener);
5743
+ }
5744
+ }
5745
+ const mockConnection = new MockConnection(...args);
5746
+ mockConnection.addListener("error", (_err) => {});
5747
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
5748
+ connected: true,
5749
+ mock: true
5750
+ } });
5613
5751
  SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
5614
- return pool;
5752
+ return mockConnection;
5615
5753
  });
5616
5754
  } });
5617
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5618
- originalFunctionCall: () => createPoolFn.apply(this, args),
5619
- recordModeHandler: ({ isPreAppStart }) => {
5620
- return SpanUtils.createAndExecuteSpan(self.mode, () => createPoolFn.apply(this, args), {
5621
- name: `mysql2.createPool`,
5622
- kind: SpanKind.CLIENT,
5623
- submodule: "createPool",
5624
- packageName: "mysql2",
5625
- packageType: PackageType.MYSQL,
5626
- instrumentationName: self.INSTRUMENTATION_NAME,
5627
- inputValue,
5628
- isPreAppStart
5629
- }, (spanInfo) => {
5630
- const pool = createPoolFn.apply(this, args);
5631
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { created: true } });
5632
- SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
5633
- return pool;
5634
- });
5635
- },
5636
- spanKind: SpanKind.CLIENT
5637
- });
5638
- else return createPoolFn.apply(this, args);
5755
+ return new OriginalConnection(...args);
5756
+ }
5757
+ const staticProps = Object.getOwnPropertyNames(OriginalConnection).filter((key) => ![
5758
+ "length",
5759
+ "name",
5760
+ "prototype"
5761
+ ].includes(key));
5762
+ for (const staticProp of staticProps) TdPatchedConnection[staticProp] = OriginalConnection[staticProp];
5763
+ Object.setPrototypeOf(TdPatchedConnection.prototype, OriginalConnection.prototype);
5764
+ return TdPatchedConnection;
5765
+ }
5766
+ _addOutputAttributesToSpan(spanInfo, result, fields) {
5767
+ if (!result) return;
5768
+ let outputValue = {};
5769
+ if (Array.isArray(result)) outputValue = {
5770
+ rowCount: result.length,
5771
+ rows: result,
5772
+ fields: fields || []
5773
+ };
5774
+ else if (result.affectedRows !== void 0) outputValue = {
5775
+ affectedRows: result.affectedRows,
5776
+ insertId: result.insertId,
5777
+ warningCount: result.warningCount
5639
5778
  };
5640
- logger.debug(`[Mysql2Instrumentation] Patched create_pool.js file`);
5641
- return wrappedFn;
5779
+ else outputValue = result;
5780
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
5642
5781
  }
5643
5782
  _wrap(target, propertyName, wrapper) {
5644
5783
  wrap(target, propertyName, wrapper);
@@ -5796,20 +5935,22 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
5796
5935
  logger.error(`[JsonwebtokenInstrumentation] error creating mock input value:`, error);
5797
5936
  return originalVerify.apply(this, args);
5798
5937
  }
5799
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5800
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalVerify.apply(this, args), {
5801
- name: "jsonwebtoken.verify",
5802
- kind: SpanKind.CLIENT,
5803
- submodule: "verify",
5804
- packageName: "jsonwebtoken",
5805
- instrumentationName: self.INSTRUMENTATION_NAME,
5806
- inputValue,
5807
- isPreAppStart: false
5808
- }, (spanInfo) => {
5809
- return self.handleReplayVerify(verifyConfig, inputValue, spanInfo);
5810
- });
5811
- } });
5812
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5938
+ if (self.mode === TuskDriftMode.REPLAY) {
5939
+ const stackTrace = captureStackTrace(["JsonwebtokenInstrumentation"]);
5940
+ return handleReplayMode({ replayModeHandler: () => {
5941
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalVerify.apply(this, args), {
5942
+ name: "jsonwebtoken.verify",
5943
+ kind: SpanKind.CLIENT,
5944
+ submodule: "verify",
5945
+ packageName: "jsonwebtoken",
5946
+ instrumentationName: self.INSTRUMENTATION_NAME,
5947
+ inputValue,
5948
+ isPreAppStart: false
5949
+ }, (spanInfo) => {
5950
+ return self.handleReplayVerify(verifyConfig, inputValue, spanInfo, stackTrace);
5951
+ });
5952
+ } });
5953
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5813
5954
  originalFunctionCall: () => originalVerify.apply(this, args),
5814
5955
  recordModeHandler: ({ isPreAppStart }) => {
5815
5956
  return SpanUtils.createAndExecuteSpan(self.mode, () => originalVerify.apply(this, args), {
@@ -5849,20 +5990,22 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
5849
5990
  secretOrPrivateKey: signConfig.secretOrPrivateKey,
5850
5991
  options: signConfig.options
5851
5992
  };
5852
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
5853
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalSign.apply(this, args), {
5854
- name: "jsonwebtoken.sign",
5855
- kind: SpanKind.CLIENT,
5856
- submodule: "sign",
5857
- packageName: "jsonwebtoken",
5858
- instrumentationName: self.INSTRUMENTATION_NAME,
5859
- inputValue,
5860
- isPreAppStart: false
5861
- }, (spanInfo) => {
5862
- return self.handleReplaySign(signConfig, inputValue, spanInfo);
5863
- });
5864
- } });
5865
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5993
+ if (self.mode === TuskDriftMode.REPLAY) {
5994
+ const stackTrace = captureStackTrace(["JsonwebtokenInstrumentation"]);
5995
+ return handleReplayMode({ replayModeHandler: () => {
5996
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalSign.apply(this, args), {
5997
+ name: "jsonwebtoken.sign",
5998
+ kind: SpanKind.CLIENT,
5999
+ submodule: "sign",
6000
+ packageName: "jsonwebtoken",
6001
+ instrumentationName: self.INSTRUMENTATION_NAME,
6002
+ inputValue,
6003
+ isPreAppStart: false
6004
+ }, (spanInfo) => {
6005
+ return self.handleReplaySign(signConfig, inputValue, spanInfo, stackTrace);
6006
+ });
6007
+ } });
6008
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
5866
6009
  originalFunctionCall: () => originalSign.apply(this, args),
5867
6010
  recordModeHandler: ({ isPreAppStart }) => {
5868
6011
  return SpanUtils.createAndExecuteSpan(self.mode, () => originalSign.apply(this, args), {
@@ -6030,18 +6173,19 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
6030
6173
  throw error;
6031
6174
  }
6032
6175
  }
6033
- async handleReplayVerify(verifyConfig, inputValue, spanInfo) {
6176
+ async handleReplayVerify(verifyConfig, inputValue, spanInfo, stackTrace) {
6034
6177
  logger.debug(`[JsonwebtokenInstrumentation] Replaying JWT verify`);
6035
6178
  const mockData = await findMockResponseAsync({
6036
6179
  mockRequestData: {
6037
6180
  traceId: spanInfo.traceId,
6038
6181
  spanId: spanInfo.spanId,
6039
- name: inputValue.token,
6182
+ name: "jsonwebtoken.verify",
6040
6183
  packageName: "jsonwebtoken",
6041
6184
  instrumentationName: this.INSTRUMENTATION_NAME,
6042
6185
  submoduleName: "verify",
6043
6186
  inputValue,
6044
- kind: SpanKind.CLIENT
6187
+ kind: SpanKind.CLIENT,
6188
+ stackTrace
6045
6189
  },
6046
6190
  tuskDrift: this.tuskDrift
6047
6191
  });
@@ -6076,18 +6220,19 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
6076
6220
  return;
6077
6221
  } else return result;
6078
6222
  }
6079
- async handleReplaySign(signConfig, inputValue, spanInfo) {
6223
+ async handleReplaySign(signConfig, inputValue, spanInfo, stackTrace) {
6080
6224
  logger.debug(`[JsonwebtokenInstrumentation] Replaying JWT sign`);
6081
6225
  const mockData = await findMockResponseAsync({
6082
6226
  mockRequestData: {
6083
6227
  traceId: spanInfo?.traceId,
6084
6228
  spanId: spanInfo?.spanId,
6085
- name: JSON.stringify(inputValue.payload),
6229
+ name: "jsonwebtoken.sign",
6086
6230
  packageName: "jsonwebtoken",
6087
6231
  instrumentationName: this.INSTRUMENTATION_NAME,
6088
6232
  submoduleName: "sign",
6089
6233
  inputValue,
6090
- kind: SpanKind.CLIENT
6234
+ kind: SpanKind.CLIENT,
6235
+ stackTrace
6091
6236
  },
6092
6237
  tuskDrift: this.tuskDrift
6093
6238
  });
@@ -6491,11 +6636,12 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
6491
6636
  this.originalFetch = globalThis.fetch;
6492
6637
  const self = this;
6493
6638
  globalThis.fetch = function(input, init) {
6494
- return self._handleFetchRequest(input, init);
6639
+ const stackTrace = captureStackTrace(["FetchInstrumentation"]);
6640
+ return self._handleFetchRequest(input, init, stackTrace);
6495
6641
  };
6496
6642
  logger.debug("Global fetch patching complete");
6497
6643
  }
6498
- async _handleFetchRequest(input, init) {
6644
+ async _handleFetchRequest(input, init, stackTrace) {
6499
6645
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
6500
6646
  const method = init?.method || "GET";
6501
6647
  const headers = init?.headers || {};
@@ -6525,7 +6671,7 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
6525
6671
  inputValue,
6526
6672
  isPreAppStart: false
6527
6673
  }, (spanInfo) => {
6528
- return this._handleReplayFetch(inputValue, spanInfo);
6674
+ return this._handleReplayFetch(inputValue, spanInfo, stackTrace);
6529
6675
  });
6530
6676
  } });
6531
6677
  else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
@@ -6606,7 +6752,7 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
6606
6752
  });
6607
6753
  });
6608
6754
  }
6609
- async _handleReplayFetch(inputValue, spanInfo) {
6755
+ async _handleReplayFetch(inputValue, spanInfo, stackTrace) {
6610
6756
  const mockData = await findMockResponseAsync({
6611
6757
  mockRequestData: {
6612
6758
  traceId: spanInfo.traceId,
@@ -6617,7 +6763,8 @@ var FetchInstrumentation = class extends TdInstrumentationBase {
6617
6763
  instrumentationName: this.INSTRUMENTATION_NAME,
6618
6764
  submoduleName: inputValue.method,
6619
6765
  inputValue,
6620
- kind: SpanKind.CLIENT
6766
+ kind: SpanKind.CLIENT,
6767
+ stackTrace
6621
6768
  },
6622
6769
  tuskDrift: this.tuskDrift,
6623
6770
  inputValueSchemaMerges: {
@@ -6892,21 +7039,23 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
6892
7039
  port: this.options?.port
6893
7040
  }
6894
7041
  };
6895
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
6896
- return SpanUtils.createAndExecuteSpan(self.mode, () => originalSendCommand.apply(this, arguments), {
6897
- name: `ioredis.${commandName}`,
6898
- kind: SpanKind.CLIENT,
6899
- submodule: commandName,
6900
- packageType: PackageType.REDIS,
6901
- packageName: "ioredis",
6902
- instrumentationName: self.INSTRUMENTATION_NAME,
6903
- inputValue,
6904
- isPreAppStart: false
6905
- }, (spanInfo) => {
6906
- return self._handleReplaySendCommand(spanInfo, cmd, inputValue, commandName);
6907
- });
6908
- } });
6909
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
7042
+ if (self.mode === TuskDriftMode.REPLAY) {
7043
+ const stackTrace = captureStackTrace(["IORedisInstrumentation"]);
7044
+ return handleReplayMode({ replayModeHandler: () => {
7045
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalSendCommand.apply(this, arguments), {
7046
+ name: `ioredis.${commandName}`,
7047
+ kind: SpanKind.CLIENT,
7048
+ submodule: commandName,
7049
+ packageType: PackageType.REDIS,
7050
+ packageName: "ioredis",
7051
+ instrumentationName: self.INSTRUMENTATION_NAME,
7052
+ inputValue,
7053
+ isPreAppStart: false
7054
+ }, (spanInfo) => {
7055
+ return self._handleReplaySendCommand(spanInfo, cmd, inputValue, commandName, stackTrace);
7056
+ });
7057
+ } });
7058
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
6910
7059
  originalFunctionCall: () => originalSendCommand.apply(this, arguments),
6911
7060
  recordModeHandler: ({ isPreAppStart }) => {
6912
7061
  return SpanUtils.createAndExecuteSpan(self.mode, () => originalSendCommand.apply(this, arguments), {
@@ -7068,7 +7217,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
7068
7217
  });
7069
7218
  return promise;
7070
7219
  }
7071
- async _handleReplaySendCommand(spanInfo, cmd, inputValue, commandName) {
7220
+ async _handleReplaySendCommand(spanInfo, cmd, inputValue, commandName, stackTrace) {
7072
7221
  logger.debug(`[IORedisInstrumentation] Replaying IORedis command ${cmd.name}`);
7073
7222
  const mockData = await findMockResponseAsync({
7074
7223
  mockRequestData: {
@@ -7079,7 +7228,8 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
7079
7228
  packageName: "ioredis",
7080
7229
  instrumentationName: this.INSTRUMENTATION_NAME,
7081
7230
  submoduleName: cmd.name,
7082
- kind: SpanKind.CLIENT
7231
+ kind: SpanKind.CLIENT,
7232
+ stackTrace
7083
7233
  },
7084
7234
  tuskDrift: this.tuskDrift
7085
7235
  });
@@ -7528,21 +7678,23 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7528
7678
  jsonableStringMap
7529
7679
  }
7530
7680
  };
7531
- if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
7532
- return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
7533
- name: "grpc.client.unary",
7534
- kind: SpanKind.CLIENT,
7535
- submodule: "client",
7536
- packageType: PackageType.GRPC,
7537
- packageName: GRPC_MODULE_NAME,
7538
- instrumentationName: self.INSTRUMENTATION_NAME,
7539
- inputValue,
7540
- isPreAppStart: false
7541
- }, (spanInfo) => {
7542
- return self._handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor);
7543
- });
7544
- } });
7545
- else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
7681
+ if (self.mode === TuskDriftMode.REPLAY) {
7682
+ const stackTrace = captureStackTrace(["GrpcInstrumentation"]);
7683
+ return handleReplayMode({ replayModeHandler: () => {
7684
+ return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
7685
+ name: "grpc.client.unary",
7686
+ kind: SpanKind.CLIENT,
7687
+ submodule: "client",
7688
+ packageType: PackageType.GRPC,
7689
+ packageName: GRPC_MODULE_NAME,
7690
+ instrumentationName: self.INSTRUMENTATION_NAME,
7691
+ inputValue,
7692
+ isPreAppStart: false
7693
+ }, (spanInfo) => {
7694
+ return self._handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace);
7695
+ });
7696
+ } });
7697
+ } else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
7546
7698
  originalFunctionCall: () => original.apply(this, args),
7547
7699
  recordModeHandler: ({ isPreAppStart }) => {
7548
7700
  return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
@@ -7674,7 +7826,7 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7674
7826
  isGrpcErrorOutput(result) {
7675
7827
  return "error" in result;
7676
7828
  }
7677
- async _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor) {
7829
+ async _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace) {
7678
7830
  logger.debug(`[GrpcInstrumentation] Replaying gRPC unary request`);
7679
7831
  const mockData = await findMockResponseAsync({
7680
7832
  mockRequestData: {
@@ -7685,7 +7837,8 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7685
7837
  packageName: GRPC_MODULE_NAME,
7686
7838
  instrumentationName: this.INSTRUMENTATION_NAME,
7687
7839
  submoduleName: "client",
7688
- kind: SpanKind.CLIENT
7840
+ kind: SpanKind.CLIENT,
7841
+ stackTrace
7689
7842
  },
7690
7843
  tuskDrift: this.tuskDrift
7691
7844
  });
@@ -10315,7 +10468,7 @@ var SpanTransformer = class SpanTransformer {
10315
10468
  if (transformMetadataString) try {
10316
10469
  transformMetadata = JSON.parse(transformMetadataString);
10317
10470
  } catch (error) {
10318
- logger.warn("Failed to parse transform metadata", error);
10471
+ logger.warn("[SpanTransformer] Failed to parse transform metadata", error);
10319
10472
  }
10320
10473
  const originalDate = OriginalGlobalUtils.getOriginalDate();
10321
10474
  return {
@@ -10701,7 +10854,7 @@ var ProtobufCommunicator = class {
10701
10854
  requestId,
10702
10855
  tags: {},
10703
10856
  outboundSpan: cleanSpan,
10704
- stackTrace: this.getStackTrace()
10857
+ stackTrace: cleanSpan?.stackTrace
10705
10858
  });
10706
10859
  const sdkMessage = SDKMessage.create({
10707
10860
  type: MessageType.MOCK_REQUEST,
@@ -10742,7 +10895,7 @@ var ProtobufCommunicator = class {
10742
10895
  requestId,
10743
10896
  tags: {},
10744
10897
  outboundSpan: cleanSpan,
10745
- stackTrace: this.getStackTrace()
10898
+ stackTrace: cleanSpan?.stackTrace
10746
10899
  });
10747
10900
  const sdkMessage = SDKMessage.create({
10748
10901
  type: MessageType.MOCK_REQUEST,
@@ -11034,7 +11187,7 @@ var TuskDriftCore = class TuskDriftCore {
11034
11187
  const packageName = this.getPackageName(modulePath);
11035
11188
  if (packageName && TuskDriftInstrumentationModuleNames.includes(packageName)) alreadyRequiredModuleNames.add(packageName);
11036
11189
  }
11037
- } else logger.warn("Running in ES Module mode. Cannot detect pre-loaded instrumentation modules.");
11190
+ } else logger.debug("Running in ES Module mode. Cannot detect pre-loaded instrumentation modules.");
11038
11191
  return alreadyRequiredModuleNames;
11039
11192
  }
11040
11193
  static getInstance() {
@@ -11140,13 +11293,13 @@ var TuskDriftCore = class TuskDriftCore {
11140
11293
  }
11141
11294
  initialize(initParams) {
11142
11295
  initializeGlobalLogger({
11143
- logLevel: initParams.logLevel || "silent",
11296
+ logLevel: initParams.logLevel || "info",
11144
11297
  prefix: "TuskDrift"
11145
11298
  });
11146
11299
  this.samplingRate = this.config.recording?.sampling_rate ?? 1;
11147
11300
  this.initParams = initParams;
11148
11301
  if (!this.initParams.env) {
11149
- const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "unknown";
11302
+ const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "development";
11150
11303
  logger.warn(`Environment not provided in initialization parameters. Using '${nodeEnv}' as the environment.`);
11151
11304
  this.initParams.env = nodeEnv;
11152
11305
  }
@@ -11224,8 +11377,8 @@ var TuskDriftCore = class TuskDriftCore {
11224
11377
  }
11225
11378
  this.appReady = true;
11226
11379
  logger.debug("Application marked as ready");
11227
- if (this.mode === TuskDriftMode.REPLAY) logger.info("Replay mode active - ready to serve mocked responses");
11228
- else if (this.mode === TuskDriftMode.RECORD) logger.info("Record mode active - capturing requests and responses");
11380
+ if (this.mode === TuskDriftMode.REPLAY) logger.debug("Replay mode active - ready to serve mocked responses");
11381
+ else if (this.mode === TuskDriftMode.RECORD) logger.debug("Record mode active - capturing inbound requests and responses");
11229
11382
  }
11230
11383
  async sendInboundSpanForReplay(span) {
11231
11384
  try {