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

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
@@ -1,10 +1,10 @@
1
1
  import { createRequire } from "node:module";
2
2
  import * as fs$1 from "fs";
3
3
  import fs from "fs";
4
- import os from "os";
5
4
  import * as path$1 from "path";
6
5
  import path, { normalize } from "path";
7
- import { satisfies } from "semver";
6
+ import { parse, satisfies } from "semver";
7
+ import os from "os";
8
8
  import { Hook } from "require-in-the-middle";
9
9
  import { Hook as Hook$1 } from "import-in-the-middle";
10
10
  import { Struct, Value } from "@use-tusk/drift-schemas/google/protobuf/struct";
@@ -53,6 +53,182 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
53
53
  }) : target, mod));
54
54
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
55
55
 
56
+ //#endregion
57
+ //#region src/nextjs/utils.ts
58
+ /**
59
+ * Get the installed Next.js version by reading package.json from node_modules.
60
+ *
61
+ * @returns The Next.js version string, or undefined if not found
62
+ */
63
+ function getNextjsVersion() {
64
+ try {
65
+ const nextPackageJsonPath = path$1.join(process.cwd(), "node_modules", "next", "package.json");
66
+ if (fs$1.existsSync(nextPackageJsonPath)) return JSON.parse(fs$1.readFileSync(nextPackageJsonPath, "utf-8")).version;
67
+ } catch (error) {}
68
+ }
69
+ /**
70
+ * Parse a semantic version string into its components.
71
+ *
72
+ * @param version - The version string to parse (e.g., "15.0.0-canary.124")
73
+ * @returns Parsed version object with major, minor, patch, and prerelease
74
+ */
75
+ function parseVersion(version$1) {
76
+ try {
77
+ const parsed = parse(version$1);
78
+ if (!parsed) return {
79
+ major: void 0,
80
+ minor: void 0,
81
+ patch: void 0,
82
+ prerelease: void 0
83
+ };
84
+ return {
85
+ major: parsed.major,
86
+ minor: parsed.minor,
87
+ patch: parsed.patch,
88
+ prerelease: parsed.prerelease.length > 0 ? parsed.prerelease.join(".") : void 0
89
+ };
90
+ } catch {
91
+ return {
92
+ major: void 0,
93
+ minor: void 0,
94
+ patch: void 0,
95
+ prerelease: void 0
96
+ };
97
+ }
98
+ }
99
+ /**
100
+ * Check if the Next.js version requires the instrumentationHook to be set.
101
+ * From Next.js 15.0.0-rc.1 onwards, the instrumentationHook is no longer needed
102
+ * and Next.js will warn if it's set.
103
+ *
104
+ * @param version - The Next.js version string
105
+ * @returns true if instrumentationHook should be set, false otherwise
106
+ */
107
+ function shouldSetInstrumentationHook(version$1) {
108
+ if (!version$1) return true;
109
+ const { major, minor, patch, prerelease } = parseVersion(version$1);
110
+ if (major === void 0 || minor === void 0 || patch === void 0) return true;
111
+ if (major >= 16) return false;
112
+ if (major < 15) return true;
113
+ if (major === 15) {
114
+ if (minor > 0 || patch > 0) return false;
115
+ if (minor === 0 && patch === 0 && prerelease === void 0) return false;
116
+ if (prerelease?.startsWith("rc.")) {
117
+ if (parseInt(prerelease.split(".")[1] || "0", 10) >= 1) return false;
118
+ }
119
+ if (prerelease?.startsWith("canary.")) {
120
+ if (parseInt(prerelease.split(".")[1] || "0", 10) >= 124) return false;
121
+ }
122
+ return true;
123
+ }
124
+ return true;
125
+ }
126
+ /**
127
+ * Log a message if debug mode is enabled.
128
+ *
129
+ * @param debug - Whether debug mode is enabled
130
+ * @param message - The message to log
131
+ */
132
+ function debugLog(debug, message) {
133
+ if (debug) console.log(`[Tusk Drift] ${message}`);
134
+ }
135
+ /**
136
+ * Log a warning message if warnings are not suppressed.
137
+ *
138
+ * @param suppress - Whether to suppress the warning
139
+ * @param message - The warning message to log
140
+ */
141
+ function warn(suppress, message) {
142
+ if (!suppress) console.warn(`[Tusk Drift] ${message}`);
143
+ }
144
+
145
+ //#endregion
146
+ //#region src/nextjs/withTuskDrift.ts
147
+ /**
148
+ * Wraps your Next.js configuration with Tusk Drift instrumentation setup.
149
+ *
150
+ * This function automatically configures Next.js to work with Tusk Drift by:
151
+ * - Enabling the Next.js instrumentation hook (for Next.js < 15.0.0-rc.1)
152
+ * - Configuring webpack externals to prevent bundling of core instrumentation packages
153
+ * - Preserving your existing Next.js configuration and webpack customizations
154
+ *
155
+ * @param nextConfig - Your existing Next.js configuration object (optional)
156
+ * @param options - Additional options to configure Tusk Drift's behavior (optional)
157
+ * @returns The wrapped Next.js configuration with Tusk Drift instrumentation enabled
158
+ *
159
+ * @example
160
+ * Basic usage:
161
+ * ```javascript
162
+ * // next.config.js
163
+ * const { withTuskDrift } = require('@use-tusk/drift-node-sdk');
164
+ *
165
+ * module.exports = withTuskDrift({
166
+ * // Your Next.js config
167
+ * });
168
+ * ```
169
+ *
170
+ * @example
171
+ * With debug logging:
172
+ * ```javascript
173
+ * // next.config.js
174
+ * const { withTuskDrift } = require('@use-tusk/drift-node-sdk');
175
+ *
176
+ * module.exports = withTuskDrift(
177
+ * {
178
+ * // Your Next.js config
179
+ * },
180
+ * {
181
+ * debug: true,
182
+ * }
183
+ * );
184
+ * ```
185
+ *
186
+ * @remarks
187
+ * The following webpack externals are added for server-side builds:
188
+ * - `require-in-the-middle` - Required for CommonJS module interception
189
+ * - `jsonpath` - Required for schema manipulation
190
+ */
191
+ function withTuskDrift(nextConfig = {}, options = {}) {
192
+ const config = nextConfig;
193
+ const debug = options.debug || false;
194
+ const suppressAllWarnings = options.suppressWarnings || false;
195
+ const nextjsVersion = getNextjsVersion();
196
+ if (nextjsVersion) debugLog(debug, `Detected Next.js version: ${nextjsVersion}`);
197
+ else warn(suppressAllWarnings || false, "Could not detect Next.js version. Some features may not work correctly. If you encounter issues, please ensure Next.js is properly installed.");
198
+ const needsInstrumentationHook = !options.disableInstrumentationHook && shouldSetInstrumentationHook(nextjsVersion);
199
+ const wrappedConfig = {
200
+ ...config,
201
+ ...needsInstrumentationHook ? { experimental: {
202
+ ...config.experimental,
203
+ instrumentationHook: true
204
+ } } : { experimental: config.experimental },
205
+ webpack: (webpackConfig, webpackOptions) => {
206
+ if (webpackOptions.isServer) {
207
+ const originalExternals = webpackConfig.externals;
208
+ const coreExternals = ["require-in-the-middle", "jsonpath"];
209
+ if (!originalExternals) {
210
+ webpackConfig.externals = coreExternals;
211
+ debugLog(debug, "Created new externals array with core packages");
212
+ } else if (Array.isArray(originalExternals)) {
213
+ for (const pkg of coreExternals) if (!originalExternals.includes(pkg)) {
214
+ originalExternals.push(pkg);
215
+ debugLog(debug, `Added ${pkg} to webpack externals`);
216
+ }
217
+ } else {
218
+ webpackConfig.externals = [originalExternals, ...coreExternals];
219
+ debugLog(debug, "Wrapped existing externals with core packages");
220
+ }
221
+ }
222
+ if (typeof config.webpack === "function") return config.webpack(webpackConfig, webpackOptions);
223
+ return webpackConfig;
224
+ }
225
+ };
226
+ if (needsInstrumentationHook) debugLog(debug, "Set experimental.instrumentationHook to true");
227
+ else debugLog(debug, "Skipped setting experimental.instrumentationHook (not needed for Next.js 15.0.0-rc.1+)");
228
+ if (options.disableInstrumentationHook && nextjsVersion && shouldSetInstrumentationHook(nextjsVersion)) warn(suppressAllWarnings || false, "You disabled instrumentationHook, but your Next.js version requires it. Tusk Drift may not initialize properly. Please remove the disableInstrumentationHook option.");
229
+ return wrappedConfig;
230
+ }
231
+
56
232
  //#endregion
57
233
  //#region src/instrumentation/core/baseClasses/TdInstrumentationAbstract.ts
58
234
  var TdInstrumentationAbstract = class {
@@ -76,7 +252,7 @@ var TdInstrumentationAbstract = class {
76
252
 
77
253
  //#endregion
78
254
  //#region package.json
79
- var version = "0.1.5";
255
+ var version = "0.1.6";
80
256
 
81
257
  //#endregion
82
258
  //#region src/version.ts
@@ -1172,7 +1348,7 @@ var JsonSchemaHelper = class JsonSchemaHelper {
1172
1348
  }
1173
1349
  /**
1174
1350
  * Generate schema from data object using standardized types
1175
- *
1351
+ *
1176
1352
  * Note: We properties always exists on JsonSchema because proto3 maps cannot be marked optional.
1177
1353
  * The JSON data is a bit inefficient because of this, but the easiest way to handle this is to keep it for now.
1178
1354
  */
@@ -2241,6 +2417,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2241
2417
  };
2242
2418
  const replayTraceId = this.replayHooks.extractTraceIdFromHeaders(req);
2243
2419
  if (!replayTraceId) return originalHandler.call(this);
2420
+ logger.debug(`[HttpInstrumentation] Setting replay trace id`, replayTraceId);
2244
2421
  const envVars = this.replayHooks.extractEnvVarsFromHeaders(req);
2245
2422
  if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
2246
2423
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
@@ -4642,7 +4819,9 @@ var TdMysql2QueryMock = class {
4642
4819
 
4643
4820
  //#endregion
4644
4821
  //#region src/instrumentation/libraries/mysql2/Instrumentation.ts
4645
- const SUPPORTED_VERSIONS$1 = [">=3.0.0 <4.0.0"];
4822
+ const COMPLETE_SUPPORTED_VERSIONS = ">=2.3.3 <4.0.0";
4823
+ const V2_3_3_TO_3_11_4 = ">=2.3.3 <3.11.5";
4824
+ const V3_11_5_TO_4_0 = ">=3.11.5 <4.0.0";
4646
4825
  var Mysql2Instrumentation = class extends TdInstrumentationBase {
4647
4826
  constructor(config = {}) {
4648
4827
  super("mysql2", config);
@@ -4653,42 +4832,112 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
4653
4832
  init() {
4654
4833
  return [new TdInstrumentationNodeModule({
4655
4834
  name: "mysql2",
4656
- supportedVersions: SUPPORTED_VERSIONS$1,
4835
+ supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
4657
4836
  files: [
4658
4837
  new TdInstrumentationNodeModuleFile({
4659
4838
  name: "mysql2/lib/connection.js",
4660
- supportedVersions: SUPPORTED_VERSIONS$1,
4661
- patch: (moduleExports) => this._patchConnection(moduleExports)
4839
+ supportedVersions: [V2_3_3_TO_3_11_4],
4840
+ patch: (moduleExports) => this._patchConnectionV2(moduleExports)
4841
+ }),
4842
+ new TdInstrumentationNodeModuleFile({
4843
+ name: "mysql2/lib/base/connection.js",
4844
+ supportedVersions: [V3_11_5_TO_4_0],
4845
+ patch: (moduleExports) => this._patchBaseConnection(moduleExports)
4846
+ }),
4847
+ new TdInstrumentationNodeModuleFile({
4848
+ name: "mysql2/lib/connection.js",
4849
+ supportedVersions: [V3_11_5_TO_4_0],
4850
+ patch: (moduleExports) => this._patchConnectionV3(moduleExports)
4662
4851
  }),
4663
4852
  new TdInstrumentationNodeModuleFile({
4664
4853
  name: "mysql2/lib/pool.js",
4665
- supportedVersions: SUPPORTED_VERSIONS$1,
4666
- patch: (moduleExports) => this._patchPool(moduleExports)
4854
+ supportedVersions: [V2_3_3_TO_3_11_4],
4855
+ patch: (moduleExports) => this._patchPoolV2(moduleExports)
4856
+ }),
4857
+ new TdInstrumentationNodeModuleFile({
4858
+ name: "mysql2/lib/pool.js",
4859
+ supportedVersions: [V3_11_5_TO_4_0],
4860
+ patch: (moduleExports) => this._patchPoolV3(moduleExports)
4861
+ }),
4862
+ new TdInstrumentationNodeModuleFile({
4863
+ name: "mysql2/lib/pool_connection.js",
4864
+ supportedVersions: [V2_3_3_TO_3_11_4],
4865
+ patch: (moduleExports) => this._patchPoolConnectionV2(moduleExports)
4667
4866
  }),
4668
4867
  new TdInstrumentationNodeModuleFile({
4669
4868
  name: "mysql2/lib/pool_connection.js",
4670
- supportedVersions: SUPPORTED_VERSIONS$1,
4671
- patch: (moduleExports) => this._patchPoolConnection(moduleExports)
4869
+ supportedVersions: [V3_11_5_TO_4_0],
4870
+ patch: (moduleExports) => this._patchPoolConnectionV3(moduleExports)
4672
4871
  }),
4673
4872
  new TdInstrumentationNodeModuleFile({
4674
4873
  name: "mysql2/lib/create_connection.js",
4675
- supportedVersions: SUPPORTED_VERSIONS$1,
4874
+ supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
4676
4875
  patch: (moduleExports) => this._patchCreateConnectionFile(moduleExports)
4677
4876
  }),
4678
4877
  new TdInstrumentationNodeModuleFile({
4679
4878
  name: "mysql2/lib/create_pool.js",
4680
- supportedVersions: SUPPORTED_VERSIONS$1,
4879
+ supportedVersions: [COMPLETE_SUPPORTED_VERSIONS],
4681
4880
  patch: (moduleExports) => this._patchCreatePoolFile(moduleExports)
4682
4881
  })
4683
4882
  ]
4684
4883
  })];
4685
4884
  }
4686
- _patchConnection(ConnectionClass) {
4687
- logger.debug(`[Mysql2Instrumentation] Patching Connection class`);
4885
+ _patchBaseConnection(BaseConnectionClass) {
4886
+ logger.debug(`[Mysql2Instrumentation] Patching BaseConnection class`);
4887
+ if (this.isModulePatched(BaseConnectionClass)) {
4888
+ logger.debug(`[Mysql2Instrumentation] BaseConnection class already patched, skipping`);
4889
+ return BaseConnectionClass;
4890
+ }
4891
+ if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.query) {
4892
+ if (!isWrapped$1(BaseConnectionClass.prototype.query)) {
4893
+ this._wrap(BaseConnectionClass.prototype, "query", this._getQueryPatchFn("connection"));
4894
+ logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.query`);
4895
+ }
4896
+ }
4897
+ if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.execute) {
4898
+ if (!isWrapped$1(BaseConnectionClass.prototype.execute)) {
4899
+ this._wrap(BaseConnectionClass.prototype, "execute", this._getExecutePatchFn("connection"));
4900
+ logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.execute`);
4901
+ }
4902
+ }
4903
+ if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.connect) {
4904
+ if (!isWrapped$1(BaseConnectionClass.prototype.connect)) {
4905
+ this._wrap(BaseConnectionClass.prototype, "connect", this._getConnectPatchFn("connection"));
4906
+ logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.connect`);
4907
+ }
4908
+ }
4909
+ if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.ping) {
4910
+ if (!isWrapped$1(BaseConnectionClass.prototype.ping)) {
4911
+ this._wrap(BaseConnectionClass.prototype, "ping", this._getPingPatchFn("connection"));
4912
+ logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.ping`);
4913
+ }
4914
+ }
4915
+ if (BaseConnectionClass.prototype && BaseConnectionClass.prototype.end) {
4916
+ if (!isWrapped$1(BaseConnectionClass.prototype.end)) {
4917
+ this._wrap(BaseConnectionClass.prototype, "end", this._getEndPatchFn("connection"));
4918
+ logger.debug(`[Mysql2Instrumentation] Wrapped BaseConnection.prototype.end`);
4919
+ }
4920
+ }
4921
+ this.markModuleAsPatched(BaseConnectionClass);
4922
+ logger.debug(`[Mysql2Instrumentation] BaseConnection class patching complete`);
4923
+ return BaseConnectionClass;
4924
+ }
4925
+ _patchConnectionV2(ConnectionClass) {
4926
+ logger.debug(`[Mysql2Instrumentation] Patching Connection class (v2)`);
4688
4927
  if (this.isModulePatched(ConnectionClass)) {
4689
4928
  logger.debug(`[Mysql2Instrumentation] Connection class already patched, skipping`);
4690
4929
  return ConnectionClass;
4691
4930
  }
4931
+ this._patchConnectionPrototypes(ConnectionClass);
4932
+ this.markModuleAsPatched(ConnectionClass);
4933
+ logger.debug(`[Mysql2Instrumentation] Connection class (v2) patching complete`);
4934
+ return ConnectionClass;
4935
+ }
4936
+ _patchConnectionV3(ConnectionClass) {
4937
+ logger.debug(`[Mysql2Instrumentation] Connection class (v3) - skipping (base patched)`);
4938
+ return ConnectionClass;
4939
+ }
4940
+ _patchConnectionPrototypes(ConnectionClass) {
4692
4941
  if (ConnectionClass.prototype && ConnectionClass.prototype.query) {
4693
4942
  if (!isWrapped$1(ConnectionClass.prototype.query)) {
4694
4943
  this._wrap(ConnectionClass.prototype, "query", this._getQueryPatchFn("connection"));
@@ -4719,16 +4968,30 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
4719
4968
  logger.debug(`[Mysql2Instrumentation] Wrapped Connection.prototype.end`);
4720
4969
  }
4721
4970
  }
4722
- this.markModuleAsPatched(ConnectionClass);
4723
- logger.debug(`[Mysql2Instrumentation] Connection class patching complete`);
4724
- return ConnectionClass;
4725
4971
  }
4726
- _patchPool(PoolClass) {
4727
- logger.debug(`[Mysql2Instrumentation] Patching Pool class`);
4972
+ _patchPoolV2(PoolClass) {
4973
+ logger.debug(`[Mysql2Instrumentation] Patching Pool class (v2)`);
4728
4974
  if (this.isModulePatched(PoolClass)) {
4729
4975
  logger.debug(`[Mysql2Instrumentation] Pool class already patched, skipping`);
4730
4976
  return PoolClass;
4731
4977
  }
4978
+ this._patchPoolPrototypes(PoolClass);
4979
+ this.markModuleAsPatched(PoolClass);
4980
+ logger.debug(`[Mysql2Instrumentation] Pool class (v2) patching complete`);
4981
+ return PoolClass;
4982
+ }
4983
+ _patchPoolV3(PoolClass) {
4984
+ logger.debug(`[Mysql2Instrumentation] Patching Pool class (v3)`);
4985
+ if (this.isModulePatched(PoolClass)) {
4986
+ logger.debug(`[Mysql2Instrumentation] Pool class already patched, skipping`);
4987
+ return PoolClass;
4988
+ }
4989
+ this._patchPoolPrototypes(PoolClass);
4990
+ this.markModuleAsPatched(PoolClass);
4991
+ logger.debug(`[Mysql2Instrumentation] Pool class (v3) patching complete`);
4992
+ return PoolClass;
4993
+ }
4994
+ _patchPoolPrototypes(PoolClass) {
4732
4995
  if (PoolClass.prototype && PoolClass.prototype.query) {
4733
4996
  if (!isWrapped$1(PoolClass.prototype.query)) {
4734
4997
  this._wrap(PoolClass.prototype, "query", this._getQueryPatchFn("pool"));
@@ -4747,30 +5010,19 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
4747
5010
  logger.debug(`[Mysql2Instrumentation] Wrapped Pool.prototype.getConnection`);
4748
5011
  }
4749
5012
  }
4750
- this.markModuleAsPatched(PoolClass);
4751
- logger.debug(`[Mysql2Instrumentation] Pool class patching complete`);
4752
- return PoolClass;
4753
- }
4754
- _patchPoolConnection(PoolConnectionClass) {
4755
- logger.debug(`[Mysql2Instrumentation] Patching PoolConnection class`);
4756
- if (this.isModulePatched(PoolConnectionClass)) {
4757
- logger.debug(`[Mysql2Instrumentation] PoolConnection class already patched, skipping`);
4758
- return PoolConnectionClass;
4759
- }
4760
- if (PoolConnectionClass.prototype && PoolConnectionClass.prototype.query) {
4761
- if (!isWrapped$1(PoolConnectionClass.prototype.query)) {
4762
- this._wrap(PoolConnectionClass.prototype, "query", this._getQueryPatchFn("poolConnection"));
4763
- logger.debug(`[Mysql2Instrumentation] Wrapped PoolConnection.prototype.query`);
4764
- }
4765
- }
4766
- if (PoolConnectionClass.prototype && PoolConnectionClass.prototype.execute) {
4767
- if (!isWrapped$1(PoolConnectionClass.prototype.execute)) {
4768
- this._wrap(PoolConnectionClass.prototype, "execute", this._getExecutePatchFn("poolConnection"));
4769
- logger.debug(`[Mysql2Instrumentation] Wrapped PoolConnection.prototype.execute`);
5013
+ if (PoolClass.prototype && PoolClass.prototype.end) {
5014
+ if (!isWrapped$1(PoolClass.prototype.end)) {
5015
+ this._wrap(PoolClass.prototype, "end", this._getEndPatchFn("pool"));
5016
+ logger.debug(`[Mysql2Instrumentation] Wrapped Pool.prototype.end`);
4770
5017
  }
4771
5018
  }
4772
- this.markModuleAsPatched(PoolConnectionClass);
4773
- logger.debug(`[Mysql2Instrumentation] PoolConnection class patching complete`);
5019
+ }
5020
+ _patchPoolConnectionV2(PoolConnectionClass) {
5021
+ logger.debug(`[Mysql2Instrumentation] PoolConnection class (v2) - skipping (inherits from Connection)`);
5022
+ return PoolConnectionClass;
5023
+ }
5024
+ _patchPoolConnectionV3(PoolConnectionClass) {
5025
+ logger.debug(`[Mysql2Instrumentation] PoolConnection class (v3) - skipping (inherits from Connection)`);
4774
5026
  return PoolConnectionClass;
4775
5027
  }
4776
5028
  _getQueryPatchFn(clientType) {
@@ -6602,7 +6854,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
6602
6854
  }
6603
6855
  const actualExports = moduleExports[Symbol.toStringTag] === "Module" ? moduleExports.default : moduleExports;
6604
6856
  if (!actualExports || !actualExports.prototype) {
6605
- logger.error(`[IORedisInstrumentation] Invalid Pipeline module exports, cannot patch`);
6857
+ logger.debug(`[IORedisInstrumentation] Invalid Pipeline module exports, cannot patch`);
6606
6858
  return moduleExports;
6607
6859
  }
6608
6860
  if (actualExports.prototype.exec) {
@@ -7521,6 +7773,372 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7521
7773
  };
7522
7774
  GrpcInstrumentation.metadataStore = /* @__PURE__ */ new Map();
7523
7775
 
7776
+ //#endregion
7777
+ //#region src/instrumentation/libraries/nextjs/Instrumentation.ts
7778
+ var NextjsInstrumentation = class extends TdInstrumentationBase {
7779
+ constructor(config = {}) {
7780
+ super("nextjs", config);
7781
+ this.INSTRUMENTATION_NAME = "NextjsInstrumentation";
7782
+ this.mode = config.mode || TuskDriftMode.DISABLED;
7783
+ this.replayHooks = new HttpReplayHooks();
7784
+ this.tuskDrift = TuskDriftCore.getInstance();
7785
+ logger.debug(`[NextjsInstrumentation] Constructor called with mode: ${this.mode}`);
7786
+ this._patchLoadedModules();
7787
+ }
7788
+ init() {
7789
+ logger.debug(`[NextjsInstrumentation] Initializing in ${this.mode} mode`);
7790
+ return [new TdInstrumentationNodeModule({
7791
+ name: "next/dist/server/base-server",
7792
+ supportedVersions: ["*"],
7793
+ patch: (moduleExports) => {
7794
+ return this._patchBaseServer(moduleExports);
7795
+ }
7796
+ })];
7797
+ }
7798
+ _patchLoadedModules() {
7799
+ logger.debug(`[NextjsInstrumentation] Checking for already-loaded Next.js modules`);
7800
+ const pattern = "/next/dist/server/base-server.js";
7801
+ let patchedCount = 0;
7802
+ for (const [modulePath, cached] of Object.entries(__require.cache)) if (modulePath.includes(pattern) && cached && cached.exports) {
7803
+ logger.debug(`[NextjsInstrumentation] Found ${pattern} at ${modulePath}, patching now`);
7804
+ this._patchBaseServer(cached.exports);
7805
+ patchedCount++;
7806
+ break;
7807
+ }
7808
+ if (patchedCount === 0) {
7809
+ logger.debug(`[NextjsInstrumentation] No Next.js server modules found in require.cache yet`);
7810
+ logger.debug(`[NextjsInstrumentation] Will wait for require-in-the-middle hooks to catch them`);
7811
+ } else logger.debug(`[NextjsInstrumentation] Patched ${patchedCount} already-loaded Next.js modules`);
7812
+ }
7813
+ _patchBaseServer(baseServerModule) {
7814
+ logger.debug(`[NextjsInstrumentation] Patching Next.js BaseServer in ${this.mode} mode`);
7815
+ const BaseServer = baseServerModule.default || baseServerModule.BaseServer || baseServerModule;
7816
+ if (BaseServer && BaseServer.prototype) {
7817
+ if (this.isModulePatched(BaseServer.prototype)) {
7818
+ logger.debug(`[NextjsInstrumentation] BaseServer.prototype already patched, skipping`);
7819
+ return baseServerModule;
7820
+ }
7821
+ if (BaseServer.prototype.handleRequest) {
7822
+ this._wrap(BaseServer.prototype, "handleRequest", this._getHandleRequestPatchFn());
7823
+ logger.debug(`[NextjsInstrumentation] Wrapped BaseServer.prototype.handleRequest`);
7824
+ this.markModuleAsPatched(BaseServer.prototype);
7825
+ } else logger.warn(`[NextjsInstrumentation] Could not find BaseServer.prototype.handleRequest`);
7826
+ } else logger.warn(`[NextjsInstrumentation] Could not find BaseServer class`);
7827
+ logger.debug(`[NextjsInstrumentation] Next.js BaseServer patching complete`);
7828
+ return baseServerModule;
7829
+ }
7830
+ _getHandleRequestPatchFn() {
7831
+ const self = this;
7832
+ return (originalHandleRequest) => {
7833
+ return async function(req, res, parsedUrl) {
7834
+ const method = req.method || "GET";
7835
+ const url = req.url || "/";
7836
+ logger.debug(`[NextjsInstrumentation] Intercepted Next.js request: ${method} ${url}`);
7837
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
7838
+ const inputValue = {
7839
+ method,
7840
+ url,
7841
+ target: url,
7842
+ headers: self._normalizeHeaders(req.headers || {})
7843
+ };
7844
+ const replayTraceId = self.replayHooks.extractTraceIdFromHeaders(req);
7845
+ if (!replayTraceId) {
7846
+ logger.debug(`[NextjsInstrumentation] No trace ID found, calling original handler`);
7847
+ return originalHandleRequest.call(this, req, res, parsedUrl);
7848
+ }
7849
+ const envVars = self.replayHooks.extractEnvVarsFromHeaders(req);
7850
+ if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
7851
+ const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
7852
+ if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
7853
+ return context.with(ctxWithReplayTraceId, () => {
7854
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalHandleRequest.call(this, req, res, parsedUrl), {
7855
+ name: url,
7856
+ kind: SpanKind.SERVER,
7857
+ packageName: "nextjs",
7858
+ submodule: method,
7859
+ packageType: PackageType.HTTP,
7860
+ instrumentationName: self.INSTRUMENTATION_NAME,
7861
+ inputValue,
7862
+ inputSchemaMerges: { headers: { matchImportance: 0 } },
7863
+ isPreAppStart: false
7864
+ }, (spanInfo) => {
7865
+ return self._handleNextjsRequestInSpan({
7866
+ req,
7867
+ res,
7868
+ parsedUrl,
7869
+ originalHandleRequest,
7870
+ spanInfo,
7871
+ inputValue,
7872
+ thisContext: this
7873
+ });
7874
+ });
7875
+ });
7876
+ } });
7877
+ else if (self.mode === TuskDriftMode.RECORD) {
7878
+ if (method.toUpperCase() === "OPTIONS" || !!req.headers["access-control-request-method"]) return originalHandleRequest.call(this, req, res, parsedUrl);
7879
+ if (!shouldSample({
7880
+ samplingRate: self.tuskDrift.getSamplingRate(),
7881
+ isAppReady: self.tuskDrift.isAppReady()
7882
+ })) {
7883
+ logger.debug(`[NextjsInstrumentation] Skipping server span due to sampling rate: ${url}`);
7884
+ return originalHandleRequest.call(this, req, res, parsedUrl);
7885
+ }
7886
+ logger.debug(`[NextjsInstrumentation] Creating server span for ${method} ${url}`);
7887
+ return handleRecordMode({
7888
+ originalFunctionCall: () => originalHandleRequest.call(this, req, res, parsedUrl),
7889
+ recordModeHandler: ({ isPreAppStart }) => {
7890
+ const inputValue = {
7891
+ method,
7892
+ url,
7893
+ target: url,
7894
+ headers: self._normalizeHeaders(req.headers || {})
7895
+ };
7896
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalHandleRequest.call(this, req, res, parsedUrl), {
7897
+ name: url,
7898
+ kind: SpanKind.SERVER,
7899
+ packageName: "nextjs",
7900
+ packageType: PackageType.HTTP,
7901
+ submodule: method,
7902
+ instrumentationName: self.INSTRUMENTATION_NAME,
7903
+ inputValue,
7904
+ inputSchemaMerges: { headers: { matchImportance: 0 } },
7905
+ isPreAppStart
7906
+ }, (spanInfo) => {
7907
+ return self._handleNextjsRequestInSpan({
7908
+ req,
7909
+ res,
7910
+ parsedUrl,
7911
+ originalHandleRequest,
7912
+ spanInfo,
7913
+ inputValue,
7914
+ thisContext: this
7915
+ });
7916
+ });
7917
+ },
7918
+ spanKind: SpanKind.SERVER
7919
+ });
7920
+ } else return originalHandleRequest.call(this, req, res, parsedUrl);
7921
+ };
7922
+ };
7923
+ }
7924
+ async _handleNextjsRequestInSpan({ req, res, parsedUrl, originalHandleRequest, spanInfo, inputValue, thisContext }) {
7925
+ const self = this;
7926
+ context.bind(spanInfo.context, req);
7927
+ context.bind(spanInfo.context, res);
7928
+ let completeInputValue = inputValue;
7929
+ this._captureRequestBody(req, spanInfo, inputValue, (updatedInputValue) => {
7930
+ completeInputValue = updatedInputValue;
7931
+ });
7932
+ let capturedStatusCode;
7933
+ let capturedStatusMessage;
7934
+ let capturedHeaders = {};
7935
+ const responseChunks = [];
7936
+ const actualRes = res.originalResponse || res;
7937
+ const originalWriteHead = actualRes.writeHead?.bind(actualRes);
7938
+ if (originalWriteHead) actualRes.writeHead = function(statusCode, statusMessage, headers) {
7939
+ capturedStatusCode = statusCode;
7940
+ if (typeof statusMessage === "object") capturedHeaders = self._normalizeHeaders(statusMessage);
7941
+ else {
7942
+ capturedStatusMessage = statusMessage;
7943
+ if (headers) capturedHeaders = self._normalizeHeaders(headers);
7944
+ }
7945
+ return originalWriteHead.call(this, statusCode, statusMessage, headers);
7946
+ };
7947
+ const originalSetHeader = actualRes.setHeader?.bind(actualRes);
7948
+ if (originalSetHeader) actualRes.setHeader = function(name, value) {
7949
+ capturedHeaders[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
7950
+ return originalSetHeader.call(this, name, value);
7951
+ };
7952
+ const originalWrite = actualRes.write?.bind(actualRes);
7953
+ if (originalWrite) actualRes.write = function(chunk, encoding, callback) {
7954
+ if (chunk) responseChunks.push(chunk);
7955
+ return originalWrite.call(this, chunk, encoding, callback);
7956
+ };
7957
+ const originalEnd = actualRes.end?.bind(actualRes);
7958
+ if (originalEnd) actualRes.end = function(chunk, encoding, callback) {
7959
+ if (chunk) responseChunks.push(chunk);
7960
+ return originalEnd.call(this, chunk, encoding, callback);
7961
+ };
7962
+ try {
7963
+ await originalHandleRequest.call(thisContext, req, res, parsedUrl);
7964
+ if (!capturedStatusCode) {
7965
+ capturedStatusCode = res.statusCode;
7966
+ capturedStatusMessage = res.statusMessage;
7967
+ }
7968
+ if (Object.keys(capturedHeaders).length === 0 && res.getHeaders) {
7969
+ const rawHeaders = res.getHeaders();
7970
+ capturedHeaders = self._normalizeHeaders(rawHeaders);
7971
+ }
7972
+ logger.debug(`[NextjsInstrumentation] Next.js request completed: ${capturedStatusCode} (${SpanUtils.getTraceInfo()})`);
7973
+ const outputValue = {
7974
+ statusCode: capturedStatusCode,
7975
+ statusMessage: capturedStatusMessage,
7976
+ headers: capturedHeaders
7977
+ };
7978
+ if (responseChunks.length > 0) try {
7979
+ const responseBuffer = combineChunks(responseChunks);
7980
+ const contentEncoding = outputValue.headers?.["content-encoding"];
7981
+ outputValue.body = await httpBodyEncoder({
7982
+ bodyBuffer: responseBuffer,
7983
+ contentEncoding
7984
+ });
7985
+ outputValue.bodySize = responseBuffer.length;
7986
+ } catch (error) {
7987
+ logger.error(`[NextjsInstrumentation] Error processing response body:`, error);
7988
+ outputValue.bodyProcessingError = error instanceof Error ? error.message : String(error);
7989
+ }
7990
+ SpanUtils.addSpanAttributes(spanInfo.span, {
7991
+ inputValue: completeInputValue,
7992
+ outputValue,
7993
+ outputSchemaMerges: {
7994
+ body: {
7995
+ encoding: EncodingType.BASE64,
7996
+ decodedType: getDecodedType(outputValue.headers?.["content-type"] || "")
7997
+ },
7998
+ headers: { matchImportance: 0 }
7999
+ },
8000
+ metadata: { ENV_VARS: EnvVarTracker.getEnvVars(spanInfo.traceId) }
8001
+ });
8002
+ EnvVarTracker.clearEnvVars(spanInfo.traceId);
8003
+ const status = (capturedStatusCode || 200) >= 400 ? {
8004
+ code: SpanStatusCode.ERROR,
8005
+ message: `HTTP ${capturedStatusCode}`
8006
+ } : { code: SpanStatusCode.OK };
8007
+ SpanUtils.setStatus(spanInfo.span, status);
8008
+ SpanUtils.endSpan(spanInfo.span);
8009
+ if (self.mode === TuskDriftMode.REPLAY) try {
8010
+ const now = OriginalGlobalUtils.getOriginalDate();
8011
+ const replayTraceId = SpanUtils.getCurrentReplayTraceId() || spanInfo.traceId;
8012
+ const { schema: inputSchema, decodedValueHash: inputValueHash } = JsonSchemaHelper.generateSchemaAndHash(completeInputValue, {
8013
+ body: {
8014
+ encoding: EncodingType.BASE64,
8015
+ decodedType: getDecodedType(completeInputValue.headers && completeInputValue.headers["content-type"] || "")
8016
+ },
8017
+ headers: { matchImportance: 0 }
8018
+ });
8019
+ const { schema: outputSchema, decodedValueHash: outputValueHash } = JsonSchemaHelper.generateSchemaAndHash(outputValue, {
8020
+ body: {
8021
+ encoding: EncodingType.BASE64,
8022
+ decodedType: getDecodedType(outputValue.headers["content-type"] || "")
8023
+ },
8024
+ headers: { matchImportance: 0 }
8025
+ });
8026
+ const cleanSpan = {
8027
+ traceId: replayTraceId,
8028
+ spanId: spanInfo.spanId,
8029
+ parentSpanId: "",
8030
+ name: completeInputValue.url,
8031
+ packageName: "nextjs",
8032
+ instrumentationName: self.INSTRUMENTATION_NAME,
8033
+ submoduleName: completeInputValue.method,
8034
+ inputValue: completeInputValue,
8035
+ outputValue,
8036
+ inputSchema,
8037
+ outputSchema,
8038
+ inputSchemaHash: JsonSchemaHelper.generateDeterministicHash(inputSchema),
8039
+ outputSchemaHash: JsonSchemaHelper.generateDeterministicHash(outputSchema),
8040
+ inputValueHash,
8041
+ outputValueHash,
8042
+ kind: SpanKind.SERVER,
8043
+ packageType: PackageType.HTTP,
8044
+ status: {
8045
+ code: (capturedStatusCode || 200) >= 400 ? StatusCode.ERROR : StatusCode.OK,
8046
+ message: (capturedStatusCode || 200) >= 400 ? `HTTP ${capturedStatusCode}` : ""
8047
+ },
8048
+ timestamp: {
8049
+ seconds: Math.floor(now.getTime() / 1e3),
8050
+ nanos: now.getTime() % 1e3 * 1e6
8051
+ },
8052
+ duration: {
8053
+ seconds: 0,
8054
+ nanos: 0
8055
+ },
8056
+ isRootSpan: true,
8057
+ isPreAppStart: false,
8058
+ metadata: void 0
8059
+ };
8060
+ await self.tuskDrift.sendInboundSpanForReplay(cleanSpan);
8061
+ } catch (e) {
8062
+ logger.error("[NextjsInstrumentation] Failed to build/send inbound replay span:", e);
8063
+ }
8064
+ } catch (error) {
8065
+ logger.error(`[NextjsInstrumentation] Error in Next.js request: ${error instanceof Error ? error.message : String(error)}`);
8066
+ SpanUtils.endSpan(spanInfo.span, {
8067
+ code: SpanStatusCode.ERROR,
8068
+ message: error instanceof Error ? error.message : String(error)
8069
+ });
8070
+ throw error;
8071
+ }
8072
+ }
8073
+ /**
8074
+ * Captures the request body from an IncomingMessage stream by patching req.read() and listening for data events.
8075
+ * Similar to HTTP instrumentation's request body capture, but adapted for Next.js.
8076
+ */
8077
+ _captureRequestBody(req, spanInfo, inputValue, onBodyCaptured) {
8078
+ const actualReq = req.originalRequest || req._req || req;
8079
+ const requestBodyChunks = [];
8080
+ let streamConsumptionMode = "NOT_CONSUMING";
8081
+ const originalRead = actualReq.read?.bind(actualReq);
8082
+ if (originalRead) actualReq.read = function read(size) {
8083
+ const chunk = originalRead(size);
8084
+ if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
8085
+ streamConsumptionMode = "READ";
8086
+ requestBodyChunks.push(Buffer.from(chunk));
8087
+ }
8088
+ return chunk;
8089
+ };
8090
+ if (typeof actualReq.once !== "function" || typeof actualReq.addListener !== "function") {
8091
+ logger.debug(`[NextjsInstrumentation] Request object doesn't have event emitter methods, skipping body capture`);
8092
+ return;
8093
+ }
8094
+ actualReq.once("resume", () => {
8095
+ actualReq.addListener("data", (chunk) => {
8096
+ if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
8097
+ streamConsumptionMode = "PIPE";
8098
+ requestBodyChunks.push(Buffer.from(chunk));
8099
+ }
8100
+ });
8101
+ });
8102
+ actualReq.addListener("end", async (chunk) => {
8103
+ if (chunk) requestBodyChunks.push(Buffer.from(chunk));
8104
+ if (requestBodyChunks.length > 0) try {
8105
+ const bodyBuffer = Buffer.concat(requestBodyChunks);
8106
+ const encodedBody = await httpBodyEncoder({
8107
+ bodyBuffer,
8108
+ contentEncoding: actualReq.headers["content-encoding"]
8109
+ });
8110
+ const updatedInputValue = {
8111
+ ...inputValue,
8112
+ body: encodedBody,
8113
+ bodySize: bodyBuffer.length
8114
+ };
8115
+ if (onBodyCaptured) onBodyCaptured(updatedInputValue);
8116
+ SpanUtils.addSpanAttributes(spanInfo.span, {
8117
+ inputValue: updatedInputValue,
8118
+ inputSchemaMerges: {
8119
+ body: {
8120
+ encoding: EncodingType.BASE64,
8121
+ decodedType: getDecodedType(actualReq.headers["content-type"] || "")
8122
+ },
8123
+ headers: { matchImportance: 0 }
8124
+ }
8125
+ });
8126
+ logger.debug(`[NextjsInstrumentation] Captured request body for ${actualReq.method} ${actualReq.url}: ${bodyBuffer.length} bytes`);
8127
+ } catch (error) {
8128
+ logger.error(`[NextjsInstrumentation] Error processing request body:`, error);
8129
+ }
8130
+ });
8131
+ }
8132
+ _normalizeHeaders(headers) {
8133
+ const normalized = {};
8134
+ for (const [key, value] of Object.entries(headers)) if (value !== void 0) normalized[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
8135
+ return normalized;
8136
+ }
8137
+ _wrap(target, propertyName, wrapper) {
8138
+ wrap(target, propertyName, wrapper);
8139
+ }
8140
+ };
8141
+
7524
8142
  //#endregion
7525
8143
  //#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
7526
8144
  var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
@@ -9957,7 +10575,18 @@ var TdSpanExporter = class {
9957
10575
  */
9958
10576
  export(spans, resultCallback) {
9959
10577
  logger.debug(`TdSpanExporter.export() called with ${spans.length} span(s)`);
9960
- const cleanSpans = spans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
10578
+ const filteredSpans = spans.filter((span) => {
10579
+ if (span.instrumentationLibrary?.name === "next.js") return false;
10580
+ return true;
10581
+ });
10582
+ logger.debug(`After filtering: ${filteredSpans.length} span(s) remaining`);
10583
+ let cleanSpans;
10584
+ try {
10585
+ cleanSpans = filteredSpans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
10586
+ } catch (error) {
10587
+ logger.error("Error transforming spans to CleanSpanData", error);
10588
+ throw error;
10589
+ }
9961
10590
  if (this.adapters.length === 0) {
9962
10591
  logger.debug("No adapters configured");
9963
10592
  resultCallback({ code: import_src.ExportResultCode.SUCCESS });
@@ -10477,6 +11106,10 @@ var TuskDriftCore = class TuskDriftCore {
10477
11106
  enabled: true,
10478
11107
  mode: this.mode
10479
11108
  });
11109
+ new NextjsInstrumentation({
11110
+ enabled: true,
11111
+ mode: this.mode
11112
+ });
10480
11113
  }
10481
11114
  initializeTracing({ baseDirectory }) {
10482
11115
  const serviceName = this.config.service?.name || "unknown";
@@ -10506,6 +11139,10 @@ var TuskDriftCore = class TuskDriftCore {
10506
11139
  return `sdk-${OriginalGlobalUtils.getOriginalDate().getTime()}-${Math.random().toString(36).substr(2, 9)}`;
10507
11140
  }
10508
11141
  initialize(initParams) {
11142
+ initializeGlobalLogger({
11143
+ logLevel: initParams.logLevel || "silent",
11144
+ prefix: "TuskDrift"
11145
+ });
10509
11146
  this.samplingRate = this.config.recording?.sampling_rate ?? 1;
10510
11147
  this.initParams = initParams;
10511
11148
  if (!this.initParams.env) {
@@ -10513,10 +11150,6 @@ var TuskDriftCore = class TuskDriftCore {
10513
11150
  logger.warn(`Environment not provided in initialization parameters. Using '${nodeEnv}' as the environment.`);
10514
11151
  this.initParams.env = nodeEnv;
10515
11152
  }
10516
- initializeGlobalLogger({
10517
- logLevel: initParams.logLevel || "silent",
10518
- prefix: "TuskDrift"
10519
- });
10520
11153
  if (this.initialized) {
10521
11154
  logger.debug("Already initialized, skipping...");
10522
11155
  return;
@@ -10712,5 +11345,5 @@ var TuskDriftSDK = class {
10712
11345
  const TuskDrift = new TuskDriftSDK();
10713
11346
 
10714
11347
  //#endregion
10715
- export { TuskDrift };
11348
+ export { TuskDrift, withTuskDrift };
10716
11349
  //# sourceMappingURL=index.js.map