@use-tusk/drift-node-sdk 0.1.12 → 0.1.13

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.cjs CHANGED
@@ -276,15 +276,6 @@ var TdInstrumentationAbstract = class {
276
276
  }
277
277
  };
278
278
 
279
- //#endregion
280
- //#region package.json
281
- var version = "0.1.12";
282
-
283
- //#endregion
284
- //#region src/version.ts
285
- const SDK_VERSION = version;
286
- const MIN_CLI_VERSION = "0.1.0";
287
-
288
279
  //#endregion
289
280
  //#region src/core/utils/dataNormalizationUtils.ts
290
281
  /**
@@ -564,51 +555,40 @@ const logger = {
564
555
 
565
556
  //#endregion
566
557
  //#region src/core/analytics/analyticsUtils.ts
567
- function sendAnalyticsPayload(payload) {
568
- try {
569
- if (TuskDriftCore.getInstance().getConfig().recording?.enable_analytics) {}
570
- } catch (e) {
571
- logger.error("Error sending analytics event:", e);
572
- }
573
- }
574
- function sendTdAnalytics(eventName, properties = {}) {
575
- const serviceId = TuskDriftCore.getInstance().getConfig().service?.id || "unknown-service";
576
- const payload = {
577
- distinctId: `tusk-drift:${serviceId}`,
578
- event: `${eventName}`,
579
- properties: {
580
- serviceId,
581
- tdMode: TuskDriftCore.getInstance().getMode(),
582
- sdkVersion: SDK_VERSION,
583
- ...properties
584
- }
585
- };
586
- sendAnalyticsPayload(payload);
587
- }
588
558
  /**
589
- * NOTE: analytics has not been implemented yet, so this function does nothing
559
+ * Send version mismatch alert to CLI (only in REPLAY mode)
590
560
  */
591
561
  function sendVersionMismatchAlert({ moduleName, foundVersion, supportedVersions }) {
562
+ logger.info("Sending version mismatch alert", {
563
+ moduleName,
564
+ foundVersion,
565
+ supportedVersions
566
+ });
592
567
  try {
593
- sendTdAnalytics("version_mismatch", {
568
+ if (TuskDriftCore.getInstance().getMode() !== TuskDriftMode.REPLAY) return;
569
+ const protobufComm = TuskDriftCore.getInstance().getProtobufCommunicator();
570
+ if (protobufComm) protobufComm.sendInstrumentationVersionMismatchAlert({
594
571
  moduleName,
595
- foundVersion: foundVersion || "unknown",
596
- supportedVersions: supportedVersions.join(", ")
572
+ requestedVersion: foundVersion,
573
+ supportedVersions
597
574
  });
598
575
  } catch (e) {
599
576
  logger.error("Error sending version mismatch alert:", e);
600
577
  }
601
578
  }
602
579
  /**
603
- * NOTE: analytics has not been implemented yet, so this function does nothing
580
+ * Send unpatched dependency alert to CLI
604
581
  */
605
- function sendUnpatchedDependencyAlert({ method, spanId, traceId, stackTrace }) {
582
+ function sendUnpatchedDependencyAlert({ traceTestServerSpanId, stackTrace }) {
583
+ logger.info("Sending unpatched dependency alert", {
584
+ traceTestServerSpanId,
585
+ stackTrace
586
+ });
606
587
  try {
607
- sendTdAnalytics("unpatched_dependency", {
608
- method,
609
- spanId,
610
- traceId,
611
- stackTrace: stackTrace ? stackTrace.split("\n").slice(0, 10).join("\n") : void 0
588
+ const protobufComm = TuskDriftCore.getInstance().getProtobufCommunicator();
589
+ if (protobufComm && stackTrace) protobufComm.sendUnpatchedDependencyAlert({
590
+ stackTrace,
591
+ traceTestServerSpanId
612
592
  });
613
593
  } catch (e) {
614
594
  logger.error("Error sending unpatched dependency alert:", e);
@@ -2039,9 +2019,12 @@ var HttpReplayHooks = class {
2039
2019
  const traceIdHeader = req.headers["x-td-trace-id"] || req.headers["X-TD-TRACE-ID"];
2040
2020
  return traceIdHeader ? String(traceIdHeader) : null;
2041
2021
  }
2042
- extractEnvVarsFromHeaders(req) {
2043
- const envVarsHeader = req.headers["x-td-env-vars"] || req.headers["X-TD-ENV-VARS"];
2044
- return envVarsHeader ? JSON.parse(String(envVarsHeader)) : void 0;
2022
+ /**
2023
+ * Check if we should fetch env vars from CLI
2024
+ */
2025
+ extractShouldFetchEnvVars(req) {
2026
+ const fetchHeader = req.headers["x-td-fetch-env-vars"] || req.headers["X-TD-FETCH-ENV-VARS"];
2027
+ return fetchHeader === "true" || fetchHeader === true;
2045
2028
  }
2046
2029
  /**
2047
2030
  * Handle outbound HTTP requests in replay mode
@@ -2654,8 +2637,13 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2654
2637
  return originalHandler.call(this);
2655
2638
  }
2656
2639
  logger.debug(`[HttpInstrumentation] Setting replay trace id`, replayTraceId);
2657
- const envVars = this.replayHooks.extractEnvVarsFromHeaders(req);
2658
- if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
2640
+ if (this.replayHooks.extractShouldFetchEnvVars(req)) try {
2641
+ const envVars = this.tuskDrift.requestEnvVarsSync(replayTraceId);
2642
+ EnvVarTracker.setEnvVars(replayTraceId, envVars);
2643
+ logger.debug(`[HttpInstrumentation] Fetched env vars from CLI for trace ${replayTraceId}`);
2644
+ } catch (error) {
2645
+ logger.error(`[HttpInstrumentation] Failed to fetch env vars from CLI:`, error);
2646
+ }
2659
2647
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
2660
2648
  if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
2661
2649
  return __opentelemetry_api.context.with(ctxWithReplayTraceId, () => {
@@ -6189,12 +6177,13 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
6189
6177
  traceId: currentSpanInfo.traceId,
6190
6178
  socketContext
6191
6179
  });
6192
- logger.warn(`[TcpInstrumentation] Full stack trace:\n${(/* @__PURE__ */ new Error()).stack}`);
6180
+ const stackTrace = (/* @__PURE__ */ new Error()).stack || "";
6181
+ const traceTestServerSpanId = SpanUtils.getCurrentReplayTraceId();
6182
+ logger.warn(`[TcpInstrumentation] Full stack trace:\n${stackTrace}`, { traceTestServerSpanId });
6193
6183
  Error.stackTraceLimit = 10;
6194
6184
  sendUnpatchedDependencyAlert({
6195
- method: methodName,
6196
- spanId: currentSpanInfo.spanId,
6197
- traceId: currentSpanInfo.traceId
6185
+ traceTestServerSpanId: traceTestServerSpanId || "",
6186
+ stackTrace
6198
6187
  });
6199
6188
  if (this.loggedSpans.size > 1e3) {
6200
6189
  logger.debug(`[TcpInstrumentation] Cleaning up logged spans cache (${this.loggedSpans.size} entries)`);
@@ -9633,8 +9622,13 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
9633
9622
  return originalHandleRequest.call(this, req, res, parsedUrl);
9634
9623
  }
9635
9624
  logger.debug(`[NextjsInstrumentation] Setting replay trace id`, replayTraceId);
9636
- const envVars = self.replayHooks.extractEnvVarsFromHeaders(req);
9637
- if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
9625
+ if (self.replayHooks.extractShouldFetchEnvVars(req)) try {
9626
+ const envVars = self.tuskDrift.requestEnvVarsSync(replayTraceId);
9627
+ EnvVarTracker.setEnvVars(replayTraceId, envVars);
9628
+ logger.debug(`[NextjsInstrumentation] Fetched env vars from CLI for trace ${replayTraceId}`);
9629
+ } catch (error) {
9630
+ logger.error(`[NextjsInstrumentation] Failed to fetch env vars from CLI:`, error);
9631
+ }
9638
9632
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
9639
9633
  if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
9640
9634
  return __opentelemetry_api.context.with(ctxWithReplayTraceId, () => {
@@ -12673,9 +12667,18 @@ var TdSpanExporter = class {
12673
12667
  }
12674
12668
  };
12675
12669
 
12670
+ //#endregion
12671
+ //#region package.json
12672
+ var version = "0.1.13";
12673
+
12674
+ //#endregion
12675
+ //#region src/version.ts
12676
+ const SDK_VERSION = version;
12677
+ const MIN_CLI_VERSION = "0.1.0";
12678
+
12676
12679
  //#endregion
12677
12680
  //#region src/core/ProtobufCommunicator.ts
12678
- var ProtobufCommunicator = class {
12681
+ var ProtobufCommunicator = class ProtobufCommunicator {
12679
12682
  constructor() {
12680
12683
  this.client = null;
12681
12684
  this.pendingRequests = /* @__PURE__ */ new Map();
@@ -12773,43 +12776,18 @@ var ProtobufCommunicator = class {
12773
12776
  });
12774
12777
  }
12775
12778
  /**
12776
- * This function uses a separate Node.js child process to communicate with the CLI over a socket.
12777
- * The child process creates its own connection and event loop, allowing proper async socket handling.
12778
- * The parent process blocks synchronously waiting for the child to complete.
12779
- *
12780
- * Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
12781
- * for instrumentations that require fetching mocks synchronously.
12779
+ * Generic synchronous request handler that spawns a child process.
12780
+ * @param sdkMessage The SDK message to send
12781
+ * @param filePrefix Prefix for temporary files (e.g., 'envvar', 'mock')
12782
+ * @param responseHandler Function to extract and return the desired response
12782
12783
  */
12783
- requestMockSync(mockRequest) {
12784
- const requestId = this.generateRequestId();
12785
- const cleanSpan = mockRequest.outboundSpan ? this.cleanSpan(mockRequest.outboundSpan) : void 0;
12786
- if (cleanSpan?.inputValue) cleanSpan.inputValue = objectToProtobufStruct(cleanSpan.inputValue);
12787
- if (cleanSpan?.inputSchema) cleanSpan.inputSchema = objectToProtobufStruct(cleanSpan.inputSchema);
12788
- if (cleanSpan?.kind) cleanSpan.kind = mapOtToPb(cleanSpan.kind);
12789
- const protoMockRequest = __use_tusk_drift_schemas_core_communication.GetMockRequest.create({
12790
- ...mockRequest,
12791
- requestId,
12792
- tags: {},
12793
- outboundSpan: cleanSpan,
12794
- stackTrace: cleanSpan?.stackTrace
12795
- });
12796
- const sdkMessage = __use_tusk_drift_schemas_core_communication.SDKMessage.create({
12797
- type: __use_tusk_drift_schemas_core_communication.MessageType.MOCK_REQUEST,
12798
- requestId,
12799
- payload: {
12800
- oneofKind: "getMockRequest",
12801
- getMockRequest: protoMockRequest
12802
- }
12803
- });
12804
- logger.debug("Sending protobuf request to CLI (sync)", {
12805
- outboundSpan: mockRequest.outboundSpan,
12806
- testId: mockRequest.testId
12807
- });
12784
+ executeSyncRequest(sdkMessage, filePrefix, responseHandler) {
12785
+ const requestId = sdkMessage.requestId;
12808
12786
  const messageBytes = __use_tusk_drift_schemas_core_communication.SDKMessage.toBinary(sdkMessage);
12809
12787
  const tempDir = os.default.tmpdir();
12810
- const requestFile = path.default.join(tempDir, `tusk-sync-request-${requestId}.bin`);
12811
- const responseFile = path.default.join(tempDir, `tusk-sync-response-${requestId}.bin`);
12812
- const scriptFile = path.default.join(tempDir, `tusk-sync-script-${requestId}.js`);
12788
+ const requestFile = path.default.join(tempDir, `tusk-sync-${filePrefix}-request-${requestId}.bin`);
12789
+ const responseFile = path.default.join(tempDir, `tusk-sync-${filePrefix}-response-${requestId}.bin`);
12790
+ const scriptFile = path.default.join(tempDir, `tusk-sync-${filePrefix}-script-${requestId}.js`);
12813
12791
  try {
12814
12792
  const lengthBuffer = Buffer.allocUnsafe(4);
12815
12793
  lengthBuffer.writeUInt32BE(messageBytes.length, 0);
@@ -12832,95 +12810,7 @@ var ProtobufCommunicator = class {
12832
12810
  type: "unix",
12833
12811
  path: path.default.join(os.default.tmpdir(), "tusk-connect.sock")
12834
12812
  };
12835
- fs.default.writeFileSync(scriptFile, `
12836
- const net = require('net');
12837
- const fs = require('fs');
12838
-
12839
- const requestFile = process.argv[2];
12840
- const responseFile = process.argv[3];
12841
- const config = JSON.parse(process.argv[4]);
12842
-
12843
- let responseReceived = false;
12844
- let timeoutId;
12845
-
12846
- function cleanup(exitCode) {
12847
- if (timeoutId) clearTimeout(timeoutId);
12848
- process.exit(exitCode);
12849
- }
12850
-
12851
- try {
12852
- // Read the request data
12853
- const requestData = fs.readFileSync(requestFile);
12854
-
12855
- // Create connection based on config
12856
- const client = config.type === 'unix'
12857
- ? net.createConnection({ path: config.path })
12858
- : net.createConnection({ host: config.host, port: config.port });
12859
-
12860
- const incomingChunks = [];
12861
- let incomingBuffer = Buffer.alloc(0);
12862
-
12863
- // Set timeout
12864
- timeoutId = setTimeout(() => {
12865
- if (!responseReceived) {
12866
- console.error('Timeout waiting for response');
12867
- client.destroy();
12868
- cleanup(1);
12869
- }
12870
- }, 10000);
12871
-
12872
- client.on('connect', () => {
12873
- // Send the request
12874
- client.write(requestData);
12875
- });
12876
-
12877
- client.on('data', (data) => {
12878
- incomingBuffer = Buffer.concat([incomingBuffer, data]);
12879
-
12880
- // Try to parse complete message (4 byte length prefix + message)
12881
- while (incomingBuffer.length >= 4) {
12882
- const messageLength = incomingBuffer.readUInt32BE(0);
12883
-
12884
- if (incomingBuffer.length < 4 + messageLength) {
12885
- // Incomplete message, wait for more data
12886
- break;
12887
- }
12888
-
12889
- // We have a complete message
12890
- const messageData = incomingBuffer.slice(4, 4 + messageLength);
12891
- incomingBuffer = incomingBuffer.slice(4 + messageLength);
12892
-
12893
- // Write the complete response (including length prefix)
12894
- const lengthPrefix = Buffer.allocUnsafe(4);
12895
- lengthPrefix.writeUInt32BE(messageLength, 0);
12896
- fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
12897
-
12898
- responseReceived = true;
12899
- client.destroy();
12900
- cleanup(0);
12901
- break;
12902
- }
12903
- });
12904
-
12905
- client.on('error', (err) => {
12906
- if (!responseReceived) {
12907
- console.error('Connection error:', err.message);
12908
- cleanup(1);
12909
- }
12910
- });
12911
-
12912
- client.on('close', () => {
12913
- if (!responseReceived) {
12914
- console.error('Connection closed without response');
12915
- cleanup(1);
12916
- }
12917
- });
12918
-
12919
- } catch (err) {
12920
- console.error('Script error:', err.message);
12921
- cleanup(1);
12922
- }
12923
- `);
12813
+ fs.default.writeFileSync(scriptFile, ProtobufCommunicator.SYNC_CHILD_SCRIPT);
12924
12814
  try {
12925
12815
  (0, child_process.execSync)(`node "${scriptFile}" "${requestFile}" "${responseFile}" '${JSON.stringify(connectionConfig)}'`, {
12926
12816
  timeout: 12e3,
@@ -12932,27 +12822,13 @@ try {
12932
12822
  if (responseBuffer.length < 4 + responseLength) throw new Error("Invalid response: incomplete message");
12933
12823
  const responseData = responseBuffer.slice(4, 4 + responseLength);
12934
12824
  const cliMessage = __use_tusk_drift_schemas_core_communication.CLIMessage.fromBinary(responseData);
12935
- if (cliMessage.payload.oneofKind !== "getMockResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12936
- const mockResponse = cliMessage.payload.getMockResponse;
12937
- if (!mockResponse) throw new Error("No mock response received");
12938
- if (mockResponse.found) try {
12939
- return {
12940
- found: true,
12941
- response: this.extractResponseData(mockResponse)
12942
- };
12943
- } catch (error) {
12944
- throw new Error(`Failed to extract response data: ${error}`);
12945
- }
12946
- else return {
12947
- found: false,
12948
- error: mockResponse.error || "Mock not found"
12949
- };
12825
+ return responseHandler(cliMessage);
12950
12826
  } catch (error) {
12951
- logger.error("[ProtobufCommunicator] error in sync request child process:", error);
12827
+ logger.error(`[ProtobufCommunicator] error in sync ${filePrefix} request child process:`, error);
12952
12828
  throw error;
12953
12829
  }
12954
12830
  } catch (error) {
12955
- throw new Error(`Sync request failed: ${error.message}`);
12831
+ throw new Error(`Sync ${filePrefix} request failed: ${error.message}`);
12956
12832
  } finally {
12957
12833
  try {
12958
12834
  if (fs.default.existsSync(requestFile)) fs.default.unlinkSync(requestFile);
@@ -12971,6 +12847,86 @@ try {
12971
12847
  }
12972
12848
  }
12973
12849
  }
12850
+ /**
12851
+ * Request environment variables from CLI synchronously using a child process.
12852
+ * This blocks the main thread, so it should be used carefully.
12853
+ * Similar to requestMockSync but for environment variables.
12854
+ */
12855
+ requestEnvVarsSync(traceTestServerSpanId) {
12856
+ const requestId = this.generateRequestId();
12857
+ const envVarRequest = __use_tusk_drift_schemas_core_communication.EnvVarRequest.create({ traceTestServerSpanId });
12858
+ const sdkMessage = __use_tusk_drift_schemas_core_communication.SDKMessage.create({
12859
+ type: __use_tusk_drift_schemas_core_communication.MessageType.ENV_VAR_REQUEST,
12860
+ requestId,
12861
+ payload: {
12862
+ oneofKind: "envVarRequest",
12863
+ envVarRequest
12864
+ }
12865
+ });
12866
+ logger.debug(`[ProtobufCommunicator] Requesting env vars (sync) for trace: ${traceTestServerSpanId}`);
12867
+ return this.executeSyncRequest(sdkMessage, "envvar", (cliMessage) => {
12868
+ if (cliMessage.payload.oneofKind !== "envVarResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12869
+ const envVarResponse = cliMessage.payload.envVarResponse;
12870
+ if (!envVarResponse) throw new Error("No env var response received");
12871
+ const envVars = {};
12872
+ if (envVarResponse.envVars) Object.entries(envVarResponse.envVars).forEach(([key, value]) => {
12873
+ envVars[key] = value;
12874
+ });
12875
+ logger.debug(`[ProtobufCommunicator] Received env vars (sync), count: ${Object.keys(envVars).length}`);
12876
+ return envVars;
12877
+ });
12878
+ }
12879
+ /**
12880
+ * This function uses a separate Node.js child process to communicate with the CLI over a socket.
12881
+ * The child process creates its own connection and event loop, allowing proper async socket handling.
12882
+ * The parent process blocks synchronously waiting for the child to complete.
12883
+ *
12884
+ * Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
12885
+ * for instrumentations that require fetching mocks synchronously.
12886
+ */
12887
+ requestMockSync(mockRequest) {
12888
+ const requestId = this.generateRequestId();
12889
+ const cleanSpan = mockRequest.outboundSpan ? this.cleanSpan(mockRequest.outboundSpan) : void 0;
12890
+ if (cleanSpan?.inputValue) cleanSpan.inputValue = objectToProtobufStruct(cleanSpan.inputValue);
12891
+ if (cleanSpan?.inputSchema) cleanSpan.inputSchema = objectToProtobufStruct(cleanSpan.inputSchema);
12892
+ if (cleanSpan?.kind) cleanSpan.kind = mapOtToPb(cleanSpan.kind);
12893
+ const protoMockRequest = __use_tusk_drift_schemas_core_communication.GetMockRequest.create({
12894
+ ...mockRequest,
12895
+ requestId,
12896
+ tags: {},
12897
+ outboundSpan: cleanSpan,
12898
+ stackTrace: cleanSpan?.stackTrace
12899
+ });
12900
+ const sdkMessage = __use_tusk_drift_schemas_core_communication.SDKMessage.create({
12901
+ type: __use_tusk_drift_schemas_core_communication.MessageType.MOCK_REQUEST,
12902
+ requestId,
12903
+ payload: {
12904
+ oneofKind: "getMockRequest",
12905
+ getMockRequest: protoMockRequest
12906
+ }
12907
+ });
12908
+ logger.debug("Sending protobuf request to CLI (sync)", {
12909
+ outboundSpan: mockRequest.outboundSpan,
12910
+ testId: mockRequest.testId
12911
+ });
12912
+ return this.executeSyncRequest(sdkMessage, "mock", (cliMessage) => {
12913
+ if (cliMessage.payload.oneofKind !== "getMockResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12914
+ const mockResponse = cliMessage.payload.getMockResponse;
12915
+ if (!mockResponse) throw new Error("No mock response received");
12916
+ if (mockResponse.found) try {
12917
+ return {
12918
+ found: true,
12919
+ response: this.extractResponseData(mockResponse)
12920
+ };
12921
+ } catch (error) {
12922
+ throw new Error(`Failed to extract response data: ${error}`);
12923
+ }
12924
+ else return {
12925
+ found: false,
12926
+ error: mockResponse.error || "Mock not found"
12927
+ };
12928
+ });
12929
+ }
12974
12930
  async sendProtobufMessage(message) {
12975
12931
  if (!this.client || !this.protobufContext) throw new Error("Not connected to CLI");
12976
12932
  const messageBytes = __use_tusk_drift_schemas_core_communication.SDKMessage.toBinary(message);
@@ -13023,6 +12979,58 @@ try {
13023
12979
  });
13024
12980
  await this.sendProtobufMessage(sdkMessage);
13025
12981
  }
12982
+ /**
12983
+ * Send an alert to the CLI (fire-and-forget, no response expected)
12984
+ */
12985
+ async sendAlert(alert) {
12986
+ if (!this.client || !this.protobufContext) {
12987
+ logger.debug("[ProtobufCommunicator] Not connected to CLI, skipping alert");
12988
+ return;
12989
+ }
12990
+ const sdkMessage = __use_tusk_drift_schemas_core_communication.SDKMessage.create({
12991
+ type: __use_tusk_drift_schemas_core_communication.MessageType.ALERT,
12992
+ requestId: this.generateRequestId(),
12993
+ payload: {
12994
+ oneofKind: "sendAlertRequest",
12995
+ sendAlertRequest: alert
12996
+ }
12997
+ });
12998
+ try {
12999
+ await this.sendProtobufMessage(sdkMessage);
13000
+ logger.debug("[ProtobufCommunicator] Alert sent to CLI");
13001
+ } catch (error) {
13002
+ logger.debug("[ProtobufCommunicator] Failed to send alert to CLI:", error);
13003
+ }
13004
+ }
13005
+ /**
13006
+ * Send instrumentation version mismatch alert to CLI
13007
+ */
13008
+ async sendInstrumentationVersionMismatchAlert(params) {
13009
+ const alert = __use_tusk_drift_schemas_core_communication.SendAlertRequest.create({ alert: {
13010
+ oneofKind: "versionMismatch",
13011
+ versionMismatch: __use_tusk_drift_schemas_core_communication.InstrumentationVersionMismatchAlert.create({
13012
+ moduleName: params.moduleName,
13013
+ requestedVersion: params.requestedVersion || "",
13014
+ supportedVersions: params.supportedVersions,
13015
+ sdkVersion: SDK_VERSION
13016
+ })
13017
+ } });
13018
+ await this.sendAlert(alert);
13019
+ }
13020
+ /**
13021
+ * Send unpatched dependency alert to CLI
13022
+ */
13023
+ async sendUnpatchedDependencyAlert(params) {
13024
+ const alert = __use_tusk_drift_schemas_core_communication.SendAlertRequest.create({ alert: {
13025
+ oneofKind: "unpatchedDependency",
13026
+ unpatchedDependency: __use_tusk_drift_schemas_core_communication.UnpatchedDependencyAlert.create({
13027
+ stackTrace: params.stackTrace,
13028
+ traceTestServerSpanId: params.traceTestServerSpanId,
13029
+ sdkVersion: SDK_VERSION
13030
+ })
13031
+ } });
13032
+ await this.sendAlert(alert);
13033
+ }
13026
13034
  handleIncomingData(data) {
13027
13035
  this.incomingBuffer = Buffer.concat([this.incomingBuffer, data]);
13028
13036
  logger.debug(`[ProtobufCommunicator] Processing buffer, length: ${this.incomingBuffer.length}`);
@@ -13080,6 +13088,22 @@ try {
13080
13088
  error: mockResponse.error || "Mock not found"
13081
13089
  });
13082
13090
  }
13091
+ if (message.payload.oneofKind === "envVarResponse") {
13092
+ const envVarResponse = message.payload.envVarResponse;
13093
+ logger.debug(`[ProtobufCommunicator] Received env var response for requestId: ${requestId}`);
13094
+ const pendingRequest = this.pendingRequests.get(requestId);
13095
+ if (!pendingRequest) {
13096
+ logger.warn("[ProtobufCommunicator] received env var response for unknown request:", requestId);
13097
+ return;
13098
+ }
13099
+ this.pendingRequests.delete(requestId);
13100
+ const envVars = {};
13101
+ if (envVarResponse?.envVars) Object.entries(envVarResponse.envVars).forEach(([key, value]) => {
13102
+ envVars[key] = value;
13103
+ });
13104
+ pendingRequest.resolve(envVars);
13105
+ return;
13106
+ }
13083
13107
  }
13084
13108
  /**
13085
13109
  * Extract response data from MockResponse
@@ -13122,6 +13146,95 @@ try {
13122
13146
  }
13123
13147
  }
13124
13148
  };
13149
+ ProtobufCommunicator.SYNC_CHILD_SCRIPT = `
13150
+ const net = require('net');
13151
+ const fs = require('fs');
13152
+
13153
+ const requestFile = process.argv[2];
13154
+ const responseFile = process.argv[3];
13155
+ const config = JSON.parse(process.argv[4]);
13156
+
13157
+ let responseReceived = false;
13158
+ let timeoutId;
13159
+
13160
+ function cleanup(exitCode) {
13161
+ if (timeoutId) clearTimeout(timeoutId);
13162
+ process.exit(exitCode);
13163
+ }
13164
+
13165
+ try {
13166
+ // Read the request data
13167
+ const requestData = fs.readFileSync(requestFile);
13168
+
13169
+ // Create connection based on config
13170
+ const client = config.type === 'unix'
13171
+ ? net.createConnection({ path: config.path })
13172
+ : net.createConnection({ host: config.host, port: config.port });
13173
+
13174
+ const incomingChunks = [];
13175
+ let incomingBuffer = Buffer.alloc(0);
13176
+
13177
+ // Set timeout
13178
+ timeoutId = setTimeout(() => {
13179
+ if (!responseReceived) {
13180
+ console.error('Timeout waiting for response');
13181
+ client.destroy();
13182
+ cleanup(1);
13183
+ }
13184
+ }, 10000);
13185
+
13186
+ client.on('connect', () => {
13187
+ // Send the request
13188
+ client.write(requestData);
13189
+ });
13190
+
13191
+ client.on('data', (data) => {
13192
+ incomingBuffer = Buffer.concat([incomingBuffer, data]);
13193
+
13194
+ // Try to parse complete message (4 byte length prefix + message)
13195
+ while (incomingBuffer.length >= 4) {
13196
+ const messageLength = incomingBuffer.readUInt32BE(0);
13197
+
13198
+ if (incomingBuffer.length < 4 + messageLength) {
13199
+ // Incomplete message, wait for more data
13200
+ break;
13201
+ }
13202
+
13203
+ // We have a complete message
13204
+ const messageData = incomingBuffer.slice(4, 4 + messageLength);
13205
+ incomingBuffer = incomingBuffer.slice(4 + messageLength);
13206
+
13207
+ // Write the complete response (including length prefix)
13208
+ const lengthPrefix = Buffer.allocUnsafe(4);
13209
+ lengthPrefix.writeUInt32BE(messageLength, 0);
13210
+ fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
13211
+
13212
+ responseReceived = true;
13213
+ client.destroy();
13214
+ cleanup(0);
13215
+ break;
13216
+ }
13217
+ });
13218
+
13219
+ client.on('error', (err) => {
13220
+ if (!responseReceived) {
13221
+ console.error('Connection error:', err.message);
13222
+ cleanup(1);
13223
+ }
13224
+ });
13225
+
13226
+ client.on('close', () => {
13227
+ if (!responseReceived) {
13228
+ console.error('Connection closed without response');
13229
+ cleanup(1);
13230
+ }
13231
+ });
13232
+
13233
+ } catch (err) {
13234
+ console.error('Script error:', err.message);
13235
+ cleanup(1);
13236
+ }
13237
+ `;
13125
13238
 
13126
13239
  //#endregion
13127
13240
  //#region src/core/TuskDriftInstrumentationModuleNames.ts
@@ -13474,6 +13587,30 @@ var TuskDriftCore = class TuskDriftCore {
13474
13587
  };
13475
13588
  }
13476
13589
  }
13590
+ /**
13591
+ * Request environment variables from CLI for a specific trace (synchronously).
13592
+ * This blocks the main thread, so it should be used carefully.
13593
+ */
13594
+ requestEnvVarsSync(traceTestServerSpanId) {
13595
+ if (!this.isConnectedWithCLI) {
13596
+ logger.error("Requesting sync env vars but CLI is not ready yet");
13597
+ throw new Error("Requesting sync env vars but CLI is not ready yet");
13598
+ }
13599
+ if (!this.communicator || this.mode !== TuskDriftMode.REPLAY) {
13600
+ logger.debug("Cannot request env vars: not in replay mode or no CLI connection");
13601
+ return {};
13602
+ }
13603
+ try {
13604
+ logger.debug(`Requesting env vars (sync) for trace: ${traceTestServerSpanId}`);
13605
+ const envVars = this.communicator.requestEnvVarsSync(traceTestServerSpanId);
13606
+ logger.debug(`Received env vars from CLI, count: ${Object.keys(envVars).length}`);
13607
+ logger.debug(`First 10 env vars: ${JSON.stringify(Object.keys(envVars).slice(0, 10), null, 2)}`);
13608
+ return envVars;
13609
+ } catch (error) {
13610
+ logger.error(`[TuskDrift] Error requesting env vars from CLI:`, error);
13611
+ return {};
13612
+ }
13613
+ }
13477
13614
  requestMockSync(mockRequest) {
13478
13615
  if (!this.isConnectedWithCLI) {
13479
13616
  logger.error("Requesting sync mock but CLI is not ready yet");
@@ -13528,6 +13665,9 @@ var TuskDriftCore = class TuskDriftCore {
13528
13665
  getTracer() {
13529
13666
  return __opentelemetry_api.trace.getTracer(TD_INSTRUMENTATION_LIBRARY_NAME);
13530
13667
  }
13668
+ getProtobufCommunicator() {
13669
+ return this.communicator;
13670
+ }
13531
13671
  };
13532
13672
  var TuskDriftSDK = class {
13533
13673
  constructor() {