@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 +333 -193
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +334 -194
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
*
|
|
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
|
-
|
|
568
|
+
if (TuskDriftCore.getInstance().getMode() !== TuskDriftMode.REPLAY) return;
|
|
569
|
+
const protobufComm = TuskDriftCore.getInstance().getProtobufCommunicator();
|
|
570
|
+
if (protobufComm) protobufComm.sendInstrumentationVersionMismatchAlert({
|
|
594
571
|
moduleName,
|
|
595
|
-
|
|
596
|
-
supportedVersions
|
|
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
|
-
*
|
|
580
|
+
* Send unpatched dependency alert to CLI
|
|
604
581
|
*/
|
|
605
|
-
function sendUnpatchedDependencyAlert({
|
|
582
|
+
function sendUnpatchedDependencyAlert({ traceTestServerSpanId, stackTrace }) {
|
|
583
|
+
logger.info("Sending unpatched dependency alert", {
|
|
584
|
+
traceTestServerSpanId,
|
|
585
|
+
stackTrace
|
|
586
|
+
});
|
|
606
587
|
try {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
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
|
-
|
|
2658
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6196
|
-
|
|
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
|
-
|
|
9637
|
-
|
|
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
|
-
*
|
|
12777
|
-
*
|
|
12778
|
-
*
|
|
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
|
-
|
|
12784
|
-
const requestId =
|
|
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
|
-
|
|
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(
|
|
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() {
|