@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/dist/index.js CHANGED
@@ -256,7 +256,7 @@ var TdInstrumentationAbstract = class {
256
256
 
257
257
  //#endregion
258
258
  //#region package.json
259
- var version = "0.1.11";
259
+ var version = "0.1.12";
260
260
 
261
261
  //#endregion
262
262
  //#region src/version.ts
@@ -2038,7 +2038,7 @@ var HttpReplayHooks = class {
2038
2038
  auth: requestOptions.auth || void 0,
2039
2039
  agent: requestOptions.agent || void 0,
2040
2040
  protocol,
2041
- hostname: requestOptions.hostname || void 0,
2041
+ hostname: requestOptions.hostname || requestOptions.host || void 0,
2042
2042
  port: requestOptions.port ? Number(requestOptions.port) : void 0,
2043
2043
  method
2044
2044
  };
@@ -3007,13 +3007,29 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
3007
3007
  readable: res.readable
3008
3008
  };
3009
3009
  const responseChunks = [];
3010
- if (res.readable) {
3010
+ let streamConsumptionMode = "NOT_CONSUMING";
3011
+ const originalRead = res.read?.bind(res);
3012
+ if (originalRead) res.read = function read(size) {
3013
+ const chunk = originalRead(size);
3014
+ if (chunk && (streamConsumptionMode === "READ" || streamConsumptionMode === "NOT_CONSUMING")) {
3015
+ streamConsumptionMode = "READ";
3016
+ responseChunks.push(Buffer.from(chunk));
3017
+ }
3018
+ return chunk;
3019
+ };
3020
+ res.once("resume", () => {
3011
3021
  res.on("data", (chunk) => {
3012
- responseChunks.push(chunk);
3022
+ if (chunk && (streamConsumptionMode === "PIPE" || streamConsumptionMode === "NOT_CONSUMING")) {
3023
+ streamConsumptionMode = "PIPE";
3024
+ responseChunks.push(Buffer.from(chunk));
3025
+ }
3013
3026
  });
3014
- res.on("end", async () => {
3015
- if (responseChunks.length > 0) try {
3016
- const responseBuffer = combineChunks(responseChunks);
3027
+ });
3028
+ res.on("end", async (chunk) => {
3029
+ if (chunk && typeof chunk !== "function") responseChunks.push(Buffer.from(chunk));
3030
+ try {
3031
+ if (responseChunks.length > 0) {
3032
+ const responseBuffer = Buffer.concat(responseChunks);
3017
3033
  const rawHeaders = this._captureHeadersFromRawHeaders(res.rawHeaders);
3018
3034
  outputValue.headers = rawHeaders;
3019
3035
  const contentEncoding = rawHeaders["content-encoding"];
@@ -3022,40 +3038,24 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
3022
3038
  contentEncoding
3023
3039
  });
3024
3040
  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
3041
  }
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"] || "")
3042
+ this._addOutputAttributesToSpan({
3043
+ spanInfo,
3044
+ outputValue,
3045
+ statusCode: res.statusCode || 1,
3046
+ outputSchemaMerges: {
3047
+ body: {
3048
+ encoding: EncodingType.BASE64,
3049
+ decodedType: getDecodedType(outputValue.headers["content-type"] || "")
3050
+ },
3051
+ headers: { matchImportance: 0 }
3051
3052
  },
3052
- headers: { matchImportance: 0 }
3053
- },
3054
- inputValue: completeInputValue
3055
- });
3056
- } catch (error) {
3057
- logger.error(`[HttpInstrumentation] Error adding output attributes to span:`, error);
3058
- }
3053
+ inputValue: completeInputValue
3054
+ });
3055
+ } catch (error) {
3056
+ logger.error(`[HttpInstrumentation] Error processing response body:`, error);
3057
+ }
3058
+ });
3059
3059
  });
3060
3060
  req.on("error", (error) => {
3061
3061
  try {
@@ -9918,6 +9918,239 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
9918
9918
  }
9919
9919
  };
9920
9920
 
9921
+ //#endregion
9922
+ //#region src/instrumentation/libraries/prisma/types.ts
9923
+ /**
9924
+ * Prisma error class names for proper error handling
9925
+ */
9926
+ let PrismaErrorClassName = /* @__PURE__ */ function(PrismaErrorClassName$1) {
9927
+ PrismaErrorClassName$1["PrismaClientKnownRequestError"] = "PrismaClientKnownRequestError";
9928
+ PrismaErrorClassName$1["PrismaClientUnknownRequestError"] = "PrismaClientUnknownRequestError";
9929
+ PrismaErrorClassName$1["PrismaClientInitializationError"] = "PrismaClientInitializationError";
9930
+ PrismaErrorClassName$1["PrismaClientValidationError"] = "PrismaClientValidationError";
9931
+ PrismaErrorClassName$1["PrismaClientRustPanicError"] = "PrismaClientRustPanicError";
9932
+ PrismaErrorClassName$1["NotFoundError"] = "NotFoundError";
9933
+ return PrismaErrorClassName$1;
9934
+ }({});
9935
+
9936
+ //#endregion
9937
+ //#region src/instrumentation/libraries/prisma/Instrumentation.ts
9938
+ var PrismaInstrumentation = class extends TdInstrumentationBase {
9939
+ constructor(config = {}) {
9940
+ super("@prisma/client", config);
9941
+ this.INSTRUMENTATION_NAME = "PrismaInstrumentation";
9942
+ this.prismaErrorClasses = [];
9943
+ this.mode = config.mode || TuskDriftMode.DISABLED;
9944
+ this.tuskDrift = TuskDriftCore.getInstance();
9945
+ }
9946
+ init() {
9947
+ return [new TdInstrumentationNodeModule({
9948
+ name: "@prisma/client",
9949
+ supportedVersions: ["5.*", "6.*"],
9950
+ patch: (moduleExports) => this._patchPrismaModule(moduleExports)
9951
+ })];
9952
+ }
9953
+ _patchPrismaModule(prismaModule) {
9954
+ if (this.isModulePatched(prismaModule)) {
9955
+ logger.debug(`[PrismaInstrumentation] Prisma module already patched, skipping`);
9956
+ return prismaModule;
9957
+ }
9958
+ this._storePrismaErrorClasses(prismaModule);
9959
+ logger.debug(`[PrismaInstrumentation] Wrapping PrismaClient constructor`);
9960
+ this._wrap(prismaModule, "PrismaClient", (OriginalPrismaClient) => {
9961
+ const self = this;
9962
+ logger.debug(`[PrismaInstrumentation] PrismaClient wrapper called`);
9963
+ return class TdPrismaClient {
9964
+ constructor(...args) {
9965
+ logger.debug(`[PrismaInstrumentation] Creating patched PrismaClient instance`);
9966
+ return new OriginalPrismaClient(...args).$extends({ query: { async $allOperations({ model, operation, args: operationArgs, query }) {
9967
+ logger.debug(`[PrismaInstrumentation] $allOperations intercepted: ${model}.${operation}`);
9968
+ return self._handlePrismaOperation({
9969
+ model,
9970
+ operation,
9971
+ args: operationArgs,
9972
+ query
9973
+ });
9974
+ } } });
9975
+ }
9976
+ };
9977
+ });
9978
+ this.markModuleAsPatched(prismaModule);
9979
+ logger.debug(`[PrismaInstrumentation] Prisma module patching complete`);
9980
+ return prismaModule;
9981
+ }
9982
+ _storePrismaErrorClasses(moduleExports) {
9983
+ const prismaNamespace = moduleExports.Prisma || {};
9984
+ this.prismaErrorClasses = [
9985
+ {
9986
+ name: PrismaErrorClassName.PrismaClientKnownRequestError,
9987
+ errorClass: moduleExports.PrismaClientKnownRequestError || prismaNamespace.PrismaClientKnownRequestError
9988
+ },
9989
+ {
9990
+ name: PrismaErrorClassName.PrismaClientUnknownRequestError,
9991
+ errorClass: moduleExports.PrismaClientUnknownRequestError || prismaNamespace.PrismaClientUnknownRequestError
9992
+ },
9993
+ {
9994
+ name: PrismaErrorClassName.PrismaClientInitializationError,
9995
+ errorClass: moduleExports.PrismaClientInitializationError || prismaNamespace.PrismaClientInitializationError
9996
+ },
9997
+ {
9998
+ name: PrismaErrorClassName.PrismaClientValidationError,
9999
+ errorClass: moduleExports.PrismaClientValidationError || prismaNamespace.PrismaClientValidationError
10000
+ },
10001
+ {
10002
+ name: PrismaErrorClassName.PrismaClientRustPanicError,
10003
+ errorClass: moduleExports.PrismaClientRustPanicError || prismaNamespace.PrismaClientRustPanicError
10004
+ },
10005
+ {
10006
+ name: PrismaErrorClassName.NotFoundError,
10007
+ errorClass: moduleExports.NotFoundError || prismaNamespace.NotFoundError
10008
+ }
10009
+ ];
10010
+ }
10011
+ _handlePrismaOperation({ model, operation, args, query }) {
10012
+ const inputValue = {
10013
+ model,
10014
+ operation,
10015
+ args
10016
+ };
10017
+ logger.debug(`[PrismaInstrumentation] Intercepted Prisma operation: ${model}.${operation} in ${this.mode} mode`);
10018
+ if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
10019
+ originalFunctionCall: () => query(args),
10020
+ recordModeHandler: ({ isPreAppStart }) => {
10021
+ return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
10022
+ name: `prisma.${operation}`,
10023
+ kind: SpanKind.CLIENT,
10024
+ submodule: model,
10025
+ packageType: PackageType.UNSPECIFIED,
10026
+ packageName: "@prisma/client",
10027
+ instrumentationName: this.INSTRUMENTATION_NAME,
10028
+ inputValue,
10029
+ isPreAppStart
10030
+ }, (spanInfo) => {
10031
+ return this._handleRecordPrismaOperation(spanInfo, query, args);
10032
+ });
10033
+ },
10034
+ spanKind: SpanKind.CLIENT
10035
+ });
10036
+ else if (this.mode === TuskDriftMode.REPLAY) {
10037
+ const stackTrace = captureStackTrace(["PrismaInstrumentation"]);
10038
+ return handleReplayMode({
10039
+ noOpRequestHandler: () => query(args),
10040
+ isServerRequest: false,
10041
+ replayModeHandler: () => {
10042
+ return SpanUtils.createAndExecuteSpan(this.mode, () => query(args), {
10043
+ name: `prisma.${operation}`,
10044
+ kind: SpanKind.CLIENT,
10045
+ submodule: model,
10046
+ packageType: PackageType.UNSPECIFIED,
10047
+ packageName: "@prisma/client",
10048
+ instrumentationName: this.INSTRUMENTATION_NAME,
10049
+ inputValue,
10050
+ isPreAppStart: false
10051
+ }, (spanInfo) => {
10052
+ return this._handleReplayPrismaOperation(spanInfo, inputValue, stackTrace);
10053
+ });
10054
+ }
10055
+ });
10056
+ } else return query(args);
10057
+ }
10058
+ async _handleRecordPrismaOperation(spanInfo, query, args) {
10059
+ try {
10060
+ logger.debug(`[PrismaInstrumentation] Recording Prisma operation`);
10061
+ const result = await query(args);
10062
+ const outputValue = {
10063
+ prismaResult: result,
10064
+ _tdOriginalFormat: "result"
10065
+ };
10066
+ try {
10067
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
10068
+ SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
10069
+ } catch (spanError) {
10070
+ logger.error(`[PrismaInstrumentation] error adding span attributes:`, spanError);
10071
+ }
10072
+ return result;
10073
+ } catch (error) {
10074
+ logger.debug(`[PrismaInstrumentation] Prisma operation error: ${error.message}`);
10075
+ try {
10076
+ const errorClassName = this._getPrismaErrorClassName(error);
10077
+ const errorWithClassName = this._cloneError(error);
10078
+ if (errorClassName) errorWithClassName.customTdName = errorClassName;
10079
+ const outputValue = {
10080
+ prismaResult: errorWithClassName,
10081
+ _tdOriginalFormat: "error"
10082
+ };
10083
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
10084
+ SpanUtils.endSpan(spanInfo.span, {
10085
+ code: SpanStatusCode.ERROR,
10086
+ message: error.message
10087
+ });
10088
+ } catch (spanError) {
10089
+ logger.error(`[PrismaInstrumentation] error extracting error and adding span attributes:`, spanError);
10090
+ }
10091
+ throw error;
10092
+ }
10093
+ }
10094
+ async _handleReplayPrismaOperation(spanInfo, inputValue, stackTrace) {
10095
+ const mockData = await findMockResponseAsync({
10096
+ mockRequestData: {
10097
+ traceId: spanInfo.traceId,
10098
+ spanId: spanInfo.spanId,
10099
+ name: `prisma.${inputValue.operation}`,
10100
+ inputValue,
10101
+ packageName: "@prisma/client",
10102
+ instrumentationName: this.INSTRUMENTATION_NAME,
10103
+ submoduleName: inputValue.model,
10104
+ kind: SpanKind.CLIENT,
10105
+ stackTrace
10106
+ },
10107
+ tuskDrift: this.tuskDrift
10108
+ });
10109
+ if (!mockData) {
10110
+ logger.warn(`[PrismaInstrumentation] No mock data found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10111
+ throw new Error(`[PrismaInstrumentation] No matching mock found for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10112
+ }
10113
+ logger.debug(`[PrismaInstrumentation] Found mock data for Prisma operation: ${inputValue.model}.${inputValue.operation}`);
10114
+ const outputValue = mockData.result;
10115
+ if (outputValue._tdOriginalFormat === "error") {
10116
+ const errorObj = outputValue.prismaResult;
10117
+ if (errorObj.customTdName) {
10118
+ const errorClass = this._getPrismaErrorClassFromName(errorObj.customTdName);
10119
+ if (errorClass) Object.setPrototypeOf(errorObj, errorClass.prototype);
10120
+ }
10121
+ SpanUtils.endSpan(spanInfo.span, {
10122
+ code: SpanStatusCode.ERROR,
10123
+ message: errorObj.message || "Prisma error"
10124
+ });
10125
+ throw errorObj;
10126
+ }
10127
+ SpanUtils.endSpan(spanInfo.span, { code: SpanStatusCode.OK });
10128
+ return outputValue.prismaResult;
10129
+ }
10130
+ _getPrismaErrorClassName(error) {
10131
+ for (const errorInfo of this.prismaErrorClasses) if (error instanceof errorInfo.errorClass) return errorInfo.name;
10132
+ }
10133
+ _getPrismaErrorClassFromName(className) {
10134
+ for (const errorInfo of this.prismaErrorClasses) if (errorInfo.name === className) return errorInfo.errorClass;
10135
+ return null;
10136
+ }
10137
+ /**
10138
+ * Deep clone an error object to make it serializable
10139
+ */
10140
+ _cloneError(error) {
10141
+ const cloned = new Error(error.message);
10142
+ cloned.name = error.name;
10143
+ cloned.stack = error.stack;
10144
+ for (const key in error) if (error.hasOwnProperty(key)) try {
10145
+ cloned[key] = error[key];
10146
+ } catch (e) {}
10147
+ return cloned;
10148
+ }
10149
+ _wrap(target, propertyName, wrapper) {
10150
+ wrap(target, propertyName, wrapper);
10151
+ }
10152
+ };
10153
+
9921
10154
  //#endregion
9922
10155
  //#region node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js
9923
10156
  var require_suppress_tracing = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.js": ((exports) => {
@@ -13047,6 +13280,10 @@ var TuskDriftCore = class TuskDriftCore {
13047
13280
  enabled: true,
13048
13281
  mode: this.mode
13049
13282
  });
13283
+ new PrismaInstrumentation({
13284
+ enabled: true,
13285
+ mode: this.mode
13286
+ });
13050
13287
  }
13051
13288
  initializeTracing({ baseDirectory }) {
13052
13289
  const serviceName = this.config.service?.name || "unknown";