@use-tusk/drift-node-sdk 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -3,8 +3,9 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
+ <a href="https://www.npmjs.com/package/@use-tusk/drift-node-sdk"><img src="https://img.shields.io/npm/v/@use-tusk/drift-node-sdk" alt="npm version"></a>
6
7
  <a href="https://opensource.org/licenses/Apache-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache 2.0"></a>
7
- <a href="https://github.com/Use-Tusk/tusk-drift-sdk/commits/main"><img src="https://img.shields.io/github/last-commit/Use-Tusk/tusk-drift-sdk" alt="GitHub last commit"></a>
8
+ <a href="https://github.com/Use-Tusk/drift-node-sdk/commits/main/"><img src="https://img.shields.io/github/last-commit/Use-Tusk/drift-node-sdk" alt="GitHub last commit"></a>
8
9
  </p>
9
10
 
10
11
  The Node.js Tusk Drift SDK enables fast and deterministic API testing by capturing and replaying API calls made to/from your service. Automatically record real-world API calls, then replay them as tests using the [Tusk CLI](https://github.com/Use-Tusk/tusk-drift-cli) to find regressions. During replay, all outbound requests are intercepted with recorded data to ensure consistent behavior without side-effects.
@@ -57,6 +58,8 @@ Follow these steps in order to properly initialize the Tusk Drift SDK:
57
58
 
58
59
  Create a separate file (e.g. `tdInit.ts`) to initialize the Tusk Drift SDK. This ensures the SDK is initialized as early as possible before any other modules are loaded.
59
60
 
61
+ #### For CommonJS Applications
62
+
60
63
  ```typescript
61
64
  // tdInit.ts
62
65
  import { TuskDrift } from "@use-tusk/drift-node-sdk";
@@ -71,6 +74,33 @@ TuskDrift.initialize({
71
74
  export { TuskDrift };
72
75
  ```
73
76
 
77
+ #### For ESM Applications
78
+
79
+ ESM applications require additional setup to properly intercept module imports:
80
+
81
+ ```typescript
82
+ // tdInit.ts
83
+ import { register } from 'node:module';
84
+ import { pathToFileURL } from 'node:url';
85
+
86
+ // Register the ESM loader
87
+ // This enables interception of ESM module imports
88
+ register('@use-tusk/drift-node-sdk/hook.mjs', pathToFileURL('./'));
89
+
90
+ import { TuskDrift } from "@use-tusk/drift-node-sdk";
91
+
92
+ // Initialize SDK immediately
93
+ TuskDrift.initialize({
94
+ apiKey: process.env.TUSK_DRIFT_API_KEY,
95
+ env: process.env.ENV,
96
+ logLevel: process.env.TUSK_DRIFT_LOG_LEVEL,
97
+ });
98
+
99
+ export { TuskDrift };
100
+ ```
101
+
102
+ **Why the ESM loader is needed**: ESM imports are statically analyzed and hoisted, meaning all imports are resolved before any code runs. The `register()` call sets up Node.js loader hooks that intercept module imports, allowing the SDK to instrument packages like `postgres`, `http`, etc. Without this, the SDK cannot patch ESM modules.
103
+
74
104
  #### Configuration Options
75
105
 
76
106
  <table>
@@ -106,7 +136,9 @@ export { TuskDrift };
106
136
 
107
137
  ### 2. Import SDK at Application Entry Point
108
138
 
109
- In your main server file (e.g., `server.ts`, `index.ts`, `app.ts`), import the initialized SDK **at the very top**, before any other imports:
139
+ #### For CommonJS Applications
140
+
141
+ In your main server file (e.g., `server.ts`, `index.ts`, `app.ts`), require the initialized SDK **at the very top**, before any other requires:
110
142
 
111
143
  ```typescript
112
144
  // server.ts
@@ -117,7 +149,24 @@ import { TuskDrift } from "./tdInit"; // MUST be the first import
117
149
  // Your application setup...
118
150
  ```
119
151
 
120
- > **IMPORTANT**: Ensure NO require/import calls are made before importing the SDK initialization file. This guarantees proper instrumentation of all dependencies.
152
+ > **IMPORTANT**: Ensure NO require calls are made before requiring the SDK initialization file. This guarantees proper instrumentation of all dependencies.
153
+
154
+ #### For ESM Applications
155
+
156
+ For ESM applications, you **cannot** control import order within your application code because all imports are hoisted. Instead, use the `--import` flag:
157
+
158
+ **Update your package.json scripts**:
159
+
160
+ ```json
161
+ {
162
+ "scripts": {
163
+ "dev": "node --import ./dist/tdInit.js dist/server.js"
164
+ "dev:record": "TUSK_DRIFT_MODE=RECORD node --import ./dist/tdInit.js dist/server.js"
165
+ }
166
+ }
167
+ ```
168
+
169
+ **Why `--import` is required for ESM**: In ESM, all `import` statements are hoisted and evaluated before any code runs, making it impossible to control import order within a file. The `--import` flag ensures the SDK initialization (including loader registration) happens in a separate phase before your application code loads, guaranteeing proper module interception.
121
170
 
122
171
  ### 3. Update Configuration File
123
172
 
package/dist/index.cjs CHANGED
@@ -34,6 +34,8 @@ let semver = require("semver");
34
34
  semver = __toESM(semver);
35
35
  let require_in_the_middle = require("require-in-the-middle");
36
36
  require_in_the_middle = __toESM(require_in_the_middle);
37
+ let import_in_the_middle = require("import-in-the-middle");
38
+ import_in_the_middle = __toESM(import_in_the_middle);
37
39
  let __use_tusk_drift_schemas_google_protobuf_struct = require("@use-tusk/drift-schemas/google/protobuf/struct");
38
40
  __use_tusk_drift_schemas_google_protobuf_struct = __toESM(__use_tusk_drift_schemas_google_protobuf_struct);
39
41
  let __opentelemetry_api = require("@opentelemetry/api");
@@ -89,7 +91,7 @@ var TdInstrumentationAbstract = class {
89
91
 
90
92
  //#endregion
91
93
  //#region package.json
92
- var version = "0.1.0";
94
+ var version = "0.1.2";
93
95
 
94
96
  //#endregion
95
97
  //#region src/version.ts
@@ -414,10 +416,11 @@ function sendUnpatchedDependencyAlert({ method, spanId, traceId, stackTrace }) {
414
416
 
415
417
  //#endregion
416
418
  //#region src/instrumentation/core/baseClasses/TdInstrumentationBase.ts
417
- var TdInstrumentationBase = class extends TdInstrumentationAbstract {
419
+ var TdInstrumentationBase = class TdInstrumentationBase extends TdInstrumentationAbstract {
418
420
  constructor(instrumentationName, config = {}) {
419
421
  super(instrumentationName, config);
420
422
  this._modules = [];
423
+ this._hooks = [];
421
424
  this._enabled = false;
422
425
  let modules = this.init();
423
426
  if (modules && !Array.isArray(modules)) modules = [modules];
@@ -458,12 +461,35 @@ var TdInstrumentationBase = class extends TdInstrumentationAbstract {
458
461
  const onRequire = (exports$1, name, baseDir) => {
459
462
  return this._onRequire(module$1, exports$1, name, baseDir);
460
463
  };
461
- new require_in_the_middle.Hook([module$1.name], { internals: true }, onRequire);
464
+ const hookFn = (exports$1, name, baseDir) => {
465
+ if (!baseDir && path.default.isAbsolute(name)) {
466
+ const parsedPath = path.default.parse(name);
467
+ name = parsedPath.name;
468
+ baseDir = parsedPath.dir;
469
+ }
470
+ return this._onRequire(module$1, exports$1, name, baseDir || void 0);
471
+ };
472
+ const cjsHook = new require_in_the_middle.Hook([module$1.name], { internals: true }, onRequire);
473
+ this._hooks.push(cjsHook);
474
+ const esmHook = new import_in_the_middle.Hook([module$1.name], { internals: false }, hookFn);
475
+ this._hooks.push(esmHook);
462
476
  }
463
477
  }
464
478
  isEnabled() {
465
479
  return this._enabled;
466
480
  }
481
+ /**
482
+ * Mark a module as patched.
483
+ */
484
+ markModuleAsPatched(moduleExports) {
485
+ TdInstrumentationBase._patchedModules.add(moduleExports);
486
+ }
487
+ /**
488
+ * Check if a module has already been patched.
489
+ */
490
+ isModulePatched(moduleExports) {
491
+ return TdInstrumentationBase._patchedModules.has(moduleExports);
492
+ }
467
493
  _onRequire(module$1, exports$1, name, baseDir) {
468
494
  if (!baseDir) {
469
495
  if (typeof module$1.patch === "function") {
@@ -526,6 +552,7 @@ var TdInstrumentationBase = class extends TdInstrumentationAbstract {
526
552
  }, exports$1);
527
553
  }
528
554
  };
555
+ TdInstrumentationBase._patchedModules = /* @__PURE__ */ new WeakSet();
529
556
 
530
557
  //#endregion
531
558
  //#region src/instrumentation/core/baseClasses/TdInstrumentationNodeModule.ts
@@ -1930,6 +1957,7 @@ function wrap(target, propertyName, wrapper) {
1930
1957
  wrapped._original = original;
1931
1958
  wrapped._propertyName = propertyName;
1932
1959
  target[propertyName] = wrapped;
1960
+ return wrapped;
1933
1961
  }
1934
1962
 
1935
1963
  //#endregion
@@ -2212,18 +2240,25 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2212
2240
  _patchHttpModule(httpModule, protocol) {
2213
2241
  const protocolUpper = protocol.toUpperCase();
2214
2242
  logger.debug(`[HttpInstrumentation] Patching ${protocolUpper} module in ${this.mode} mode`);
2215
- if (httpModule._tdPatched) {
2243
+ if (this.isModulePatched(httpModule)) {
2216
2244
  logger.debug(`[HttpInstrumentation] ${protocolUpper} module already patched, skipping`);
2217
2245
  return httpModule;
2218
2246
  }
2219
- this._wrap(httpModule, "request", this._getRequestPatchFn(protocol));
2220
- this._wrap(httpModule, "get", this._getGetPatchFn(protocol));
2247
+ if (httpModule[Symbol.toStringTag] === "Module") {
2248
+ if (httpModule.default) {
2249
+ this._wrap(httpModule.default, "request", this._getRequestPatchFn(protocol));
2250
+ this._wrap(httpModule.default, "get", this._getGetPatchFn(protocol));
2251
+ }
2252
+ } else {
2253
+ this._wrap(httpModule, "request", this._getRequestPatchFn(protocol));
2254
+ this._wrap(httpModule, "get", this._getGetPatchFn(protocol));
2255
+ }
2221
2256
  const HttpServer = httpModule.Server;
2222
2257
  if (HttpServer && HttpServer.prototype) {
2223
2258
  this._wrap(HttpServer.prototype, "emit", this._getServerEmitPatchFn(protocol));
2224
2259
  logger.debug(`[HttpInstrumentation] Wrapped Server.prototype.emit for ${protocolUpper}`);
2225
2260
  }
2226
- httpModule._tdPatched = true;
2261
+ this.markModuleAsPatched(httpModule);
2227
2262
  logger.debug(`[HttpInstrumentation] ${protocolUpper} module patching complete`);
2228
2263
  return httpModule;
2229
2264
  }
@@ -2568,7 +2603,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2568
2603
  }
2569
2604
  });
2570
2605
  }
2571
- _captureClientRequestBody(req, spanInfo, inputValue, schemaMerges) {
2606
+ _captureClientRequestBody(req, spanInfo, inputValue, schemaMerges, onBodyCaptured) {
2572
2607
  const requestBodyChunks = [];
2573
2608
  let requestBodyCaptured = false;
2574
2609
  const originalWrite = req.write?.bind(req);
@@ -2590,6 +2625,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2590
2625
  body: encodedBody,
2591
2626
  bodySize: bodyBuffer.length
2592
2627
  };
2628
+ if (onBodyCaptured) onBodyCaptured(updatedInputValue);
2593
2629
  SpanUtils.addSpanAttributes(spanInfo.span, {
2594
2630
  inputValue: updatedInputValue,
2595
2631
  inputSchemaMerges: {
@@ -2611,7 +2647,10 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2611
2647
  }
2612
2648
  _handleOutboundRequestInSpan(originalRequest, args, spanInfo, inputValue, schemaMerges) {
2613
2649
  const req = originalRequest.apply(this, args);
2614
- this._captureClientRequestBody(req, spanInfo, inputValue, schemaMerges);
2650
+ let completeInputValue = inputValue;
2651
+ this._captureClientRequestBody(req, spanInfo, inputValue, schemaMerges, (updatedInputValue) => {
2652
+ completeInputValue = updatedInputValue;
2653
+ });
2615
2654
  req.on("response", (res) => {
2616
2655
  logger.debug(`[HttpInstrumentation] HTTP response received: ${res.statusCode} (${SpanUtils.getTraceInfo()})`);
2617
2656
  const outputValue = {
@@ -2651,7 +2690,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2651
2690
  },
2652
2691
  headers: { matchImportance: 0 }
2653
2692
  },
2654
- inputValue
2693
+ inputValue: completeInputValue
2655
2694
  });
2656
2695
  } catch (error) {
2657
2696
  logger.error(`[HttpInstrumentation] Error processing response body:`, error);
@@ -2669,7 +2708,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2669
2708
  },
2670
2709
  headers: { matchImportance: 0 }
2671
2710
  },
2672
- inputValue
2711
+ inputValue: completeInputValue
2673
2712
  });
2674
2713
  } catch (error) {
2675
2714
  logger.error(`[HttpInstrumentation] Error adding output attributes to span:`, error);
@@ -2912,7 +2951,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2912
2951
  return fallback;
2913
2952
  }
2914
2953
  _wrap(target, propertyName, wrapper) {
2915
- wrap(target, propertyName, wrapper);
2954
+ return wrap(target, propertyName, wrapper);
2916
2955
  }
2917
2956
  };
2918
2957
 
@@ -3307,7 +3346,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3307
3346
  }
3308
3347
  _patchPgModule(pgModule) {
3309
3348
  logger.debug(`[PgInstrumentation] Patching PG module in ${this.mode} mode`);
3310
- if (pgModule._tdPatched) {
3349
+ if (this.isModulePatched(pgModule)) {
3311
3350
  logger.debug(`[PgInstrumentation] PG module already patched, skipping`);
3312
3351
  return pgModule;
3313
3352
  }
@@ -3319,7 +3358,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3319
3358
  this._wrap(pgModule.Client.prototype, "connect", this._getConnectPatchFn("client"));
3320
3359
  logger.debug(`[PgInstrumentation] Wrapped Client.prototype.connect`);
3321
3360
  }
3322
- pgModule._tdPatched = true;
3361
+ this.markModuleAsPatched(pgModule);
3323
3362
  logger.debug(`[PgInstrumentation] PG module patching complete`);
3324
3363
  return pgModule;
3325
3364
  }
@@ -3641,7 +3680,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3641
3680
  SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
3642
3681
  }
3643
3682
  _patchPgPoolModule(pgPoolModule) {
3644
- if (pgPoolModule._tdPatched) {
3683
+ if (this.isModulePatched(pgPoolModule)) {
3645
3684
  logger.debug(`[PgInstrumentation] PG Pool module already patched, skipping`);
3646
3685
  return pgPoolModule;
3647
3686
  }
@@ -3653,7 +3692,7 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3653
3692
  this._wrap(pgPoolModule.prototype, "connect", this._getPoolConnectPatchFn());
3654
3693
  logger.debug(`[PgInstrumentation] Wrapped Pool.prototype.connect`);
3655
3694
  }
3656
- pgPoolModule._tdPatched = true;
3695
+ this.markModuleAsPatched(pgPoolModule);
3657
3696
  logger.debug(`[PgInstrumentation] PG Pool module patching complete`);
3658
3697
  return pgPoolModule;
3659
3698
  }
@@ -3764,6 +3803,17 @@ var PgInstrumentation = class extends TdInstrumentationBase {
3764
3803
  }
3765
3804
  };
3766
3805
 
3806
+ //#endregion
3807
+ //#region src/instrumentation/libraries/postgres/types.ts
3808
+ function isPostgresOutputValueType(value) {
3809
+ return value !== null && value !== void 0 && typeof value === "object" && "_tdOriginalFormat" in value && Object.values(PostgresReturnType).includes(value._tdOriginalFormat);
3810
+ }
3811
+ let PostgresReturnType = /* @__PURE__ */ function(PostgresReturnType$1) {
3812
+ PostgresReturnType$1["ARRAY"] = "array";
3813
+ PostgresReturnType$1["OBJECT"] = "object";
3814
+ return PostgresReturnType$1;
3815
+ }({});
3816
+
3767
3817
  //#endregion
3768
3818
  //#region src/instrumentation/libraries/postgres/Instrumentation.ts
3769
3819
  var PostgresInstrumentation = class extends TdInstrumentationBase {
@@ -3782,25 +3832,41 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
3782
3832
  }
3783
3833
  _patchPostgresModule(postgresModule) {
3784
3834
  logger.debug(`[PostgresInstrumentation] Patching Postgres module in ${this.mode} mode`);
3785
- if (postgresModule._tdPatched) {
3835
+ if (this.isModulePatched(postgresModule)) {
3786
3836
  logger.debug(`[PostgresInstrumentation] Postgres module already patched, skipping`);
3787
3837
  return postgresModule;
3788
3838
  }
3789
- if (typeof postgresModule === "function") {
3839
+ const self$1 = this;
3840
+ if (postgresModule[Symbol.toStringTag] === "Module") {
3841
+ logger.debug(`[PostgresInstrumentation] Wrapping ESM default export`);
3842
+ this._wrap(postgresModule, "default", (originalFunction) => {
3843
+ return function(...args) {
3844
+ return self$1._handlePostgresConnection(originalFunction, args);
3845
+ };
3846
+ });
3847
+ } else {
3848
+ logger.debug(`[PostgresInstrumentation] Module is a function (CJS style)`);
3790
3849
  const originalFunction = postgresModule;
3791
- const self$1 = this;
3792
3850
  const wrappedFunction = function(...args) {
3851
+ logger.debug(`[PostgresInstrumentation] Wrapped postgres() (CJS) called with args:`, args);
3793
3852
  return self$1._handlePostgresConnection(originalFunction, args);
3794
3853
  };
3795
3854
  Object.setPrototypeOf(wrappedFunction, Object.getPrototypeOf(originalFunction));
3796
3855
  Object.defineProperty(wrappedFunction, "name", { value: originalFunction.name });
3856
+ for (const key in originalFunction) if (originalFunction.hasOwnProperty(key)) wrappedFunction[key] = originalFunction[key];
3857
+ Object.getOwnPropertyNames(originalFunction).forEach((key) => {
3858
+ if (key !== "prototype" && key !== "length" && key !== "name") {
3859
+ const descriptor = Object.getOwnPropertyDescriptor(originalFunction, key);
3860
+ if (descriptor) Object.defineProperty(wrappedFunction, key, descriptor);
3861
+ }
3862
+ });
3797
3863
  postgresModule = wrappedFunction;
3798
3864
  }
3799
3865
  if (postgresModule.sql && typeof postgresModule.sql === "function") {
3800
3866
  this._wrap(postgresModule, "sql", this._getSqlPatchFn());
3801
3867
  logger.debug(`[PostgresInstrumentation] Wrapped sql function`);
3802
3868
  }
3803
- postgresModule._tdPatched = true;
3869
+ this.markModuleAsPatched(postgresModule);
3804
3870
  logger.debug(`[PostgresInstrumentation] Postgres module patching complete`);
3805
3871
  return postgresModule;
3806
3872
  }
@@ -4262,8 +4328,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4262
4328
  const isResultObject = processedResult && typeof processedResult === "object" && "rows" in processedResult;
4263
4329
  const rows = isResultObject ? processedResult.rows || [] : processedResult || [];
4264
4330
  return Object.assign(rows, {
4265
- command: isResultObject ? processedResult.command : "SELECT",
4266
- count: isResultObject ? processedResult.count : rows.length
4331
+ command: isResultObject ? processedResult.command : void 0,
4332
+ count: isResultObject ? processedResult.count : void 0
4267
4333
  });
4268
4334
  }
4269
4335
  async handleReplayUnsafeQuery({ inputValue, spanInfo, submodule, name }) {
@@ -4314,8 +4380,8 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4314
4380
  return Object.values(row);
4315
4381
  });
4316
4382
  return Object.assign(valueArrays, {
4317
- command: isResultObject ? result.command : "SELECT",
4318
- count: isResultObject ? result.count : valueArrays.length
4383
+ command: isResultObject ? result.command : void 0,
4384
+ count: isResultObject ? result.count : void 0
4319
4385
  });
4320
4386
  });
4321
4387
  } });
@@ -4325,61 +4391,40 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4325
4391
  * based on common PostgreSQL data patterns.
4326
4392
  */
4327
4393
  convertPostgresTypes(result) {
4328
- if (!result) return result;
4329
- if (result && typeof result === "object" && "rows" in result) {
4330
- const convertedResult = { ...result };
4331
- if (Array.isArray(result.rows)) convertedResult.rows = result.rows.map((row) => {
4332
- if (typeof row !== "object" || row === null) return row;
4333
- const convertedRow = { ...row };
4334
- Object.keys(row).forEach((fieldName) => {
4335
- const value = row[fieldName];
4336
- if (value === null || value === void 0) return;
4337
- if (typeof value === "string") {
4338
- if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
4339
- const dateObj = new Date(value);
4340
- if (!isNaN(dateObj.getTime())) convertedRow[fieldName] = dateObj;
4341
- }
4342
- }
4343
- });
4344
- return convertedRow;
4345
- });
4346
- return convertedResult;
4394
+ if (!isPostgresOutputValueType(result)) {
4395
+ logger.error(`[PostgresInstrumentation] output value is not of type PostgresOutputValueType: ${JSON.stringify(result)}`);
4396
+ return;
4397
+ }
4398
+ if (!result) return;
4399
+ const { _tdOriginalFormat: originalFormat,...convertedResult } = result;
4400
+ if (originalFormat === PostgresReturnType.OBJECT) return convertedResult;
4401
+ else if (originalFormat === PostgresReturnType.ARRAY) return convertedResult.rows || [];
4402
+ else {
4403
+ logger.error(`[PostgresInstrumentation] Invalid result format: ${JSON.stringify(result)}`);
4404
+ return;
4347
4405
  }
4348
- if (Array.isArray(result)) return result.map((row) => {
4349
- if (typeof row !== "object" || row === null) return row;
4350
- const convertedRow = { ...row };
4351
- Object.keys(row).forEach((fieldName) => {
4352
- const value = row[fieldName];
4353
- if (value === null || value === void 0) return;
4354
- if (typeof value === "string") {
4355
- if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
4356
- const dateObj = new Date(value);
4357
- if (!isNaN(dateObj.getTime())) convertedRow[fieldName] = dateObj;
4358
- }
4359
- }
4360
- });
4361
- return convertedRow;
4362
- });
4363
- return result;
4364
4406
  }
4365
4407
  _addOutputAttributesToSpan(spanInfo, result) {
4366
4408
  if (!result) return;
4367
4409
  let outputValue;
4368
- if (Array.isArray(result)) outputValue = {
4369
- count: result.length,
4370
- rows: result,
4371
- command: "SELECT"
4372
- };
4373
- else if (result && typeof result === "object" && "count" in result) outputValue = {
4374
- count: result.count || 0,
4375
- rows: result.rows || result,
4376
- command: result.command || "UNKNOWN"
4377
- };
4378
- else outputValue = {
4379
- count: 1,
4380
- rows: [result],
4381
- command: "UNKNOWN"
4382
- };
4410
+ if (Array.isArray(result)) {
4411
+ logger.debug(`[PostgresInstrumentation] Adding output attributes to span for array result: ${JSON.stringify(result)}`);
4412
+ outputValue = {
4413
+ _tdOriginalFormat: PostgresReturnType.ARRAY,
4414
+ rows: result
4415
+ };
4416
+ } else if (typeof result === "object") {
4417
+ logger.debug(`[PostgresInstrumentation] Adding output attributes to span for object result: ${JSON.stringify(result)}`);
4418
+ outputValue = {
4419
+ _tdOriginalFormat: PostgresReturnType.OBJECT,
4420
+ count: result.count,
4421
+ rows: result.rows,
4422
+ command: result.command
4423
+ };
4424
+ } else {
4425
+ logger.error(`[PostgresInstrumentation] Invalid result format: ${JSON.stringify(result)}`);
4426
+ return;
4427
+ }
4383
4428
  SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
4384
4429
  }
4385
4430
  _wrap(target, propertyName, wrapper) {
@@ -4413,7 +4458,7 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
4413
4458
  }
4414
4459
  _patchNetModule(netModule) {
4415
4460
  logger.debug(`[TcpInstrumentation] Patching NET module in ${this.mode} mode`);
4416
- if (netModule._tdPatched) {
4461
+ if (this.isModulePatched(netModule)) {
4417
4462
  logger.debug(`[TcpInstrumentation] NET module already patched, skipping`);
4418
4463
  return netModule;
4419
4464
  }
@@ -4430,7 +4475,7 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
4430
4475
  netModule.Socket.prototype.write = function(...args) {
4431
4476
  return self$1._handleTcpCall("write", originalWrite, args, this);
4432
4477
  };
4433
- netModule._tdPatched = true;
4478
+ this.markModuleAsPatched(netModule);
4434
4479
  return netModule;
4435
4480
  }
4436
4481
  _logUnpatchedDependency(methodName, currentSpanInfo, socketContext) {
@@ -4493,7 +4538,7 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
4493
4538
  })];
4494
4539
  }
4495
4540
  _patchJsonwebtokenModule(jwtModule) {
4496
- if (jwtModule._tdPatched) {
4541
+ if (this.isModulePatched(jwtModule)) {
4497
4542
  logger.debug(`[JsonwebtokenInstrumentation] jsonwebtoken module already patched, skipping`);
4498
4543
  return jwtModule;
4499
4544
  }
@@ -4508,7 +4553,7 @@ var JsonwebtokenInstrumentation = class extends TdInstrumentationBase {
4508
4553
  this._wrap(jwtModule, "sign", this._getSignPatchFn());
4509
4554
  logger.debug(`[JsonwebtokenInstrumentation] Wrapped jwt.sign`);
4510
4555
  }
4511
- jwtModule._tdPatched = true;
4556
+ this.markModuleAsPatched(jwtModule);
4512
4557
  logger.debug(`[JsonwebtokenInstrumentation] jsonwebtoken module patching complete`);
4513
4558
  return jwtModule;
4514
4559
  }
@@ -4906,7 +4951,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
4906
4951
  }
4907
4952
  _patchJwksRsaModule(jwksModule) {
4908
4953
  logger.debug(`[JwksRsaInstrumentation] Patching jwks-rsa module, current mode: ${this.tuskDrift.getMode()}`);
4909
- if (jwksModule._tdPatched) {
4954
+ if (this.isModulePatched(jwksModule)) {
4910
4955
  logger.debug(`[JwksRsaInstrumentation] jwks-rsa module already patched, skipping`);
4911
4956
  return jwksModule;
4912
4957
  }
@@ -4926,7 +4971,7 @@ var JwksRsaInstrumentation = class extends TdInstrumentationBase {
4926
4971
  };
4927
4972
  logger.debug(`[JwksRsaInstrumentation] Patched expressJwtSecret method`);
4928
4973
  }
4929
- jwksModule._tdPatched = true;
4974
+ this.markModuleAsPatched(jwksModule);
4930
4975
  logger.debug(`[JwksRsaInstrumentation] jwks-rsa module patching complete`);
4931
4976
  return jwksModule;
4932
4977
  }