@use-tusk/drift-node-sdk 0.1.11 → 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.js CHANGED
@@ -25,7 +25,7 @@ import { SpanExportServiceClient } from "@use-tusk/drift-schemas/backend/span_ex
25
25
  import { TwirpFetchTransport } from "@protobuf-ts/twirp-transport";
26
26
  import { BatchSpanProcessor, NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
27
27
  import { execSync } from "child_process";
28
- import { CLIMessage, ConnectRequest, GetMockRequest, MessageType, SDKMessage, SendInboundSpanForReplayRequest } from "@use-tusk/drift-schemas/core/communication";
28
+ import { CLIMessage, ConnectRequest, EnvVarRequest, GetMockRequest, InstrumentationVersionMismatchAlert, MessageType, SDKMessage, SendAlertRequest, SendInboundSpanForReplayRequest, UnpatchedDependencyAlert } from "@use-tusk/drift-schemas/core/communication";
29
29
  import { Resource } from "@opentelemetry/resources";
30
30
 
31
31
  //#region rolldown:runtime
@@ -254,15 +254,6 @@ var TdInstrumentationAbstract = class {
254
254
  }
255
255
  };
256
256
 
257
- //#endregion
258
- //#region package.json
259
- var version = "0.1.11";
260
-
261
- //#endregion
262
- //#region src/version.ts
263
- const SDK_VERSION = version;
264
- const MIN_CLI_VERSION = "0.1.0";
265
-
266
257
  //#endregion
267
258
  //#region src/core/utils/dataNormalizationUtils.ts
268
259
  /**
@@ -542,51 +533,40 @@ const logger = {
542
533
 
543
534
  //#endregion
544
535
  //#region src/core/analytics/analyticsUtils.ts
545
- function sendAnalyticsPayload(payload) {
546
- try {
547
- if (TuskDriftCore.getInstance().getConfig().recording?.enable_analytics) {}
548
- } catch (e) {
549
- logger.error("Error sending analytics event:", e);
550
- }
551
- }
552
- function sendTdAnalytics(eventName, properties = {}) {
553
- const serviceId = TuskDriftCore.getInstance().getConfig().service?.id || "unknown-service";
554
- const payload = {
555
- distinctId: `tusk-drift:${serviceId}`,
556
- event: `${eventName}`,
557
- properties: {
558
- serviceId,
559
- tdMode: TuskDriftCore.getInstance().getMode(),
560
- sdkVersion: SDK_VERSION,
561
- ...properties
562
- }
563
- };
564
- sendAnalyticsPayload(payload);
565
- }
566
536
  /**
567
- * NOTE: analytics has not been implemented yet, so this function does nothing
537
+ * Send version mismatch alert to CLI (only in REPLAY mode)
568
538
  */
569
539
  function sendVersionMismatchAlert({ moduleName, foundVersion, supportedVersions }) {
540
+ logger.info("Sending version mismatch alert", {
541
+ moduleName,
542
+ foundVersion,
543
+ supportedVersions
544
+ });
570
545
  try {
571
- sendTdAnalytics("version_mismatch", {
546
+ if (TuskDriftCore.getInstance().getMode() !== TuskDriftMode.REPLAY) return;
547
+ const protobufComm = TuskDriftCore.getInstance().getProtobufCommunicator();
548
+ if (protobufComm) protobufComm.sendInstrumentationVersionMismatchAlert({
572
549
  moduleName,
573
- foundVersion: foundVersion || "unknown",
574
- supportedVersions: supportedVersions.join(", ")
550
+ requestedVersion: foundVersion,
551
+ supportedVersions
575
552
  });
576
553
  } catch (e) {
577
554
  logger.error("Error sending version mismatch alert:", e);
578
555
  }
579
556
  }
580
557
  /**
581
- * NOTE: analytics has not been implemented yet, so this function does nothing
558
+ * Send unpatched dependency alert to CLI
582
559
  */
583
- function sendUnpatchedDependencyAlert({ method, spanId, traceId, stackTrace }) {
560
+ function sendUnpatchedDependencyAlert({ traceTestServerSpanId, stackTrace }) {
561
+ logger.info("Sending unpatched dependency alert", {
562
+ traceTestServerSpanId,
563
+ stackTrace
564
+ });
584
565
  try {
585
- sendTdAnalytics("unpatched_dependency", {
586
- method,
587
- spanId,
588
- traceId,
589
- stackTrace: stackTrace ? stackTrace.split("\n").slice(0, 10).join("\n") : void 0
566
+ const protobufComm = TuskDriftCore.getInstance().getProtobufCommunicator();
567
+ if (protobufComm && stackTrace) protobufComm.sendUnpatchedDependencyAlert({
568
+ stackTrace,
569
+ traceTestServerSpanId
590
570
  });
591
571
  } catch (e) {
592
572
  logger.error("Error sending unpatched dependency alert:", e);
@@ -2017,9 +1997,12 @@ var HttpReplayHooks = class {
2017
1997
  const traceIdHeader = req.headers["x-td-trace-id"] || req.headers["X-TD-TRACE-ID"];
2018
1998
  return traceIdHeader ? String(traceIdHeader) : null;
2019
1999
  }
2020
- extractEnvVarsFromHeaders(req) {
2021
- const envVarsHeader = req.headers["x-td-env-vars"] || req.headers["X-TD-ENV-VARS"];
2022
- return envVarsHeader ? JSON.parse(String(envVarsHeader)) : void 0;
2000
+ /**
2001
+ * Check if we should fetch env vars from CLI
2002
+ */
2003
+ extractShouldFetchEnvVars(req) {
2004
+ const fetchHeader = req.headers["x-td-fetch-env-vars"] || req.headers["X-TD-FETCH-ENV-VARS"];
2005
+ return fetchHeader === "true" || fetchHeader === true;
2023
2006
  }
2024
2007
  /**
2025
2008
  * Handle outbound HTTP requests in replay mode
@@ -2038,7 +2021,7 @@ var HttpReplayHooks = class {
2038
2021
  auth: requestOptions.auth || void 0,
2039
2022
  agent: requestOptions.agent || void 0,
2040
2023
  protocol,
2041
- hostname: requestOptions.hostname || void 0,
2024
+ hostname: requestOptions.hostname || requestOptions.host || void 0,
2042
2025
  port: requestOptions.port ? Number(requestOptions.port) : void 0,
2043
2026
  method
2044
2027
  };
@@ -2632,8 +2615,13 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2632
2615
  return originalHandler.call(this);
2633
2616
  }
2634
2617
  logger.debug(`[HttpInstrumentation] Setting replay trace id`, replayTraceId);
2635
- const envVars = this.replayHooks.extractEnvVarsFromHeaders(req);
2636
- if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
2618
+ if (this.replayHooks.extractShouldFetchEnvVars(req)) try {
2619
+ const envVars = this.tuskDrift.requestEnvVarsSync(replayTraceId);
2620
+ EnvVarTracker.setEnvVars(replayTraceId, envVars);
2621
+ logger.debug(`[HttpInstrumentation] Fetched env vars from CLI for trace ${replayTraceId}`);
2622
+ } catch (error) {
2623
+ logger.error(`[HttpInstrumentation] Failed to fetch env vars from CLI:`, error);
2624
+ }
2637
2625
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
2638
2626
  if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
2639
2627
  return context.with(ctxWithReplayTraceId, () => {
@@ -3007,13 +2995,29 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
3007
2995
  readable: res.readable
3008
2996
  };
3009
2997
  const responseChunks = [];
3010
- if (res.readable) {
2998
+ let streamConsumptionMode = "NOT_CONSUMING";
2999
+ const originalRead = res.read?.bind(res);
3000
+ if (originalRead) res.read = function read(size) {
3001
+ const chunk = originalRead(size);
3002
+ if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
3003
+ streamConsumptionMode = "READ";
3004
+ responseChunks.push(Buffer.from(chunk));
3005
+ }
3006
+ return chunk;
3007
+ };
3008
+ res.once("resume", () => {
3011
3009
  res.on("data", (chunk) => {
3012
- responseChunks.push(chunk);
3010
+ if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
3011
+ streamConsumptionMode = "PIPE";
3012
+ responseChunks.push(Buffer.from(chunk));
3013
+ }
3013
3014
  });
3014
- res.on("end", async () => {
3015
- if (responseChunks.length > 0) try {
3016
- const responseBuffer = combineChunks(responseChunks);
3015
+ });
3016
+ res.on("end", async (chunk) => {
3017
+ if (chunk && typeof chunk !== "function") responseChunks.push(Buffer.from(chunk));
3018
+ try {
3019
+ if (responseChunks.length > 0) {
3020
+ const responseBuffer = Buffer.concat(responseChunks);
3017
3021
  const rawHeaders = this._captureHeadersFromRawHeaders(res.rawHeaders);
3018
3022
  outputValue.headers = rawHeaders;
3019
3023
  const contentEncoding = rawHeaders["content-encoding"];
@@ -3022,40 +3026,24 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
3022
3026
  contentEncoding
3023
3027
  });
3024
3028
  outputValue.bodySize = responseBuffer.length;
3025
- this._addOutputAttributesToSpan({
3026
- spanInfo,
3027
- outputValue,
3028
- statusCode: res.statusCode || 1,
3029
- outputSchemaMerges: {
3030
- body: {
3031
- encoding: EncodingType.BASE64,
3032
- decodedType: getDecodedType(outputValue.headers["content-type"] || "")
3033
- },
3034
- headers: { matchImportance: 0 }
3035
- },
3036
- inputValue: completeInputValue
3037
- });
3038
- } catch (error) {
3039
- logger.error(`[HttpInstrumentation] Error processing response body:`, error);
3040
3029
  }
3041
- });
3042
- } else try {
3043
- this._addOutputAttributesToSpan({
3044
- spanInfo,
3045
- outputValue,
3046
- statusCode: res.statusCode || 1,
3047
- outputSchemaMerges: {
3048
- body: {
3049
- encoding: EncodingType.BASE64,
3050
- decodedType: getDecodedType(outputValue.headers["content-type"] || "")
3030
+ this._addOutputAttributesToSpan({
3031
+ spanInfo,
3032
+ outputValue,
3033
+ statusCode: res.statusCode || 1,
3034
+ outputSchemaMerges: {
3035
+ body: {
3036
+ encoding: EncodingType.BASE64,
3037
+ decodedType: getDecodedType(outputValue.headers["content-type"] || "")
3038
+ },
3039
+ headers: { matchImportance: 0 }
3051
3040
  },
3052
- headers: { matchImportance: 0 }
3053
- },
3054
- inputValue: completeInputValue
3055
- });
3056
- } catch (error) {
3057
- logger.error(`[HttpInstrumentation] Error adding output attributes to span:`, error);
3058
- }
3041
+ inputValue: completeInputValue
3042
+ });
3043
+ } catch (error) {
3044
+ logger.error(`[HttpInstrumentation] Error processing response body:`, error);
3045
+ }
3046
+ });
3059
3047
  });
3060
3048
  req.on("error", (error) => {
3061
3049
  try {
@@ -6167,12 +6155,13 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
6167
6155
  traceId: currentSpanInfo.traceId,
6168
6156
  socketContext
6169
6157
  });
6170
- logger.warn(`[TcpInstrumentation] Full stack trace:\n${(/* @__PURE__ */ new Error()).stack}`);
6158
+ const stackTrace = (/* @__PURE__ */ new Error()).stack || "";
6159
+ const traceTestServerSpanId = SpanUtils.getCurrentReplayTraceId();
6160
+ logger.warn(`[TcpInstrumentation] Full stack trace:\n${stackTrace}`, { traceTestServerSpanId });
6171
6161
  Error.stackTraceLimit = 10;
6172
6162
  sendUnpatchedDependencyAlert({
6173
- method: methodName,
6174
- spanId: currentSpanInfo.spanId,
6175
- traceId: currentSpanInfo.traceId
6163
+ traceTestServerSpanId: traceTestServerSpanId || "",
6164
+ stackTrace
6176
6165
  });
6177
6166
  if (this.loggedSpans.size > 1e3) {
6178
6167
  logger.debug(`[TcpInstrumentation] Cleaning up logged spans cache (${this.loggedSpans.size} entries)`);
@@ -9611,8 +9600,13 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
9611
9600
  return originalHandleRequest.call(this, req, res, parsedUrl);
9612
9601
  }
9613
9602
  logger.debug(`[NextjsInstrumentation] Setting replay trace id`, replayTraceId);
9614
- const envVars = self.replayHooks.extractEnvVarsFromHeaders(req);
9615
- if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
9603
+ if (self.replayHooks.extractShouldFetchEnvVars(req)) try {
9604
+ const envVars = self.tuskDrift.requestEnvVarsSync(replayTraceId);
9605
+ EnvVarTracker.setEnvVars(replayTraceId, envVars);
9606
+ logger.debug(`[NextjsInstrumentation] Fetched env vars from CLI for trace ${replayTraceId}`);
9607
+ } catch (error) {
9608
+ logger.error(`[NextjsInstrumentation] Failed to fetch env vars from CLI:`, error);
9609
+ }
9616
9610
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
9617
9611
  if (!ctxWithReplayTraceId) throw new Error("Error setting current replay trace id");
9618
9612
  return context.with(ctxWithReplayTraceId, () => {
@@ -9918,6 +9912,239 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
9918
9912
  }
9919
9913
  };
9920
9914
 
9915
+ //#endregion
9916
+ //#region src/instrumentation/libraries/prisma/types.ts
9917
+ /**
9918
+ * Prisma error class names for proper error handling
9919
+ */
9920
+ let PrismaErrorClassName = /* @__PURE__ */ function(PrismaErrorClassName$1) {
9921
+ PrismaErrorClassName$1["PrismaClientKnownRequestError"] = "PrismaClientKnownRequestError";
9922
+ PrismaErrorClassName$1["PrismaClientUnknownRequestError"] = "PrismaClientUnknownRequestError";
9923
+ PrismaErrorClassName$1["PrismaClientInitializationError"] = "PrismaClientInitializationError";
9924
+ PrismaErrorClassName$1["PrismaClientValidationError"] = "PrismaClientValidationError";
9925
+ PrismaErrorClassName$1["PrismaClientRustPanicError"] = "PrismaClientRustPanicError";
9926
+ PrismaErrorClassName$1["NotFoundError"] = "NotFoundError";
9927
+ return PrismaErrorClassName$1;
9928
+ }({});
9929
+
9930
+ //#endregion
9931
+ //#region src/instrumentation/libraries/prisma/Instrumentation.ts
9932
+ var PrismaInstrumentation = class extends TdInstrumentationBase {
9933
+ constructor(config = {}) {
9934
+ super("@prisma/client", config);
9935
+ this.INSTRUMENTATION_NAME = "PrismaInstrumentation";
9936
+ this.prismaErrorClasses = [];
9937
+ this.mode = config.mode || TuskDriftMode.DISABLED;
9938
+ this.tuskDrift = TuskDriftCore.getInstance();
9939
+ }
9940
+ init() {
9941
+ return [new TdInstrumentationNodeModule({
9942
+ name: "@prisma/client",
9943
+ supportedVersions: ["5.*", "6.*"],
9944
+ patch: (moduleExports) => this._patchPrismaModule(moduleExports)
9945
+ })];
9946
+ }
9947
+ _patchPrismaModule(prismaModule) {
9948
+ if (this.isModulePatched(prismaModule)) {
9949
+ logger.debug(`[PrismaInstrumentation] Prisma module already patched, skipping`);
9950
+ return prismaModule;
9951
+ }
9952
+ this._storePrismaErrorClasses(prismaModule);
9953
+ logger.debug(`[PrismaInstrumentation] Wrapping PrismaClient constructor`);
9954
+ this._wrap(prismaModule, "PrismaClient", (OriginalPrismaClient) => {
9955
+ const self = this;
9956
+ logger.debug(`[PrismaInstrumentation] PrismaClient wrapper called`);
9957
+ return class TdPrismaClient {
9958
+ constructor(...args) {
9959
+ logger.debug(`[PrismaInstrumentation] Creating patched PrismaClient instance`);
9960
+ return new OriginalPrismaClient(...args).$extends({ query: { async $allOperations({ model, operation, args: operationArgs, query }) {
9961
+ logger.debug(`[PrismaInstrumentation] $allOperations intercepted: ${model}.${operation}`);
9962
+ return self._handlePrismaOperation({
9963
+ model,
9964
+ operation,
9965
+ args: operationArgs,
9966
+ query
9967
+ });
9968
+ } } });
9969
+ }
9970
+ };
9971
+ });
9972
+ this.markModuleAsPatched(prismaModule);
9973
+ logger.debug(`[PrismaInstrumentation] Prisma module patching complete`);
9974
+ return prismaModule;
9975
+ }
9976
+ _storePrismaErrorClasses(moduleExports) {
9977
+ const prismaNamespace = moduleExports.Prisma || {};
9978
+ this.prismaErrorClasses = [
9979
+ {
9980
+ name: PrismaErrorClassName.PrismaClientKnownRequestError,
9981
+ errorClass: moduleExports.PrismaClientKnownRequestError || prismaNamespace.PrismaClientKnownRequestError
9982
+ },
9983
+ {
9984
+ name: PrismaErrorClassName.PrismaClientUnknownRequestError,
9985
+ errorClass: moduleExports.PrismaClientUnknownRequestError || prismaNamespace.PrismaClientUnknownRequestError
9986
+ },
9987
+ {
9988
+ name: PrismaErrorClassName.PrismaClientInitializationError,
9989
+ errorClass: moduleExports.PrismaClientInitializationError || prismaNamespace.PrismaClientInitializationError
9990
+ },
9991
+ {
9992
+ name: PrismaErrorClassName.PrismaClientValidationError,
9993
+ errorClass: moduleExports.PrismaClientValidationError || prismaNamespace.PrismaClientValidationError
9994
+ },
9995
+ {
9996
+ name: PrismaErrorClassName.PrismaClientRustPanicError,
9997
+ errorClass: moduleExports.PrismaClientRustPanicError || prismaNamespace.PrismaClientRustPanicError
9998
+ },
9999
+ {
10000
+ name: PrismaErrorClassName.NotFoundError,
10001
+ errorClass: moduleExports.NotFoundError || prismaNamespace.NotFoundError
10002
+ }
10003
+ ];
10004
+ }
10005
+ _handlePrismaOperation({ model, operation, args, query }) {
10006
+ const inputValue = {
10007
+ model,
10008
+ operation,
10009
+ args
10010
+ };
10011
+ logger.debug(`[PrismaInstrumentation] Intercepted Prisma operation: ${model}.${operation} in ${this.mode} mode`);
10012
+ if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
10013
+ originalFunctionCall: () => query(args),
10014
+ recordModeHandler: ({ isPreAppStart }) => {
10015
+ return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
10016
+ name: `prisma.${operation}`,
10017
+ kind: SpanKind.CLIENT,
10018
+ submodule: model,
10019
+ packageType: PackageType.UNSPECIFIED,
10020
+ packageName: "@prisma/client",
10021
+ instrumentationName: this.INSTRUMENTATION_NAME,
10022
+ inputValue,
10023
+ isPreAppStart
10024
+ }, (spanInfo) => {
10025
+ return this._handleRecordPrismaOperation(spanInfo, query, args);
10026
+ });
10027
+ },
10028
+ spanKind: SpanKind.CLIENT
10029
+ });
10030
+ else if (this.mode === TuskDriftMode.REPLAY) {
10031
+ const stackTrace = captureStackTrace(["PrismaInstrumentation"]);
10032
+ return handleReplayMode({
10033
+ noOpRequestHandler: () => query(args),
10034
+ isServerRequest: false,
10035
+ replayModeHandler: () => {
10036
+ return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
10037
+ name: `prisma.${operation}`,
10038
+ kind: SpanKind.CLIENT,
10039
+ submodule: model,
10040
+ packageType: PackageType.UNSPECIFIED,
10041
+ packageName: "@prisma/client",
10042
+ instrumentationName: this.INSTRUMENTATION_NAME,
10043
+ inputValue,
10044
+ isPreAppStart: false
10045
+ }, (spanInfo) => {
10046
+ return this._handleReplayPrismaOperation(spanInfo, inputValue, stackTrace);
10047
+ });
10048
+ }
10049
+ });
10050
+ } else return query(args);
10051
+ }
10052
+ async _handleRecordPrismaOperation(spanInfo, query, args) {
10053
+ try {
10054
+ logger.debug(`[PrismaInstrumentation] Recording Prisma operation`);
10055
+ const result = await query(args);
10056
+ const outputValue = {
10057
+ prismaResult: result,
10058
+ _tdOriginalFormat: "result"
10059
+ };
10060
+ try {
10061
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
10062
+ SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
10063
+ } catch (spanError) {
10064
+ logger.error(`[PrismaInstrumentation] error adding span attributes:`, spanError);
10065
+ }
10066
+ return result;
10067
+ } catch (error) {
10068
+ logger.debug(`[PrismaInstrumentation] Prisma operation error: ${error.message}`);
10069
+ try {
10070
+ const errorClassName = this._getPrismaErrorClassName(error);
10071
+ const errorWithClassName = this._cloneError(error);
10072
+ if (errorClassName) errorWithClassName.customTdName = errorClassName;
10073
+ const outputValue = {
10074
+ prismaResult: errorWithClassName,
10075
+ _tdOriginalFormat: "error"
10076
+ };
10077
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
10078
+ SpanUtils.endSpan(spanInfo.span, {
10079
+ code: SpanStatusCode.ERROR,
10080
+ message: error.message
10081
+ });
10082
+ } catch (spanError) {
10083
+ logger.error(`[PrismaInstrumentation] error extracting error and adding span attributes:`, spanError);
10084
+ }
10085
+ throw error;
10086
+ }
10087
+ }
10088
+ async _handleReplayPrismaOperation(spanInfo, inputValue, stackTrace) {
10089
+ const mockData = await findMockResponseAsync({
10090
+ mockRequestData: {
10091
+ traceId: spanInfo.traceId,
10092
+ spanId: spanInfo.spanId,
10093
+ name: `prisma.${inputValue.operation}`,
10094
+ inputValue,
10095
+ packageName: "@prisma/client",
10096
+ instrumentationName: this.INSTRUMENTATION_NAME,
10097
+ submoduleName: inputValue.model,
10098
+ kind: SpanKind.CLIENT,
10099
+ stackTrace
10100
+ },
10101
+ tuskDrift: this.tuskDrift
10102
+ });
10103
+ if (!mockData) {
10104
+ logger.warn(`[PrismaInstrumentation] No mock data found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10105
+ throw new Error(`[PrismaInstrumentation] No matching mock found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10106
+ }
10107
+ logger.debug(`[PrismaInstrumentation] Found mock data for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10108
+ const outputValue = mockData.result;
10109
+ if (outputValue._tdOriginalFormat === "error") {
10110
+ const errorObj = outputValue.prismaResult;
10111
+ if (errorObj.customTdName) {
10112
+ const errorClass = this._getPrismaErrorClassFromName(errorObj.customTdName);
10113
+ if (errorClass) Object.setPrototypeOf(errorObj, errorClass.prototype);
10114
+ }
10115
+ SpanUtils.endSpan(spanInfo.span, {
10116
+ code: SpanStatusCode.ERROR,
10117
+ message: errorObj.message || "Prisma error"
10118
+ });
10119
+ throw errorObj;
10120
+ }
10121
+ SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
10122
+ return outputValue.prismaResult;
10123
+ }
10124
+ _getPrismaErrorClassName(error) {
10125
+ for (const errorInfo of this.prismaErrorClasses) if (error instanceof errorInfo.errorClass) return errorInfo.name;
10126
+ }
10127
+ _getPrismaErrorClassFromName(className) {
10128
+ for (const errorInfo of this.prismaErrorClasses) if (errorInfo.name === className) return errorInfo.errorClass;
10129
+ return null;
10130
+ }
10131
+ /**
10132
+ * Deep clone an error object to make it serializable
10133
+ */
10134
+ _cloneError(error) {
10135
+ const cloned = new Error(error.message);
10136
+ cloned.name = error.name;
10137
+ cloned.stack = error.stack;
10138
+ for (const key in error) if (error.hasOwnProperty(key)) try {
10139
+ cloned[key] = error[key];
10140
+ } catch (e) {}
10141
+ return cloned;
10142
+ }
10143
+ _wrap(target, propertyName, wrapper) {
10144
+ wrap(target, propertyName, wrapper);
10145
+ }
10146
+ };
10147
+
9921
10148
  //#endregion
9922
10149
  //#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
9923
10150
  var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
@@ -12418,9 +12645,18 @@ var TdSpanExporter = class {
12418
12645
  }
12419
12646
  };
12420
12647
 
12648
+ //#endregion
12649
+ //#region package.json
12650
+ var version = "0.1.13";
12651
+
12652
+ //#endregion
12653
+ //#region src/version.ts
12654
+ const SDK_VERSION = version;
12655
+ const MIN_CLI_VERSION = "0.1.0";
12656
+
12421
12657
  //#endregion
12422
12658
  //#region src/core/ProtobufCommunicator.ts
12423
- var ProtobufCommunicator = class {
12659
+ var ProtobufCommunicator = class ProtobufCommunicator {
12424
12660
  constructor() {
12425
12661
  this.client = null;
12426
12662
  this.pendingRequests = /* @__PURE__ */ new Map();
@@ -12518,43 +12754,18 @@ var ProtobufCommunicator = class {
12518
12754
  });
12519
12755
  }
12520
12756
  /**
12521
- * This function uses a separate Node.js child process to communicate with the CLI over a socket.
12522
- * The child process creates its own connection and event loop, allowing proper async socket handling.
12523
- * The parent process blocks synchronously waiting for the child to complete.
12524
- *
12525
- * Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
12526
- * for instrumentations that require fetching mocks synchronously.
12757
+ * Generic synchronous request handler that spawns a child process.
12758
+ * @param sdkMessage The SDK message to send
12759
+ * @param filePrefix Prefix for temporary files (e.g., 'envvar', 'mock')
12760
+ * @param responseHandler Function to extract and return the desired response
12527
12761
  */
12528
- requestMockSync(mockRequest) {
12529
- const requestId = this.generateRequestId();
12530
- const cleanSpan = mockRequest.outboundSpan ? this.cleanSpan(mockRequest.outboundSpan) : void 0;
12531
- if (cleanSpan?.inputValue) cleanSpan.inputValue = objectToProtobufStruct(cleanSpan.inputValue);
12532
- if (cleanSpan?.inputSchema) cleanSpan.inputSchema = objectToProtobufStruct(cleanSpan.inputSchema);
12533
- if (cleanSpan?.kind) cleanSpan.kind = mapOtToPb(cleanSpan.kind);
12534
- const protoMockRequest = GetMockRequest.create({
12535
- ...mockRequest,
12536
- requestId,
12537
- tags: {},
12538
- outboundSpan: cleanSpan,
12539
- stackTrace: cleanSpan?.stackTrace
12540
- });
12541
- const sdkMessage = SDKMessage.create({
12542
- type: MessageType.MOCK_REQUEST,
12543
- requestId,
12544
- payload: {
12545
- oneofKind: "getMockRequest",
12546
- getMockRequest: protoMockRequest
12547
- }
12548
- });
12549
- logger.debug("Sending protobuf request to CLI (sync)", {
12550
- outboundSpan: mockRequest.outboundSpan,
12551
- testId: mockRequest.testId
12552
- });
12762
+ executeSyncRequest(sdkMessage, filePrefix, responseHandler) {
12763
+ const requestId = sdkMessage.requestId;
12553
12764
  const messageBytes = SDKMessage.toBinary(sdkMessage);
12554
12765
  const tempDir = os.tmpdir();
12555
- const requestFile = path.join(tempDir, `tusk-sync-request-${requestId}.bin`);
12556
- const responseFile = path.join(tempDir, `tusk-sync-response-${requestId}.bin`);
12557
- const scriptFile = path.join(tempDir, `tusk-sync-script-${requestId}.js`);
12766
+ const requestFile = path.join(tempDir, `tusk-sync-${filePrefix}-request-${requestId}.bin`);
12767
+ const responseFile = path.join(tempDir, `tusk-sync-${filePrefix}-response-${requestId}.bin`);
12768
+ const scriptFile = path.join(tempDir, `tusk-sync-${filePrefix}-script-${requestId}.js`);
12558
12769
  try {
12559
12770
  const lengthBuffer = Buffer.allocUnsafe(4);
12560
12771
  lengthBuffer.writeUInt32BE(messageBytes.length, 0);
@@ -12577,95 +12788,7 @@ var ProtobufCommunicator = class {
12577
12788
  type: "unix",
12578
12789
  path: path.join(os.tmpdir(), "tusk-connect.sock")
12579
12790
  };
12580
- fs.writeFileSync(scriptFile, `
12581
- const net = require('net');
12582
- const fs = require('fs');
12583
-
12584
- const requestFile = process.argv[2];
12585
- const responseFile = process.argv[3];
12586
- const config = JSON.parse(process.argv[4]);
12587
-
12588
- let responseReceived = false;
12589
- let timeoutId;
12590
-
12591
- function cleanup(exitCode) {
12592
- if (timeoutId) clearTimeout(timeoutId);
12593
- process.exit(exitCode);
12594
- }
12595
-
12596
- try {
12597
- // Read the request data
12598
- const requestData = fs.readFileSync(requestFile);
12599
-
12600
- // Create connection based on config
12601
- const client = config.type === 'unix'
12602
- ? net.createConnection({ path: config.path })
12603
- : net.createConnection({ host: config.host, port: config.port });
12604
-
12605
- const incomingChunks = [];
12606
- let incomingBuffer = Buffer.alloc(0);
12607
-
12608
- // Set timeout
12609
- timeoutId = setTimeout(() => {
12610
- if (!responseReceived) {
12611
- console.error('Timeout waiting for response');
12612
- client.destroy();
12613
- cleanup(1);
12614
- }
12615
- }, 10000);
12616
-
12617
- client.on('connect', () => {
12618
- // Send the request
12619
- client.write(requestData);
12620
- });
12621
-
12622
- client.on('data', (data) => {
12623
- incomingBuffer = Buffer.concat([incomingBuffer, data]);
12624
-
12625
- // Try to parse complete message (4 byte length prefix + message)
12626
- while (incomingBuffer.length >= 4) {
12627
- const messageLength = incomingBuffer.readUInt32BE(0);
12628
-
12629
- if (incomingBuffer.length < 4 + messageLength) {
12630
- // Incomplete message, wait for more data
12631
- break;
12632
- }
12633
-
12634
- // We have a complete message
12635
- const messageData = incomingBuffer.slice(4, 4 + messageLength);
12636
- incomingBuffer = incomingBuffer.slice(4 + messageLength);
12637
-
12638
- // Write the complete response (including length prefix)
12639
- const lengthPrefix = Buffer.allocUnsafe(4);
12640
- lengthPrefix.writeUInt32BE(messageLength, 0);
12641
- fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
12642
-
12643
- responseReceived = true;
12644
- client.destroy();
12645
- cleanup(0);
12646
- break;
12647
- }
12648
- });
12649
-
12650
- client.on('error', (err) => {
12651
- if (!responseReceived) {
12652
- console.error('Connection error:', err.message);
12653
- cleanup(1);
12654
- }
12655
- });
12656
-
12657
- client.on('close', () => {
12658
- if (!responseReceived) {
12659
- console.error('Connection closed without response');
12660
- cleanup(1);
12661
- }
12662
- });
12663
-
12664
- } catch (err) {
12665
- console.error('Script error:', err.message);
12666
- cleanup(1);
12667
- }
12668
- `);
12791
+ fs.writeFileSync(scriptFile, ProtobufCommunicator.SYNC_CHILD_SCRIPT);
12669
12792
  try {
12670
12793
  execSync(`node "${scriptFile}" "${requestFile}" "${responseFile}" '${JSON.stringify(connectionConfig)}'`, {
12671
12794
  timeout: 12e3,
@@ -12677,27 +12800,13 @@ try {
12677
12800
  if (responseBuffer.length < 4 + responseLength) throw new Error("Invalid response: incomplete message");
12678
12801
  const responseData = responseBuffer.slice(4, 4 + responseLength);
12679
12802
  const cliMessage = CLIMessage.fromBinary(responseData);
12680
- if (cliMessage.payload.oneofKind !== "getMockResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12681
- const mockResponse = cliMessage.payload.getMockResponse;
12682
- if (!mockResponse) throw new Error("No mock response received");
12683
- if (mockResponse.found) try {
12684
- return {
12685
- found: true,
12686
- response: this.extractResponseData(mockResponse)
12687
- };
12688
- } catch (error) {
12689
- throw new Error(`Failed to extract response data: ${error}`);
12690
- }
12691
- else return {
12692
- found: false,
12693
- error: mockResponse.error || "Mock not found"
12694
- };
12803
+ return responseHandler(cliMessage);
12695
12804
  } catch (error) {
12696
- logger.error("[ProtobufCommunicator] error in sync request child process:", error);
12805
+ logger.error(`[ProtobufCommunicator] error in sync ${filePrefix} request child process:`, error);
12697
12806
  throw error;
12698
12807
  }
12699
12808
  } catch (error) {
12700
- throw new Error(`Sync request failed: ${error.message}`);
12809
+ throw new Error(`Sync ${filePrefix} request failed: ${error.message}`);
12701
12810
  } finally {
12702
12811
  try {
12703
12812
  if (fs.existsSync(requestFile)) fs.unlinkSync(requestFile);
@@ -12716,6 +12825,86 @@ try {
12716
12825
  }
12717
12826
  }
12718
12827
  }
12828
+ /**
12829
+ * Request environment variables from CLI synchronously using a child process.
12830
+ * This blocks the main thread, so it should be used carefully.
12831
+ * Similar to requestMockSync but for environment variables.
12832
+ */
12833
+ requestEnvVarsSync(traceTestServerSpanId) {
12834
+ const requestId = this.generateRequestId();
12835
+ const envVarRequest = EnvVarRequest.create({ traceTestServerSpanId });
12836
+ const sdkMessage = SDKMessage.create({
12837
+ type: MessageType.ENV_VAR_REQUEST,
12838
+ requestId,
12839
+ payload: {
12840
+ oneofKind: "envVarRequest",
12841
+ envVarRequest
12842
+ }
12843
+ });
12844
+ logger.debug(`[ProtobufCommunicator] Requesting env vars (sync) for trace: ${traceTestServerSpanId}`);
12845
+ return this.executeSyncRequest(sdkMessage, "envvar", (cliMessage) => {
12846
+ if (cliMessage.payload.oneofKind !== "envVarResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12847
+ const envVarResponse = cliMessage.payload.envVarResponse;
12848
+ if (!envVarResponse) throw new Error("No env var response received");
12849
+ const envVars = {};
12850
+ if (envVarResponse.envVars) Object.entries(envVarResponse.envVars).forEach(([key, value]) => {
12851
+ envVars[key] = value;
12852
+ });
12853
+ logger.debug(`[ProtobufCommunicator] Received env vars (sync), count: ${Object.keys(envVars).length}`);
12854
+ return envVars;
12855
+ });
12856
+ }
12857
+ /**
12858
+ * This function uses a separate Node.js child process to communicate with the CLI over a socket.
12859
+ * The child process creates its own connection and event loop, allowing proper async socket handling.
12860
+ * The parent process blocks synchronously waiting for the child to complete.
12861
+ *
12862
+ * Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
12863
+ * for instrumentations that require fetching mocks synchronously.
12864
+ */
12865
+ requestMockSync(mockRequest) {
12866
+ const requestId = this.generateRequestId();
12867
+ const cleanSpan = mockRequest.outboundSpan ? this.cleanSpan(mockRequest.outboundSpan) : void 0;
12868
+ if (cleanSpan?.inputValue) cleanSpan.inputValue = objectToProtobufStruct(cleanSpan.inputValue);
12869
+ if (cleanSpan?.inputSchema) cleanSpan.inputSchema = objectToProtobufStruct(cleanSpan.inputSchema);
12870
+ if (cleanSpan?.kind) cleanSpan.kind = mapOtToPb(cleanSpan.kind);
12871
+ const protoMockRequest = GetMockRequest.create({
12872
+ ...mockRequest,
12873
+ requestId,
12874
+ tags: {},
12875
+ outboundSpan: cleanSpan,
12876
+ stackTrace: cleanSpan?.stackTrace
12877
+ });
12878
+ const sdkMessage = SDKMessage.create({
12879
+ type: MessageType.MOCK_REQUEST,
12880
+ requestId,
12881
+ payload: {
12882
+ oneofKind: "getMockRequest",
12883
+ getMockRequest: protoMockRequest
12884
+ }
12885
+ });
12886
+ logger.debug("Sending protobuf request to CLI (sync)", {
12887
+ outboundSpan: mockRequest.outboundSpan,
12888
+ testId: mockRequest.testId
12889
+ });
12890
+ return this.executeSyncRequest(sdkMessage, "mock", (cliMessage) => {
12891
+ if (cliMessage.payload.oneofKind !== "getMockResponse") throw new Error(`Unexpected response type: ${cliMessage.type}`);
12892
+ const mockResponse = cliMessage.payload.getMockResponse;
12893
+ if (!mockResponse) throw new Error("No mock response received");
12894
+ if (mockResponse.found) try {
12895
+ return {
12896
+ found: true,
12897
+ response: this.extractResponseData(mockResponse)
12898
+ };
12899
+ } catch (error) {
12900
+ throw new Error(`Failed to extract response data: ${error}`);
12901
+ }
12902
+ else return {
12903
+ found: false,
12904
+ error: mockResponse.error || "Mock not found"
12905
+ };
12906
+ });
12907
+ }
12719
12908
  async sendProtobufMessage(message) {
12720
12909
  if (!this.client || !this.protobufContext) throw new Error("Not connected to CLI");
12721
12910
  const messageBytes = SDKMessage.toBinary(message);
@@ -12768,6 +12957,58 @@ try {
12768
12957
  });
12769
12958
  await this.sendProtobufMessage(sdkMessage);
12770
12959
  }
12960
+ /**
12961
+ * Send an alert to the CLI (fire-and-forget, no response expected)
12962
+ */
12963
+ async sendAlert(alert) {
12964
+ if (!this.client || !this.protobufContext) {
12965
+ logger.debug("[ProtobufCommunicator] Not connected to CLI, skipping alert");
12966
+ return;
12967
+ }
12968
+ const sdkMessage = SDKMessage.create({
12969
+ type: MessageType.ALERT,
12970
+ requestId: this.generateRequestId(),
12971
+ payload: {
12972
+ oneofKind: "sendAlertRequest",
12973
+ sendAlertRequest: alert
12974
+ }
12975
+ });
12976
+ try {
12977
+ await this.sendProtobufMessage(sdkMessage);
12978
+ logger.debug("[ProtobufCommunicator] Alert sent to CLI");
12979
+ } catch (error) {
12980
+ logger.debug("[ProtobufCommunicator] Failed to send alert to CLI:", error);
12981
+ }
12982
+ }
12983
+ /**
12984
+ * Send instrumentation version mismatch alert to CLI
12985
+ */
12986
+ async sendInstrumentationVersionMismatchAlert(params) {
12987
+ const alert = SendAlertRequest.create({ alert: {
12988
+ oneofKind: "versionMismatch",
12989
+ versionMismatch: InstrumentationVersionMismatchAlert.create({
12990
+ moduleName: params.moduleName,
12991
+ requestedVersion: params.requestedVersion || "",
12992
+ supportedVersions: params.supportedVersions,
12993
+ sdkVersion: SDK_VERSION
12994
+ })
12995
+ } });
12996
+ await this.sendAlert(alert);
12997
+ }
12998
+ /**
12999
+ * Send unpatched dependency alert to CLI
13000
+ */
13001
+ async sendUnpatchedDependencyAlert(params) {
13002
+ const alert = SendAlertRequest.create({ alert: {
13003
+ oneofKind: "unpatchedDependency",
13004
+ unpatchedDependency: UnpatchedDependencyAlert.create({
13005
+ stackTrace: params.stackTrace,
13006
+ traceTestServerSpanId: params.traceTestServerSpanId,
13007
+ sdkVersion: SDK_VERSION
13008
+ })
13009
+ } });
13010
+ await this.sendAlert(alert);
13011
+ }
12771
13012
  handleIncomingData(data) {
12772
13013
  this.incomingBuffer = Buffer.concat([this.incomingBuffer, data]);
12773
13014
  logger.debug(`[ProtobufCommunicator] Processing buffer, length: ${this.incomingBuffer.length}`);
@@ -12825,6 +13066,22 @@ try {
12825
13066
  error: mockResponse.error || "Mock not found"
12826
13067
  });
12827
13068
  }
13069
+ if (message.payload.oneofKind === "envVarResponse") {
13070
+ const envVarResponse = message.payload.envVarResponse;
13071
+ logger.debug(`[ProtobufCommunicator] Received env var response for requestId: ${requestId}`);
13072
+ const pendingRequest = this.pendingRequests.get(requestId);
13073
+ if (!pendingRequest) {
13074
+ logger.warn("[ProtobufCommunicator] received env var response for unknown request:", requestId);
13075
+ return;
13076
+ }
13077
+ this.pendingRequests.delete(requestId);
13078
+ const envVars = {};
13079
+ if (envVarResponse?.envVars) Object.entries(envVarResponse.envVars).forEach(([key, value]) => {
13080
+ envVars[key] = value;
13081
+ });
13082
+ pendingRequest.resolve(envVars);
13083
+ return;
13084
+ }
12828
13085
  }
12829
13086
  /**
12830
13087
  * Extract response data from MockResponse
@@ -12867,6 +13124,95 @@ try {
12867
13124
  }
12868
13125
  }
12869
13126
  };
13127
+ ProtobufCommunicator.SYNC_CHILD_SCRIPT = `
13128
+ const net = require('net');
13129
+ const fs = require('fs');
13130
+
13131
+ const requestFile = process.argv[2];
13132
+ const responseFile = process.argv[3];
13133
+ const config = JSON.parse(process.argv[4]);
13134
+
13135
+ let responseReceived = false;
13136
+ let timeoutId;
13137
+
13138
+ function cleanup(exitCode) {
13139
+ if (timeoutId) clearTimeout(timeoutId);
13140
+ process.exit(exitCode);
13141
+ }
13142
+
13143
+ try {
13144
+ // Read the request data
13145
+ const requestData = fs.readFileSync(requestFile);
13146
+
13147
+ // Create connection based on config
13148
+ const client = config.type === 'unix'
13149
+ ? net.createConnection({ path: config.path })
13150
+ : net.createConnection({ host: config.host, port: config.port });
13151
+
13152
+ const incomingChunks = [];
13153
+ let incomingBuffer = Buffer.alloc(0);
13154
+
13155
+ // Set timeout
13156
+ timeoutId = setTimeout(() => {
13157
+ if (!responseReceived) {
13158
+ console.error('Timeout waiting for response');
13159
+ client.destroy();
13160
+ cleanup(1);
13161
+ }
13162
+ }, 10000);
13163
+
13164
+ client.on('connect', () => {
13165
+ // Send the request
13166
+ client.write(requestData);
13167
+ });
13168
+
13169
+ client.on('data', (data) => {
13170
+ incomingBuffer = Buffer.concat([incomingBuffer, data]);
13171
+
13172
+ // Try to parse complete message (4 byte length prefix + message)
13173
+ while (incomingBuffer.length >= 4) {
13174
+ const messageLength = incomingBuffer.readUInt32BE(0);
13175
+
13176
+ if (incomingBuffer.length < 4 + messageLength) {
13177
+ // Incomplete message, wait for more data
13178
+ break;
13179
+ }
13180
+
13181
+ // We have a complete message
13182
+ const messageData = incomingBuffer.slice(4, 4 + messageLength);
13183
+ incomingBuffer = incomingBuffer.slice(4 + messageLength);
13184
+
13185
+ // Write the complete response (including length prefix)
13186
+ const lengthPrefix = Buffer.allocUnsafe(4);
13187
+ lengthPrefix.writeUInt32BE(messageLength, 0);
13188
+ fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
13189
+
13190
+ responseReceived = true;
13191
+ client.destroy();
13192
+ cleanup(0);
13193
+ break;
13194
+ }
13195
+ });
13196
+
13197
+ client.on('error', (err) => {
13198
+ if (!responseReceived) {
13199
+ console.error('Connection error:', err.message);
13200
+ cleanup(1);
13201
+ }
13202
+ });
13203
+
13204
+ client.on('close', () => {
13205
+ if (!responseReceived) {
13206
+ console.error('Connection closed without response');
13207
+ cleanup(1);
13208
+ }
13209
+ });
13210
+
13211
+ } catch (err) {
13212
+ console.error('Script error:', err.message);
13213
+ cleanup(1);
13214
+ }
13215
+ `;
12870
13216
 
12871
13217
  //#endregion
12872
13218
  //#region src/core/TuskDriftInstrumentationModuleNames.ts
@@ -13047,6 +13393,10 @@ var TuskDriftCore = class TuskDriftCore {
13047
13393
  enabled: true,
13048
13394
  mode: this.mode
13049
13395
  });
13396
+ new PrismaInstrumentation({
13397
+ enabled: true,
13398
+ mode: this.mode
13399
+ });
13050
13400
  }
13051
13401
  initializeTracing({ baseDirectory }) {
13052
13402
  const serviceName = this.config.service?.name || "unknown";
@@ -13215,6 +13565,30 @@ var TuskDriftCore = class TuskDriftCore {
13215
13565
  };
13216
13566
  }
13217
13567
  }
13568
+ /**
13569
+ * Request environment variables from CLI for a specific trace (synchronously).
13570
+ * This blocks the main thread, so it should be used carefully.
13571
+ */
13572
+ requestEnvVarsSync(traceTestServerSpanId) {
13573
+ if (!this.isConnectedWithCLI) {
13574
+ logger.error("Requesting sync env vars but CLI is not ready yet");
13575
+ throw new Error("Requesting sync env vars but CLI is not ready yet");
13576
+ }
13577
+ if (!this.communicator || this.mode !== TuskDriftMode.REPLAY) {
13578
+ logger.debug("Cannot request env vars: not in replay mode or no CLI connection");
13579
+ return {};
13580
+ }
13581
+ try {
13582
+ logger.debug(`Requesting env vars (sync) for trace: ${traceTestServerSpanId}`);
13583
+ const envVars = this.communicator.requestEnvVarsSync(traceTestServerSpanId);
13584
+ logger.debug(`Received env vars from CLI, count: ${Object.keys(envVars).length}`);
13585
+ logger.debug(`First 10 env vars: ${JSON.stringify(Object.keys(envVars).slice(0, 10), null, 2)}`);
13586
+ return envVars;
13587
+ } catch (error) {
13588
+ logger.error(`[TuskDrift] Error requesting env vars from CLI:`, error);
13589
+ return {};
13590
+ }
13591
+ }
13218
13592
  requestMockSync(mockRequest) {
13219
13593
  if (!this.isConnectedWithCLI) {
13220
13594
  logger.error("Requesting sync mock but CLI is not ready yet");
@@ -13269,6 +13643,9 @@ var TuskDriftCore = class TuskDriftCore {
13269
13643
  getTracer() {
13270
13644
  return trace.getTracer(TD_INSTRUMENTATION_LIBRARY_NAME);
13271
13645
  }
13646
+ getProtobufCommunicator() {
13647
+ return this.communicator;
13648
+ }
13272
13649
  };
13273
13650
  var TuskDriftSDK = class {
13274
13651
  constructor() {