@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/README.md +1 -0
- package/dist/index.cjs +608 -231
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +609 -232
- 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.11";
|
|
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
|
|
@@ -2060,7 +2043,7 @@ var HttpReplayHooks = class {
|
|
|
2060
2043
|
auth: requestOptions.auth || void 0,
|
|
2061
2044
|
agent: requestOptions.agent || void 0,
|
|
2062
2045
|
protocol,
|
|
2063
|
-
hostname: requestOptions.hostname || void 0,
|
|
2046
|
+
hostname: requestOptions.hostname || requestOptions.host || void 0,
|
|
2064
2047
|
port: requestOptions.port ? Number(requestOptions.port) : void 0,
|
|
2065
2048
|
method
|
|
2066
2049
|
};
|
|
@@ -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, () => {
|
|
@@ -3029,13 +3017,29 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
3029
3017
|
readable: res.readable
|
|
3030
3018
|
};
|
|
3031
3019
|
const responseChunks = [];
|
|
3032
|
-
|
|
3020
|
+
let streamConsumptionMode = "NOT_CONSUMING";
|
|
3021
|
+
const originalRead = res.read?.bind(res);
|
|
3022
|
+
if (originalRead) res.read = function read(size) {
|
|
3023
|
+
const chunk = originalRead(size);
|
|
3024
|
+
if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
3025
|
+
streamConsumptionMode = "READ";
|
|
3026
|
+
responseChunks.push(Buffer.from(chunk));
|
|
3027
|
+
}
|
|
3028
|
+
return chunk;
|
|
3029
|
+
};
|
|
3030
|
+
res.once("resume", () => {
|
|
3033
3031
|
res.on("data", (chunk) => {
|
|
3034
|
-
|
|
3032
|
+
if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
3033
|
+
streamConsumptionMode = "PIPE";
|
|
3034
|
+
responseChunks.push(Buffer.from(chunk));
|
|
3035
|
+
}
|
|
3035
3036
|
});
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3037
|
+
});
|
|
3038
|
+
res.on("end", async (chunk) => {
|
|
3039
|
+
if (chunk && typeof chunk !== "function") responseChunks.push(Buffer.from(chunk));
|
|
3040
|
+
try {
|
|
3041
|
+
if (responseChunks.length > 0) {
|
|
3042
|
+
const responseBuffer = Buffer.concat(responseChunks);
|
|
3039
3043
|
const rawHeaders = this._captureHeadersFromRawHeaders(res.rawHeaders);
|
|
3040
3044
|
outputValue.headers = rawHeaders;
|
|
3041
3045
|
const contentEncoding = rawHeaders["content-encoding"];
|
|
@@ -3044,40 +3048,24 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
3044
3048
|
contentEncoding
|
|
3045
3049
|
});
|
|
3046
3050
|
outputValue.bodySize = responseBuffer.length;
|
|
3047
|
-
this._addOutputAttributesToSpan({
|
|
3048
|
-
spanInfo,
|
|
3049
|
-
outputValue,
|
|
3050
|
-
statusCode: res.statusCode || 1,
|
|
3051
|
-
outputSchemaMerges: {
|
|
3052
|
-
body: {
|
|
3053
|
-
encoding: __use_tusk_drift_schemas_core_json_schema.EncodingType.BASE64,
|
|
3054
|
-
decodedType: getDecodedType(outputValue.headers["content-type"] || "")
|
|
3055
|
-
},
|
|
3056
|
-
headers: { matchImportance: 0 }
|
|
3057
|
-
},
|
|
3058
|
-
inputValue: completeInputValue
|
|
3059
|
-
});
|
|
3060
|
-
} catch (error) {
|
|
3061
|
-
logger.error(`[HttpInstrumentation] Error processing response body:`, error);
|
|
3062
3051
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3052
|
+
this._addOutputAttributesToSpan({
|
|
3053
|
+
spanInfo,
|
|
3054
|
+
outputValue,
|
|
3055
|
+
statusCode: res.statusCode || 1,
|
|
3056
|
+
outputSchemaMerges: {
|
|
3057
|
+
body: {
|
|
3058
|
+
encoding: __use_tusk_drift_schemas_core_json_schema.EncodingType.BASE64,
|
|
3059
|
+
decodedType: getDecodedType(outputValue.headers["content-type"] || "")
|
|
3060
|
+
},
|
|
3061
|
+
headers: { matchImportance: 0 }
|
|
3073
3062
|
},
|
|
3074
|
-
|
|
3075
|
-
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
}
|
|
3063
|
+
inputValue: completeInputValue
|
|
3064
|
+
});
|
|
3065
|
+
} catch (error) {
|
|
3066
|
+
logger.error(`[HttpInstrumentation] Error processing response body:`, error);
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3081
3069
|
});
|
|
3082
3070
|
req.on("error", (error) => {
|
|
3083
3071
|
try {
|
|
@@ -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, () => {
|
|
@@ -9940,6 +9934,239 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
|
|
|
9940
9934
|
}
|
|
9941
9935
|
};
|
|
9942
9936
|
|
|
9937
|
+
//#endregion
|
|
9938
|
+
//#region src/instrumentation/libraries/prisma/types.ts
|
|
9939
|
+
/**
|
|
9940
|
+
* Prisma error class names for proper error handling
|
|
9941
|
+
*/
|
|
9942
|
+
let PrismaErrorClassName = /* @__PURE__ */ function(PrismaErrorClassName$1) {
|
|
9943
|
+
PrismaErrorClassName$1["PrismaClientKnownRequestError"] = "PrismaClientKnownRequestError";
|
|
9944
|
+
PrismaErrorClassName$1["PrismaClientUnknownRequestError"] = "PrismaClientUnknownRequestError";
|
|
9945
|
+
PrismaErrorClassName$1["PrismaClientInitializationError"] = "PrismaClientInitializationError";
|
|
9946
|
+
PrismaErrorClassName$1["PrismaClientValidationError"] = "PrismaClientValidationError";
|
|
9947
|
+
PrismaErrorClassName$1["PrismaClientRustPanicError"] = "PrismaClientRustPanicError";
|
|
9948
|
+
PrismaErrorClassName$1["NotFoundError"] = "NotFoundError";
|
|
9949
|
+
return PrismaErrorClassName$1;
|
|
9950
|
+
}({});
|
|
9951
|
+
|
|
9952
|
+
//#endregion
|
|
9953
|
+
//#region src/instrumentation/libraries/prisma/Instrumentation.ts
|
|
9954
|
+
var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
9955
|
+
constructor(config = {}) {
|
|
9956
|
+
super("@prisma/client", config);
|
|
9957
|
+
this.INSTRUMENTATION_NAME = "PrismaInstrumentation";
|
|
9958
|
+
this.prismaErrorClasses = [];
|
|
9959
|
+
this.mode = config.mode || TuskDriftMode.DISABLED;
|
|
9960
|
+
this.tuskDrift = TuskDriftCore.getInstance();
|
|
9961
|
+
}
|
|
9962
|
+
init() {
|
|
9963
|
+
return [new TdInstrumentationNodeModule({
|
|
9964
|
+
name: "@prisma/client",
|
|
9965
|
+
supportedVersions: ["5.*", "6.*"],
|
|
9966
|
+
patch: (moduleExports) => this._patchPrismaModule(moduleExports)
|
|
9967
|
+
})];
|
|
9968
|
+
}
|
|
9969
|
+
_patchPrismaModule(prismaModule) {
|
|
9970
|
+
if (this.isModulePatched(prismaModule)) {
|
|
9971
|
+
logger.debug(`[PrismaInstrumentation] Prisma module already patched, skipping`);
|
|
9972
|
+
return prismaModule;
|
|
9973
|
+
}
|
|
9974
|
+
this._storePrismaErrorClasses(prismaModule);
|
|
9975
|
+
logger.debug(`[PrismaInstrumentation] Wrapping PrismaClient constructor`);
|
|
9976
|
+
this._wrap(prismaModule, "PrismaClient", (OriginalPrismaClient) => {
|
|
9977
|
+
const self = this;
|
|
9978
|
+
logger.debug(`[PrismaInstrumentation] PrismaClient wrapper called`);
|
|
9979
|
+
return class TdPrismaClient {
|
|
9980
|
+
constructor(...args) {
|
|
9981
|
+
logger.debug(`[PrismaInstrumentation] Creating patched PrismaClient instance`);
|
|
9982
|
+
return new OriginalPrismaClient(...args).$extends({ query: { async $allOperations({ model, operation, args: operationArgs, query }) {
|
|
9983
|
+
logger.debug(`[PrismaInstrumentation] $allOperations intercepted: ${model}.${operation}`);
|
|
9984
|
+
return self._handlePrismaOperation({
|
|
9985
|
+
model,
|
|
9986
|
+
operation,
|
|
9987
|
+
args: operationArgs,
|
|
9988
|
+
query
|
|
9989
|
+
});
|
|
9990
|
+
} } });
|
|
9991
|
+
}
|
|
9992
|
+
};
|
|
9993
|
+
});
|
|
9994
|
+
this.markModuleAsPatched(prismaModule);
|
|
9995
|
+
logger.debug(`[PrismaInstrumentation] Prisma module patching complete`);
|
|
9996
|
+
return prismaModule;
|
|
9997
|
+
}
|
|
9998
|
+
_storePrismaErrorClasses(moduleExports) {
|
|
9999
|
+
const prismaNamespace = moduleExports.Prisma || {};
|
|
10000
|
+
this.prismaErrorClasses = [
|
|
10001
|
+
{
|
|
10002
|
+
name: PrismaErrorClassName.PrismaClientKnownRequestError,
|
|
10003
|
+
errorClass: moduleExports.PrismaClientKnownRequestError || prismaNamespace.PrismaClientKnownRequestError
|
|
10004
|
+
},
|
|
10005
|
+
{
|
|
10006
|
+
name: PrismaErrorClassName.PrismaClientUnknownRequestError,
|
|
10007
|
+
errorClass: moduleExports.PrismaClientUnknownRequestError || prismaNamespace.PrismaClientUnknownRequestError
|
|
10008
|
+
},
|
|
10009
|
+
{
|
|
10010
|
+
name: PrismaErrorClassName.PrismaClientInitializationError,
|
|
10011
|
+
errorClass: moduleExports.PrismaClientInitializationError || prismaNamespace.PrismaClientInitializationError
|
|
10012
|
+
},
|
|
10013
|
+
{
|
|
10014
|
+
name: PrismaErrorClassName.PrismaClientValidationError,
|
|
10015
|
+
errorClass: moduleExports.PrismaClientValidationError || prismaNamespace.PrismaClientValidationError
|
|
10016
|
+
},
|
|
10017
|
+
{
|
|
10018
|
+
name: PrismaErrorClassName.PrismaClientRustPanicError,
|
|
10019
|
+
errorClass: moduleExports.PrismaClientRustPanicError || prismaNamespace.PrismaClientRustPanicError
|
|
10020
|
+
},
|
|
10021
|
+
{
|
|
10022
|
+
name: PrismaErrorClassName.NotFoundError,
|
|
10023
|
+
errorClass: moduleExports.NotFoundError || prismaNamespace.NotFoundError
|
|
10024
|
+
}
|
|
10025
|
+
];
|
|
10026
|
+
}
|
|
10027
|
+
_handlePrismaOperation({ model, operation, args, query }) {
|
|
10028
|
+
const inputValue = {
|
|
10029
|
+
model,
|
|
10030
|
+
operation,
|
|
10031
|
+
args
|
|
10032
|
+
};
|
|
10033
|
+
logger.debug(`[PrismaInstrumentation] Intercepted Prisma operation: ${model}.${operation} in ${this.mode} mode`);
|
|
10034
|
+
if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
10035
|
+
originalFunctionCall: () => query(args),
|
|
10036
|
+
recordModeHandler: ({ isPreAppStart }) => {
|
|
10037
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
|
|
10038
|
+
name: `prisma.${operation}`,
|
|
10039
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10040
|
+
submodule: model,
|
|
10041
|
+
packageType: __use_tusk_drift_schemas_core_span.PackageType.UNSPECIFIED,
|
|
10042
|
+
packageName: "@prisma/client",
|
|
10043
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10044
|
+
inputValue,
|
|
10045
|
+
isPreAppStart
|
|
10046
|
+
}, (spanInfo) => {
|
|
10047
|
+
return this._handleRecordPrismaOperation(spanInfo, query, args);
|
|
10048
|
+
});
|
|
10049
|
+
},
|
|
10050
|
+
spanKind: __opentelemetry_api.SpanKind.CLIENT
|
|
10051
|
+
});
|
|
10052
|
+
else if (this.mode === TuskDriftMode.REPLAY) {
|
|
10053
|
+
const stackTrace = captureStackTrace(["PrismaInstrumentation"]);
|
|
10054
|
+
return handleReplayMode({
|
|
10055
|
+
noOpRequestHandler: () => query(args),
|
|
10056
|
+
isServerRequest: false,
|
|
10057
|
+
replayModeHandler: () => {
|
|
10058
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
|
|
10059
|
+
name: `prisma.${operation}`,
|
|
10060
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10061
|
+
submodule: model,
|
|
10062
|
+
packageType: __use_tusk_drift_schemas_core_span.PackageType.UNSPECIFIED,
|
|
10063
|
+
packageName: "@prisma/client",
|
|
10064
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10065
|
+
inputValue,
|
|
10066
|
+
isPreAppStart: false
|
|
10067
|
+
}, (spanInfo) => {
|
|
10068
|
+
return this._handleReplayPrismaOperation(spanInfo, inputValue, stackTrace);
|
|
10069
|
+
});
|
|
10070
|
+
}
|
|
10071
|
+
});
|
|
10072
|
+
} else return query(args);
|
|
10073
|
+
}
|
|
10074
|
+
async _handleRecordPrismaOperation(spanInfo, query, args) {
|
|
10075
|
+
try {
|
|
10076
|
+
logger.debug(`[PrismaInstrumentation] Recording Prisma operation`);
|
|
10077
|
+
const result = await query(args);
|
|
10078
|
+
const outputValue = {
|
|
10079
|
+
prismaResult: result,
|
|
10080
|
+
_tdOriginalFormat: "result"
|
|
10081
|
+
};
|
|
10082
|
+
try {
|
|
10083
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
10084
|
+
SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
|
|
10085
|
+
} catch (spanError) {
|
|
10086
|
+
logger.error(`[PrismaInstrumentation] error adding span attributes:`, spanError);
|
|
10087
|
+
}
|
|
10088
|
+
return result;
|
|
10089
|
+
} catch (error) {
|
|
10090
|
+
logger.debug(`[PrismaInstrumentation] Prisma operation error: ${error.message}`);
|
|
10091
|
+
try {
|
|
10092
|
+
const errorClassName = this._getPrismaErrorClassName(error);
|
|
10093
|
+
const errorWithClassName = this._cloneError(error);
|
|
10094
|
+
if (errorClassName) errorWithClassName.customTdName = errorClassName;
|
|
10095
|
+
const outputValue = {
|
|
10096
|
+
prismaResult: errorWithClassName,
|
|
10097
|
+
_tdOriginalFormat: "error"
|
|
10098
|
+
};
|
|
10099
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
10100
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
10101
|
+
code: __opentelemetry_api.SpanStatusCode.ERROR,
|
|
10102
|
+
message: error.message
|
|
10103
|
+
});
|
|
10104
|
+
} catch (spanError) {
|
|
10105
|
+
logger.error(`[PrismaInstrumentation] error extracting error and adding span attributes:`, spanError);
|
|
10106
|
+
}
|
|
10107
|
+
throw error;
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
10110
|
+
async _handleReplayPrismaOperation(spanInfo, inputValue, stackTrace) {
|
|
10111
|
+
const mockData = await findMockResponseAsync({
|
|
10112
|
+
mockRequestData: {
|
|
10113
|
+
traceId: spanInfo.traceId,
|
|
10114
|
+
spanId: spanInfo.spanId,
|
|
10115
|
+
name: `prisma.${inputValue.operation}`,
|
|
10116
|
+
inputValue,
|
|
10117
|
+
packageName: "@prisma/client",
|
|
10118
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10119
|
+
submoduleName: inputValue.model,
|
|
10120
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10121
|
+
stackTrace
|
|
10122
|
+
},
|
|
10123
|
+
tuskDrift: this.tuskDrift
|
|
10124
|
+
});
|
|
10125
|
+
if (!mockData) {
|
|
10126
|
+
logger.warn(`[PrismaInstrumentation] No mock data found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10127
|
+
throw new Error(`[PrismaInstrumentation] No matching mock found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10128
|
+
}
|
|
10129
|
+
logger.debug(`[PrismaInstrumentation] Found mock data for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10130
|
+
const outputValue = mockData.result;
|
|
10131
|
+
if (outputValue._tdOriginalFormat === "error") {
|
|
10132
|
+
const errorObj = outputValue.prismaResult;
|
|
10133
|
+
if (errorObj.customTdName) {
|
|
10134
|
+
const errorClass = this._getPrismaErrorClassFromName(errorObj.customTdName);
|
|
10135
|
+
if (errorClass) Object.setPrototypeOf(errorObj, errorClass.prototype);
|
|
10136
|
+
}
|
|
10137
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
10138
|
+
code: __opentelemetry_api.SpanStatusCode.ERROR,
|
|
10139
|
+
message: errorObj.message || "Prisma error"
|
|
10140
|
+
});
|
|
10141
|
+
throw errorObj;
|
|
10142
|
+
}
|
|
10143
|
+
SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
|
|
10144
|
+
return outputValue.prismaResult;
|
|
10145
|
+
}
|
|
10146
|
+
_getPrismaErrorClassName(error) {
|
|
10147
|
+
for (const errorInfo of this.prismaErrorClasses) if (error instanceof errorInfo.errorClass) return errorInfo.name;
|
|
10148
|
+
}
|
|
10149
|
+
_getPrismaErrorClassFromName(className) {
|
|
10150
|
+
for (const errorInfo of this.prismaErrorClasses) if (errorInfo.name === className) return errorInfo.errorClass;
|
|
10151
|
+
return null;
|
|
10152
|
+
}
|
|
10153
|
+
/**
|
|
10154
|
+
* Deep clone an error object to make it serializable
|
|
10155
|
+
*/
|
|
10156
|
+
_cloneError(error) {
|
|
10157
|
+
const cloned = new Error(error.message);
|
|
10158
|
+
cloned.name = error.name;
|
|
10159
|
+
cloned.stack = error.stack;
|
|
10160
|
+
for (const key in error) if (error.hasOwnProperty(key)) try {
|
|
10161
|
+
cloned[key] = error[key];
|
|
10162
|
+
} catch (e) {}
|
|
10163
|
+
return cloned;
|
|
10164
|
+
}
|
|
10165
|
+
_wrap(target, propertyName, wrapper) {
|
|
10166
|
+
wrap(target, propertyName, wrapper);
|
|
10167
|
+
}
|
|
10168
|
+
};
|
|
10169
|
+
|
|
9943
10170
|
//#endregion
|
|
9944
10171
|
//#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
|
|
9945
10172
|
var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
|
|
@@ -12440,9 +12667,18 @@ var TdSpanExporter = class {
|
|
|
12440
12667
|
}
|
|
12441
12668
|
};
|
|
12442
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
|
+
|
|
12443
12679
|
//#endregion
|
|
12444
12680
|
//#region src/core/ProtobufCommunicator.ts
|
|
12445
|
-
var ProtobufCommunicator = class {
|
|
12681
|
+
var ProtobufCommunicator = class ProtobufCommunicator {
|
|
12446
12682
|
constructor() {
|
|
12447
12683
|
this.client = null;
|
|
12448
12684
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -12540,43 +12776,18 @@ var ProtobufCommunicator = class {
|
|
|
12540
12776
|
});
|
|
12541
12777
|
}
|
|
12542
12778
|
/**
|
|
12543
|
-
*
|
|
12544
|
-
*
|
|
12545
|
-
*
|
|
12546
|
-
*
|
|
12547
|
-
* Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
|
|
12548
|
-
* 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
|
|
12549
12783
|
*/
|
|
12550
|
-
|
|
12551
|
-
const requestId =
|
|
12552
|
-
const cleanSpan = mockRequest.outboundSpan ? this.cleanSpan(mockRequest.outboundSpan) : void 0;
|
|
12553
|
-
if (cleanSpan?.inputValue) cleanSpan.inputValue = objectToProtobufStruct(cleanSpan.inputValue);
|
|
12554
|
-
if (cleanSpan?.inputSchema) cleanSpan.inputSchema = objectToProtobufStruct(cleanSpan.inputSchema);
|
|
12555
|
-
if (cleanSpan?.kind) cleanSpan.kind = mapOtToPb(cleanSpan.kind);
|
|
12556
|
-
const protoMockRequest = __use_tusk_drift_schemas_core_communication.GetMockRequest.create({
|
|
12557
|
-
...mockRequest,
|
|
12558
|
-
requestId,
|
|
12559
|
-
tags: {},
|
|
12560
|
-
outboundSpan: cleanSpan,
|
|
12561
|
-
stackTrace: cleanSpan?.stackTrace
|
|
12562
|
-
});
|
|
12563
|
-
const sdkMessage = __use_tusk_drift_schemas_core_communication.SDKMessage.create({
|
|
12564
|
-
type: __use_tusk_drift_schemas_core_communication.MessageType.MOCK_REQUEST,
|
|
12565
|
-
requestId,
|
|
12566
|
-
payload: {
|
|
12567
|
-
oneofKind: "getMockRequest",
|
|
12568
|
-
getMockRequest: protoMockRequest
|
|
12569
|
-
}
|
|
12570
|
-
});
|
|
12571
|
-
logger.debug("Sending protobuf request to CLI (sync)", {
|
|
12572
|
-
outboundSpan: mockRequest.outboundSpan,
|
|
12573
|
-
testId: mockRequest.testId
|
|
12574
|
-
});
|
|
12784
|
+
executeSyncRequest(sdkMessage, filePrefix, responseHandler) {
|
|
12785
|
+
const requestId = sdkMessage.requestId;
|
|
12575
12786
|
const messageBytes = __use_tusk_drift_schemas_core_communication.SDKMessage.toBinary(sdkMessage);
|
|
12576
12787
|
const tempDir = os.default.tmpdir();
|
|
12577
|
-
const requestFile = path.default.join(tempDir, `tusk-sync-request-${requestId}.bin`);
|
|
12578
|
-
const responseFile = path.default.join(tempDir, `tusk-sync-response-${requestId}.bin`);
|
|
12579
|
-
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`);
|
|
12580
12791
|
try {
|
|
12581
12792
|
const lengthBuffer = Buffer.allocUnsafe(4);
|
|
12582
12793
|
lengthBuffer.writeUInt32BE(messageBytes.length, 0);
|
|
@@ -12599,95 +12810,7 @@ var ProtobufCommunicator = class {
|
|
|
12599
12810
|
type: "unix",
|
|
12600
12811
|
path: path.default.join(os.default.tmpdir(), "tusk-connect.sock")
|
|
12601
12812
|
};
|
|
12602
|
-
fs.default.writeFileSync(scriptFile,
|
|
12603
|
-
const net = require('net');
|
|
12604
|
-
const fs = require('fs');
|
|
12605
|
-
|
|
12606
|
-
const requestFile = process.argv[2];
|
|
12607
|
-
const responseFile = process.argv[3];
|
|
12608
|
-
const config = JSON.parse(process.argv[4]);
|
|
12609
|
-
|
|
12610
|
-
let responseReceived = false;
|
|
12611
|
-
let timeoutId;
|
|
12612
|
-
|
|
12613
|
-
function cleanup(exitCode) {
|
|
12614
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
12615
|
-
process.exit(exitCode);
|
|
12616
|
-
}
|
|
12617
|
-
|
|
12618
|
-
try {
|
|
12619
|
-
// Read the request data
|
|
12620
|
-
const requestData = fs.readFileSync(requestFile);
|
|
12621
|
-
|
|
12622
|
-
// Create connection based on config
|
|
12623
|
-
const client = config.type === 'unix'
|
|
12624
|
-
? net.createConnection({ path: config.path })
|
|
12625
|
-
: net.createConnection({ host: config.host, port: config.port });
|
|
12626
|
-
|
|
12627
|
-
const incomingChunks = [];
|
|
12628
|
-
let incomingBuffer = Buffer.alloc(0);
|
|
12629
|
-
|
|
12630
|
-
// Set timeout
|
|
12631
|
-
timeoutId = setTimeout(() => {
|
|
12632
|
-
if (!responseReceived) {
|
|
12633
|
-
console.error('Timeout waiting for response');
|
|
12634
|
-
client.destroy();
|
|
12635
|
-
cleanup(1);
|
|
12636
|
-
}
|
|
12637
|
-
}, 10000);
|
|
12638
|
-
|
|
12639
|
-
client.on('connect', () => {
|
|
12640
|
-
// Send the request
|
|
12641
|
-
client.write(requestData);
|
|
12642
|
-
});
|
|
12643
|
-
|
|
12644
|
-
client.on('data', (data) => {
|
|
12645
|
-
incomingBuffer = Buffer.concat([incomingBuffer, data]);
|
|
12646
|
-
|
|
12647
|
-
// Try to parse complete message (4 byte length prefix + message)
|
|
12648
|
-
while (incomingBuffer.length >= 4) {
|
|
12649
|
-
const messageLength = incomingBuffer.readUInt32BE(0);
|
|
12650
|
-
|
|
12651
|
-
if (incomingBuffer.length < 4 + messageLength) {
|
|
12652
|
-
// Incomplete message, wait for more data
|
|
12653
|
-
break;
|
|
12654
|
-
}
|
|
12655
|
-
|
|
12656
|
-
// We have a complete message
|
|
12657
|
-
const messageData = incomingBuffer.slice(4, 4 + messageLength);
|
|
12658
|
-
incomingBuffer = incomingBuffer.slice(4 + messageLength);
|
|
12659
|
-
|
|
12660
|
-
// Write the complete response (including length prefix)
|
|
12661
|
-
const lengthPrefix = Buffer.allocUnsafe(4);
|
|
12662
|
-
lengthPrefix.writeUInt32BE(messageLength, 0);
|
|
12663
|
-
fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
|
|
12664
|
-
|
|
12665
|
-
responseReceived = true;
|
|
12666
|
-
client.destroy();
|
|
12667
|
-
cleanup(0);
|
|
12668
|
-
break;
|
|
12669
|
-
}
|
|
12670
|
-
});
|
|
12671
|
-
|
|
12672
|
-
client.on('error', (err) => {
|
|
12673
|
-
if (!responseReceived) {
|
|
12674
|
-
console.error('Connection error:', err.message);
|
|
12675
|
-
cleanup(1);
|
|
12676
|
-
}
|
|
12677
|
-
});
|
|
12678
|
-
|
|
12679
|
-
client.on('close', () => {
|
|
12680
|
-
if (!responseReceived) {
|
|
12681
|
-
console.error('Connection closed without response');
|
|
12682
|
-
cleanup(1);
|
|
12683
|
-
}
|
|
12684
|
-
});
|
|
12685
|
-
|
|
12686
|
-
} catch (err) {
|
|
12687
|
-
console.error('Script error:', err.message);
|
|
12688
|
-
cleanup(1);
|
|
12689
|
-
}
|
|
12690
|
-
`);
|
|
12813
|
+
fs.default.writeFileSync(scriptFile, ProtobufCommunicator.SYNC_CHILD_SCRIPT);
|
|
12691
12814
|
try {
|
|
12692
12815
|
(0, child_process.execSync)(`node "${scriptFile}" "${requestFile}" "${responseFile}" '${JSON.stringify(connectionConfig)}'`, {
|
|
12693
12816
|
timeout: 12e3,
|
|
@@ -12699,27 +12822,13 @@ try {
|
|
|
12699
12822
|
if (responseBuffer.length < 4 + responseLength) throw new Error("Invalid response: incomplete message");
|
|
12700
12823
|
const responseData = responseBuffer.slice(4, 4 + responseLength);
|
|
12701
12824
|
const cliMessage = __use_tusk_drift_schemas_core_communication.CLIMessage.fromBinary(responseData);
|
|
12702
|
-
|
|
12703
|
-
const mockResponse = cliMessage.payload.getMockResponse;
|
|
12704
|
-
if (!mockResponse) throw new Error("No mock response received");
|
|
12705
|
-
if (mockResponse.found) try {
|
|
12706
|
-
return {
|
|
12707
|
-
found: true,
|
|
12708
|
-
response: this.extractResponseData(mockResponse)
|
|
12709
|
-
};
|
|
12710
|
-
} catch (error) {
|
|
12711
|
-
throw new Error(`Failed to extract response data: ${error}`);
|
|
12712
|
-
}
|
|
12713
|
-
else return {
|
|
12714
|
-
found: false,
|
|
12715
|
-
error: mockResponse.error || "Mock not found"
|
|
12716
|
-
};
|
|
12825
|
+
return responseHandler(cliMessage);
|
|
12717
12826
|
} catch (error) {
|
|
12718
|
-
logger.error(
|
|
12827
|
+
logger.error(`[ProtobufCommunicator] error in sync ${filePrefix} request child process:`, error);
|
|
12719
12828
|
throw error;
|
|
12720
12829
|
}
|
|
12721
12830
|
} catch (error) {
|
|
12722
|
-
throw new Error(`Sync request failed: ${error.message}`);
|
|
12831
|
+
throw new Error(`Sync ${filePrefix} request failed: ${error.message}`);
|
|
12723
12832
|
} finally {
|
|
12724
12833
|
try {
|
|
12725
12834
|
if (fs.default.existsSync(requestFile)) fs.default.unlinkSync(requestFile);
|
|
@@ -12738,6 +12847,86 @@ try {
|
|
|
12738
12847
|
}
|
|
12739
12848
|
}
|
|
12740
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
|
+
}
|
|
12741
12930
|
async sendProtobufMessage(message) {
|
|
12742
12931
|
if (!this.client || !this.protobufContext) throw new Error("Not connected to CLI");
|
|
12743
12932
|
const messageBytes = __use_tusk_drift_schemas_core_communication.SDKMessage.toBinary(message);
|
|
@@ -12790,6 +12979,58 @@ try {
|
|
|
12790
12979
|
});
|
|
12791
12980
|
await this.sendProtobufMessage(sdkMessage);
|
|
12792
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
|
+
}
|
|
12793
13034
|
handleIncomingData(data) {
|
|
12794
13035
|
this.incomingBuffer = Buffer.concat([this.incomingBuffer, data]);
|
|
12795
13036
|
logger.debug(`[ProtobufCommunicator] Processing buffer, length: ${this.incomingBuffer.length}`);
|
|
@@ -12847,6 +13088,22 @@ try {
|
|
|
12847
13088
|
error: mockResponse.error || "Mock not found"
|
|
12848
13089
|
});
|
|
12849
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
|
+
}
|
|
12850
13107
|
}
|
|
12851
13108
|
/**
|
|
12852
13109
|
* Extract response data from MockResponse
|
|
@@ -12889,6 +13146,95 @@ try {
|
|
|
12889
13146
|
}
|
|
12890
13147
|
}
|
|
12891
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
|
+
`;
|
|
12892
13238
|
|
|
12893
13239
|
//#endregion
|
|
12894
13240
|
//#region src/core/TuskDriftInstrumentationModuleNames.ts
|
|
@@ -13069,6 +13415,10 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
13069
13415
|
enabled: true,
|
|
13070
13416
|
mode: this.mode
|
|
13071
13417
|
});
|
|
13418
|
+
new PrismaInstrumentation({
|
|
13419
|
+
enabled: true,
|
|
13420
|
+
mode: this.mode
|
|
13421
|
+
});
|
|
13072
13422
|
}
|
|
13073
13423
|
initializeTracing({ baseDirectory }) {
|
|
13074
13424
|
const serviceName = this.config.service?.name || "unknown";
|
|
@@ -13237,6 +13587,30 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
13237
13587
|
};
|
|
13238
13588
|
}
|
|
13239
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
|
+
}
|
|
13240
13614
|
requestMockSync(mockRequest) {
|
|
13241
13615
|
if (!this.isConnectedWithCLI) {
|
|
13242
13616
|
logger.error("Requesting sync mock but CLI is not ready yet");
|
|
@@ -13291,6 +13665,9 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
13291
13665
|
getTracer() {
|
|
13292
13666
|
return __opentelemetry_api.trace.getTracer(TD_INSTRUMENTATION_LIBRARY_NAME);
|
|
13293
13667
|
}
|
|
13668
|
+
getProtobufCommunicator() {
|
|
13669
|
+
return this.communicator;
|
|
13670
|
+
}
|
|
13294
13671
|
};
|
|
13295
13672
|
var TuskDriftSDK = class {
|
|
13296
13673
|
constructor() {
|