@use-tusk/drift-node-sdk 0.1.11 → 0.1.12
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 +276 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +276 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Tusk Drift currently supports the following packages and versions:
|
|
|
28
28
|
- **MySQL**: `mysql2@3.x`
|
|
29
29
|
- **IORedis**: `ioredis@4.x-5.x`
|
|
30
30
|
- **GraphQL**: `graphql@15.x-16.x`
|
|
31
|
+
- **Prisma**: `prisma@5.x-6.x`
|
|
31
32
|
- **JSON Web Tokens**: `jsonwebtoken@5.x-9.x`
|
|
32
33
|
- **JWKS RSA**: `jwks-rsa@1.x-3.x`
|
|
33
34
|
|
package/dist/index.cjs
CHANGED
|
@@ -278,7 +278,7 @@ var TdInstrumentationAbstract = class {
|
|
|
278
278
|
|
|
279
279
|
//#endregion
|
|
280
280
|
//#region package.json
|
|
281
|
-
var version = "0.1.
|
|
281
|
+
var version = "0.1.12";
|
|
282
282
|
|
|
283
283
|
//#endregion
|
|
284
284
|
//#region src/version.ts
|
|
@@ -2060,7 +2060,7 @@ var HttpReplayHooks = class {
|
|
|
2060
2060
|
auth: requestOptions.auth || void 0,
|
|
2061
2061
|
agent: requestOptions.agent || void 0,
|
|
2062
2062
|
protocol,
|
|
2063
|
-
hostname: requestOptions.hostname || void 0,
|
|
2063
|
+
hostname: requestOptions.hostname || requestOptions.host || void 0,
|
|
2064
2064
|
port: requestOptions.port ? Number(requestOptions.port) : void 0,
|
|
2065
2065
|
method
|
|
2066
2066
|
};
|
|
@@ -3029,13 +3029,29 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
3029
3029
|
readable: res.readable
|
|
3030
3030
|
};
|
|
3031
3031
|
const responseChunks = [];
|
|
3032
|
-
|
|
3032
|
+
let streamConsumptionMode = "NOT_CONSUMING";
|
|
3033
|
+
const originalRead = res.read?.bind(res);
|
|
3034
|
+
if (originalRead) res.read = function read(size) {
|
|
3035
|
+
const chunk = originalRead(size);
|
|
3036
|
+
if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
3037
|
+
streamConsumptionMode = "READ";
|
|
3038
|
+
responseChunks.push(Buffer.from(chunk));
|
|
3039
|
+
}
|
|
3040
|
+
return chunk;
|
|
3041
|
+
};
|
|
3042
|
+
res.once("resume", () => {
|
|
3033
3043
|
res.on("data", (chunk) => {
|
|
3034
|
-
|
|
3044
|
+
if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
|
|
3045
|
+
streamConsumptionMode = "PIPE";
|
|
3046
|
+
responseChunks.push(Buffer.from(chunk));
|
|
3047
|
+
}
|
|
3035
3048
|
});
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3049
|
+
});
|
|
3050
|
+
res.on("end", async (chunk) => {
|
|
3051
|
+
if (chunk && typeof chunk !== "function") responseChunks.push(Buffer.from(chunk));
|
|
3052
|
+
try {
|
|
3053
|
+
if (responseChunks.length > 0) {
|
|
3054
|
+
const responseBuffer = Buffer.concat(responseChunks);
|
|
3039
3055
|
const rawHeaders = this._captureHeadersFromRawHeaders(res.rawHeaders);
|
|
3040
3056
|
outputValue.headers = rawHeaders;
|
|
3041
3057
|
const contentEncoding = rawHeaders["content-encoding"];
|
|
@@ -3044,40 +3060,24 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
|
|
|
3044
3060
|
contentEncoding
|
|
3045
3061
|
});
|
|
3046
3062
|
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
3063
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3064
|
+
this._addOutputAttributesToSpan({
|
|
3065
|
+
spanInfo,
|
|
3066
|
+
outputValue,
|
|
3067
|
+
statusCode: res.statusCode || 1,
|
|
3068
|
+
outputSchemaMerges: {
|
|
3069
|
+
body: {
|
|
3070
|
+
encoding: __use_tusk_drift_schemas_core_json_schema.EncodingType.BASE64,
|
|
3071
|
+
decodedType: getDecodedType(outputValue.headers["content-type"] || "")
|
|
3072
|
+
},
|
|
3073
|
+
headers: { matchImportance: 0 }
|
|
3073
3074
|
},
|
|
3074
|
-
|
|
3075
|
-
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
}
|
|
3075
|
+
inputValue: completeInputValue
|
|
3076
|
+
});
|
|
3077
|
+
} catch (error) {
|
|
3078
|
+
logger.error(`[HttpInstrumentation] Error processing response body:`, error);
|
|
3079
|
+
}
|
|
3080
|
+
});
|
|
3081
3081
|
});
|
|
3082
3082
|
req.on("error", (error) => {
|
|
3083
3083
|
try {
|
|
@@ -9940,6 +9940,239 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
|
|
|
9940
9940
|
}
|
|
9941
9941
|
};
|
|
9942
9942
|
|
|
9943
|
+
//#endregion
|
|
9944
|
+
//#region src/instrumentation/libraries/prisma/types.ts
|
|
9945
|
+
/**
|
|
9946
|
+
* Prisma error class names for proper error handling
|
|
9947
|
+
*/
|
|
9948
|
+
let PrismaErrorClassName = /* @__PURE__ */ function(PrismaErrorClassName$1) {
|
|
9949
|
+
PrismaErrorClassName$1["PrismaClientKnownRequestError"] = "PrismaClientKnownRequestError";
|
|
9950
|
+
PrismaErrorClassName$1["PrismaClientUnknownRequestError"] = "PrismaClientUnknownRequestError";
|
|
9951
|
+
PrismaErrorClassName$1["PrismaClientInitializationError"] = "PrismaClientInitializationError";
|
|
9952
|
+
PrismaErrorClassName$1["PrismaClientValidationError"] = "PrismaClientValidationError";
|
|
9953
|
+
PrismaErrorClassName$1["PrismaClientRustPanicError"] = "PrismaClientRustPanicError";
|
|
9954
|
+
PrismaErrorClassName$1["NotFoundError"] = "NotFoundError";
|
|
9955
|
+
return PrismaErrorClassName$1;
|
|
9956
|
+
}({});
|
|
9957
|
+
|
|
9958
|
+
//#endregion
|
|
9959
|
+
//#region src/instrumentation/libraries/prisma/Instrumentation.ts
|
|
9960
|
+
var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
9961
|
+
constructor(config = {}) {
|
|
9962
|
+
super("@prisma/client", config);
|
|
9963
|
+
this.INSTRUMENTATION_NAME = "PrismaInstrumentation";
|
|
9964
|
+
this.prismaErrorClasses = [];
|
|
9965
|
+
this.mode = config.mode || TuskDriftMode.DISABLED;
|
|
9966
|
+
this.tuskDrift = TuskDriftCore.getInstance();
|
|
9967
|
+
}
|
|
9968
|
+
init() {
|
|
9969
|
+
return [new TdInstrumentationNodeModule({
|
|
9970
|
+
name: "@prisma/client",
|
|
9971
|
+
supportedVersions: ["5.*", "6.*"],
|
|
9972
|
+
patch: (moduleExports) => this._patchPrismaModule(moduleExports)
|
|
9973
|
+
})];
|
|
9974
|
+
}
|
|
9975
|
+
_patchPrismaModule(prismaModule) {
|
|
9976
|
+
if (this.isModulePatched(prismaModule)) {
|
|
9977
|
+
logger.debug(`[PrismaInstrumentation] Prisma module already patched, skipping`);
|
|
9978
|
+
return prismaModule;
|
|
9979
|
+
}
|
|
9980
|
+
this._storePrismaErrorClasses(prismaModule);
|
|
9981
|
+
logger.debug(`[PrismaInstrumentation] Wrapping PrismaClient constructor`);
|
|
9982
|
+
this._wrap(prismaModule, "PrismaClient", (OriginalPrismaClient) => {
|
|
9983
|
+
const self = this;
|
|
9984
|
+
logger.debug(`[PrismaInstrumentation] PrismaClient wrapper called`);
|
|
9985
|
+
return class TdPrismaClient {
|
|
9986
|
+
constructor(...args) {
|
|
9987
|
+
logger.debug(`[PrismaInstrumentation] Creating patched PrismaClient instance`);
|
|
9988
|
+
return new OriginalPrismaClient(...args).$extends({ query: { async $allOperations({ model, operation, args: operationArgs, query }) {
|
|
9989
|
+
logger.debug(`[PrismaInstrumentation] $allOperations intercepted: ${model}.${operation}`);
|
|
9990
|
+
return self._handlePrismaOperation({
|
|
9991
|
+
model,
|
|
9992
|
+
operation,
|
|
9993
|
+
args: operationArgs,
|
|
9994
|
+
query
|
|
9995
|
+
});
|
|
9996
|
+
} } });
|
|
9997
|
+
}
|
|
9998
|
+
};
|
|
9999
|
+
});
|
|
10000
|
+
this.markModuleAsPatched(prismaModule);
|
|
10001
|
+
logger.debug(`[PrismaInstrumentation] Prisma module patching complete`);
|
|
10002
|
+
return prismaModule;
|
|
10003
|
+
}
|
|
10004
|
+
_storePrismaErrorClasses(moduleExports) {
|
|
10005
|
+
const prismaNamespace = moduleExports.Prisma || {};
|
|
10006
|
+
this.prismaErrorClasses = [
|
|
10007
|
+
{
|
|
10008
|
+
name: PrismaErrorClassName.PrismaClientKnownRequestError,
|
|
10009
|
+
errorClass: moduleExports.PrismaClientKnownRequestError || prismaNamespace.PrismaClientKnownRequestError
|
|
10010
|
+
},
|
|
10011
|
+
{
|
|
10012
|
+
name: PrismaErrorClassName.PrismaClientUnknownRequestError,
|
|
10013
|
+
errorClass: moduleExports.PrismaClientUnknownRequestError || prismaNamespace.PrismaClientUnknownRequestError
|
|
10014
|
+
},
|
|
10015
|
+
{
|
|
10016
|
+
name: PrismaErrorClassName.PrismaClientInitializationError,
|
|
10017
|
+
errorClass: moduleExports.PrismaClientInitializationError || prismaNamespace.PrismaClientInitializationError
|
|
10018
|
+
},
|
|
10019
|
+
{
|
|
10020
|
+
name: PrismaErrorClassName.PrismaClientValidationError,
|
|
10021
|
+
errorClass: moduleExports.PrismaClientValidationError || prismaNamespace.PrismaClientValidationError
|
|
10022
|
+
},
|
|
10023
|
+
{
|
|
10024
|
+
name: PrismaErrorClassName.PrismaClientRustPanicError,
|
|
10025
|
+
errorClass: moduleExports.PrismaClientRustPanicError || prismaNamespace.PrismaClientRustPanicError
|
|
10026
|
+
},
|
|
10027
|
+
{
|
|
10028
|
+
name: PrismaErrorClassName.NotFoundError,
|
|
10029
|
+
errorClass: moduleExports.NotFoundError || prismaNamespace.NotFoundError
|
|
10030
|
+
}
|
|
10031
|
+
];
|
|
10032
|
+
}
|
|
10033
|
+
_handlePrismaOperation({ model, operation, args, query }) {
|
|
10034
|
+
const inputValue = {
|
|
10035
|
+
model,
|
|
10036
|
+
operation,
|
|
10037
|
+
args
|
|
10038
|
+
};
|
|
10039
|
+
logger.debug(`[PrismaInstrumentation] Intercepted Prisma operation: ${model}.${operation} in ${this.mode} mode`);
|
|
10040
|
+
if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
10041
|
+
originalFunctionCall: () => query(args),
|
|
10042
|
+
recordModeHandler: ({ isPreAppStart }) => {
|
|
10043
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
|
|
10044
|
+
name: `prisma.${operation}`,
|
|
10045
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10046
|
+
submodule: model,
|
|
10047
|
+
packageType: __use_tusk_drift_schemas_core_span.PackageType.UNSPECIFIED,
|
|
10048
|
+
packageName: "@prisma/client",
|
|
10049
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10050
|
+
inputValue,
|
|
10051
|
+
isPreAppStart
|
|
10052
|
+
}, (spanInfo) => {
|
|
10053
|
+
return this._handleRecordPrismaOperation(spanInfo, query, args);
|
|
10054
|
+
});
|
|
10055
|
+
},
|
|
10056
|
+
spanKind: __opentelemetry_api.SpanKind.CLIENT
|
|
10057
|
+
});
|
|
10058
|
+
else if (this.mode === TuskDriftMode.REPLAY) {
|
|
10059
|
+
const stackTrace = captureStackTrace(["PrismaInstrumentation"]);
|
|
10060
|
+
return handleReplayMode({
|
|
10061
|
+
noOpRequestHandler: () => query(args),
|
|
10062
|
+
isServerRequest: false,
|
|
10063
|
+
replayModeHandler: () => {
|
|
10064
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
|
|
10065
|
+
name: `prisma.${operation}`,
|
|
10066
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10067
|
+
submodule: model,
|
|
10068
|
+
packageType: __use_tusk_drift_schemas_core_span.PackageType.UNSPECIFIED,
|
|
10069
|
+
packageName: "@prisma/client",
|
|
10070
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10071
|
+
inputValue,
|
|
10072
|
+
isPreAppStart: false
|
|
10073
|
+
}, (spanInfo) => {
|
|
10074
|
+
return this._handleReplayPrismaOperation(spanInfo, inputValue, stackTrace);
|
|
10075
|
+
});
|
|
10076
|
+
}
|
|
10077
|
+
});
|
|
10078
|
+
} else return query(args);
|
|
10079
|
+
}
|
|
10080
|
+
async _handleRecordPrismaOperation(spanInfo, query, args) {
|
|
10081
|
+
try {
|
|
10082
|
+
logger.debug(`[PrismaInstrumentation] Recording Prisma operation`);
|
|
10083
|
+
const result = await query(args);
|
|
10084
|
+
const outputValue = {
|
|
10085
|
+
prismaResult: result,
|
|
10086
|
+
_tdOriginalFormat: "result"
|
|
10087
|
+
};
|
|
10088
|
+
try {
|
|
10089
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
10090
|
+
SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
|
|
10091
|
+
} catch (spanError) {
|
|
10092
|
+
logger.error(`[PrismaInstrumentation] error adding span attributes:`, spanError);
|
|
10093
|
+
}
|
|
10094
|
+
return result;
|
|
10095
|
+
} catch (error) {
|
|
10096
|
+
logger.debug(`[PrismaInstrumentation] Prisma operation error: ${error.message}`);
|
|
10097
|
+
try {
|
|
10098
|
+
const errorClassName = this._getPrismaErrorClassName(error);
|
|
10099
|
+
const errorWithClassName = this._cloneError(error);
|
|
10100
|
+
if (errorClassName) errorWithClassName.customTdName = errorClassName;
|
|
10101
|
+
const outputValue = {
|
|
10102
|
+
prismaResult: errorWithClassName,
|
|
10103
|
+
_tdOriginalFormat: "error"
|
|
10104
|
+
};
|
|
10105
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
10106
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
10107
|
+
code: __opentelemetry_api.SpanStatusCode.ERROR,
|
|
10108
|
+
message: error.message
|
|
10109
|
+
});
|
|
10110
|
+
} catch (spanError) {
|
|
10111
|
+
logger.error(`[PrismaInstrumentation] error extracting error and adding span attributes:`, spanError);
|
|
10112
|
+
}
|
|
10113
|
+
throw error;
|
|
10114
|
+
}
|
|
10115
|
+
}
|
|
10116
|
+
async _handleReplayPrismaOperation(spanInfo, inputValue, stackTrace) {
|
|
10117
|
+
const mockData = await findMockResponseAsync({
|
|
10118
|
+
mockRequestData: {
|
|
10119
|
+
traceId: spanInfo.traceId,
|
|
10120
|
+
spanId: spanInfo.spanId,
|
|
10121
|
+
name: `prisma.${inputValue.operation}`,
|
|
10122
|
+
inputValue,
|
|
10123
|
+
packageName: "@prisma/client",
|
|
10124
|
+
instrumentationName: this.INSTRUMENTATION_NAME,
|
|
10125
|
+
submoduleName: inputValue.model,
|
|
10126
|
+
kind: __opentelemetry_api.SpanKind.CLIENT,
|
|
10127
|
+
stackTrace
|
|
10128
|
+
},
|
|
10129
|
+
tuskDrift: this.tuskDrift
|
|
10130
|
+
});
|
|
10131
|
+
if (!mockData) {
|
|
10132
|
+
logger.warn(`[PrismaInstrumentation] No mock data found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10133
|
+
throw new Error(`[PrismaInstrumentation] No matching mock found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10134
|
+
}
|
|
10135
|
+
logger.debug(`[PrismaInstrumentation] Found mock data for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
|
|
10136
|
+
const outputValue = mockData.result;
|
|
10137
|
+
if (outputValue._tdOriginalFormat === "error") {
|
|
10138
|
+
const errorObj = outputValue.prismaResult;
|
|
10139
|
+
if (errorObj.customTdName) {
|
|
10140
|
+
const errorClass = this._getPrismaErrorClassFromName(errorObj.customTdName);
|
|
10141
|
+
if (errorClass) Object.setPrototypeOf(errorObj, errorClass.prototype);
|
|
10142
|
+
}
|
|
10143
|
+
SpanUtils.endSpan(spanInfo.span, {
|
|
10144
|
+
code: __opentelemetry_api.SpanStatusCode.ERROR,
|
|
10145
|
+
message: errorObj.message || "Prisma error"
|
|
10146
|
+
});
|
|
10147
|
+
throw errorObj;
|
|
10148
|
+
}
|
|
10149
|
+
SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
|
|
10150
|
+
return outputValue.prismaResult;
|
|
10151
|
+
}
|
|
10152
|
+
_getPrismaErrorClassName(error) {
|
|
10153
|
+
for (const errorInfo of this.prismaErrorClasses) if (error instanceof errorInfo.errorClass) return errorInfo.name;
|
|
10154
|
+
}
|
|
10155
|
+
_getPrismaErrorClassFromName(className) {
|
|
10156
|
+
for (const errorInfo of this.prismaErrorClasses) if (errorInfo.name === className) return errorInfo.errorClass;
|
|
10157
|
+
return null;
|
|
10158
|
+
}
|
|
10159
|
+
/**
|
|
10160
|
+
* Deep clone an error object to make it serializable
|
|
10161
|
+
*/
|
|
10162
|
+
_cloneError(error) {
|
|
10163
|
+
const cloned = new Error(error.message);
|
|
10164
|
+
cloned.name = error.name;
|
|
10165
|
+
cloned.stack = error.stack;
|
|
10166
|
+
for (const key in error) if (error.hasOwnProperty(key)) try {
|
|
10167
|
+
cloned[key] = error[key];
|
|
10168
|
+
} catch (e) {}
|
|
10169
|
+
return cloned;
|
|
10170
|
+
}
|
|
10171
|
+
_wrap(target, propertyName, wrapper) {
|
|
10172
|
+
wrap(target, propertyName, wrapper);
|
|
10173
|
+
}
|
|
10174
|
+
};
|
|
10175
|
+
|
|
9943
10176
|
//#endregion
|
|
9944
10177
|
//#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
|
|
9945
10178
|
var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
|
|
@@ -13069,6 +13302,10 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
13069
13302
|
enabled: true,
|
|
13070
13303
|
mode: this.mode
|
|
13071
13304
|
});
|
|
13305
|
+
new PrismaInstrumentation({
|
|
13306
|
+
enabled: true,
|
|
13307
|
+
mode: this.mode
|
|
13308
|
+
});
|
|
13072
13309
|
}
|
|
13073
13310
|
initializeTracing({ baseDirectory }) {
|
|
13074
13311
|
const serviceName = this.config.service?.name || "unknown";
|