@use-tusk/drift-node-sdk 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -58,6 +58,10 @@ let __use_tusk_drift_schemas_backend_span_export_service = require("@use-tusk/dr
58
58
  __use_tusk_drift_schemas_backend_span_export_service = __toESM(__use_tusk_drift_schemas_backend_span_export_service);
59
59
  let jsonpath = require("jsonpath");
60
60
  jsonpath = __toESM(jsonpath);
61
+ let net = require("net");
62
+ net = __toESM(net);
63
+ let stream = require("stream");
64
+ stream = __toESM(stream);
61
65
  let __opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
62
66
  __opentelemetry_semantic_conventions = __toESM(__opentelemetry_semantic_conventions);
63
67
  let __use_tusk_drift_schemas_backend_span_export_service_client = require("@use-tusk/drift-schemas/backend/span_export_service.client");
@@ -66,8 +70,6 @@ let __protobuf_ts_twirp_transport = require("@protobuf-ts/twirp-transport");
66
70
  __protobuf_ts_twirp_transport = __toESM(__protobuf_ts_twirp_transport);
67
71
  let __opentelemetry_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
68
72
  __opentelemetry_sdk_trace_node = __toESM(__opentelemetry_sdk_trace_node);
69
- let net = require("net");
70
- net = __toESM(net);
71
73
  let child_process = require("child_process");
72
74
  child_process = __toESM(child_process);
73
75
  let __use_tusk_drift_schemas_core_communication = require("@use-tusk/drift-schemas/core/communication");
@@ -273,7 +275,7 @@ var TdInstrumentationAbstract = class {
273
275
 
274
276
  //#endregion
275
277
  //#region package.json
276
- var version = "0.1.7";
278
+ var version = "0.1.8";
277
279
 
278
280
  //#endregion
279
281
  //#region src/version.ts
@@ -470,6 +472,20 @@ function loadTuskConfig() {
470
472
  }
471
473
  }
472
474
 
475
+ //#endregion
476
+ //#region src/core/utils/runtimeDetectionUtils.ts
477
+ function isNextJsRuntime() {
478
+ return process.env.NEXT_RUNTIME !== void 0 || typeof global.__NEXT_DATA__ !== "undefined";
479
+ }
480
+ function isEsm(moduleExports) {
481
+ if (!moduleExports || typeof moduleExports !== "object") return false;
482
+ try {
483
+ return moduleExports[Symbol.toStringTag] === "Module";
484
+ } catch (error) {
485
+ return false;
486
+ }
487
+ }
488
+
473
489
  //#endregion
474
490
  //#region src/core/utils/logger.ts
475
491
  var Logger = class {
@@ -749,9 +765,11 @@ var TdInstrumentationNodeModule = class {
749
765
 
750
766
  //#endregion
751
767
  //#region src/core/types.ts
768
+ const TD_INSTRUMENTATION_LIBRARY_NAME = "tusk-drift-sdk";
752
769
  const REPLAY_TRACE_ID_CONTEXT_KEY = (0, __opentelemetry_api.createContextKey)("td.replayTraceId");
753
770
  const SPAN_KIND_CONTEXT_KEY = (0, __opentelemetry_api.createContextKey)("td.spanKind");
754
771
  const IS_PRE_APP_START_CONTEXT_KEY = (0, __opentelemetry_api.createContextKey)("td.isPreAppStart");
772
+ const STOP_RECORDING_CHILD_SPANS_CONTEXT_KEY = (0, __opentelemetry_api.createContextKey)("td.stopRecordingChildSpans");
755
773
  const CALLING_LIBRARY_CONTEXT_KEY = (0, __opentelemetry_api.createContextKey)("td.callingLibrary");
756
774
  let TdSpanAttributes = /* @__PURE__ */ function(TdSpanAttributes$1) {
757
775
  /**
@@ -794,6 +812,74 @@ let TdSpanAttributes = /* @__PURE__ */ function(TdSpanAttributes$1) {
794
812
  return TdSpanAttributes$1;
795
813
  }({});
796
814
 
815
+ //#endregion
816
+ //#region src/core/tracing/TraceBlockingManager.ts
817
+ /**
818
+ * Manages blocked trace IDs to prevent creation and export of spans
819
+ * that belong to traces exceeding size limits.
820
+ *
821
+ * This class uses an in-memory Set for O(1) lookup performance and
822
+ * automatically cleans up old entries to prevent memory leaks.
823
+ */
824
+ var TraceBlockingManager = class TraceBlockingManager {
825
+ constructor() {
826
+ this.blockedTraceIds = /* @__PURE__ */ new Set();
827
+ this.traceTimestamps = /* @__PURE__ */ new Map();
828
+ this.cleanupIntervalId = null;
829
+ this.DEFAULT_TTL_MS = 600 * 1e3;
830
+ this.CLEANUP_INTERVAL_MS = 120 * 1e3;
831
+ this.startCleanupInterval();
832
+ }
833
+ /**
834
+ * Get singleton instance
835
+ */
836
+ static getInstance() {
837
+ if (!TraceBlockingManager.instance) TraceBlockingManager.instance = new TraceBlockingManager();
838
+ return TraceBlockingManager.instance;
839
+ }
840
+ /**
841
+ * Check if a trace ID is blocked
842
+ */
843
+ isTraceBlocked(traceId) {
844
+ return this.blockedTraceIds.has(traceId);
845
+ }
846
+ /**
847
+ * Block a trace ID and all future spans for this trace
848
+ */
849
+ blockTrace(traceId) {
850
+ if (!this.blockedTraceIds.has(traceId)) {
851
+ this.blockedTraceIds.add(traceId);
852
+ const originalDate = OriginalGlobalUtils.getOriginalDate();
853
+ this.traceTimestamps.set(traceId, originalDate.getTime());
854
+ logger.debug(`[TraceBlockingManager] Blocked trace: ${traceId}`);
855
+ }
856
+ }
857
+ /**
858
+ * Start periodic cleanup of old blocked trace IDs
859
+ */
860
+ startCleanupInterval() {
861
+ if (this.cleanupIntervalId) return;
862
+ this.cleanupIntervalId = setInterval(() => {
863
+ this.cleanupOldTraces();
864
+ }, this.CLEANUP_INTERVAL_MS);
865
+ if (this.cleanupIntervalId.unref) this.cleanupIntervalId.unref();
866
+ }
867
+ /**
868
+ * Clean up trace IDs older than TTL
869
+ */
870
+ cleanupOldTraces() {
871
+ const now = OriginalGlobalUtils.getOriginalDate().getTime();
872
+ const expiredTraces = [];
873
+ for (const [traceId, timestamp] of this.traceTimestamps.entries()) if (now - timestamp > this.DEFAULT_TTL_MS) expiredTraces.push(traceId);
874
+ for (const traceId of expiredTraces) {
875
+ this.blockedTraceIds.delete(traceId);
876
+ this.traceTimestamps.delete(traceId);
877
+ }
878
+ if (expiredTraces.length > 0) logger.debug(`[TraceBlockingManager] Cleaned up ${expiredTraces.length} expired blocked trace(s)`);
879
+ }
880
+ };
881
+ TraceBlockingManager.instance = null;
882
+
797
883
  //#endregion
798
884
  //#region src/core/tracing/SpanUtils.ts
799
885
  var SpanUtils = class SpanUtils {
@@ -804,6 +890,14 @@ var SpanUtils = class SpanUtils {
804
890
  try {
805
891
  const tracer = TuskDriftCore.getInstance().getTracer();
806
892
  const parentContext = options.parentContext || __opentelemetry_api.context.active();
893
+ const activeSpan = __opentelemetry_api.trace.getSpan(parentContext);
894
+ if (activeSpan) {
895
+ const parentTraceId = activeSpan.spanContext().traceId;
896
+ if (TraceBlockingManager.getInstance().isTraceBlocked(parentTraceId)) {
897
+ logger.debug(`[SpanUtils] Skipping span creation for '${options.name}' - trace ${parentTraceId} is blocked`);
898
+ return null;
899
+ }
900
+ }
807
901
  const span = tracer.startSpan(options.name, {
808
902
  kind: options.kind || __opentelemetry_api.SpanKind.CLIENT,
809
903
  attributes: options.attributes || {}
@@ -843,7 +937,14 @@ var SpanUtils = class SpanUtils {
843
937
  * @returns The result of the function execution
844
938
  */
845
939
  static createAndExecuteSpan(mode, originalFunctionCall, options, fn) {
846
- const { name, kind, packageName, instrumentationName, packageType, submodule, inputValue, inputSchemaMerges, isPreAppStart, metadata } = options;
940
+ const spanContext = __opentelemetry_api.trace.getActiveSpan()?.spanContext();
941
+ if (spanContext) {
942
+ if (__opentelemetry_api.context.active().getValue(STOP_RECORDING_CHILD_SPANS_CONTEXT_KEY)) {
943
+ logger.debug(`[SpanUtils] Stopping recording of child spans for span ${spanContext.spanId}, packageName: ${options.packageName}, instrumentationName: ${options.instrumentationName}`);
944
+ return originalFunctionCall();
945
+ }
946
+ }
947
+ const { name, kind, packageName, instrumentationName, packageType, submodule, inputValue, inputSchemaMerges, isPreAppStart, metadata, stopRecordingChildSpans } = options;
847
948
  let spanInfo = null;
848
949
  try {
849
950
  spanInfo = SpanUtils.createSpan({
@@ -868,6 +969,7 @@ var SpanUtils = class SpanUtils {
868
969
  }
869
970
  if (!spanInfo) if (mode === TuskDriftMode.REPLAY) throw new Error("Error creating span in replay mode");
870
971
  else return originalFunctionCall();
972
+ if (stopRecordingChildSpans) spanInfo.context = spanInfo.context.setValue(STOP_RECORDING_CHILD_SPANS_CONTEXT_KEY, true);
871
973
  return SpanUtils.withSpan(spanInfo, () => fn(spanInfo));
872
974
  }
873
975
  /**
@@ -1123,6 +1225,22 @@ function getDecodedType(contentType) {
1123
1225
  const mainType = contentTypeString.toLowerCase().split(";")[0].trim();
1124
1226
  return CONTENT_TYPE_MAPPING[mainType];
1125
1227
  }
1228
+ const STATIC_ASSET_TYPES = new Set([
1229
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.HTML,
1230
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.CSS,
1231
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.JAVASCRIPT,
1232
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.JPEG,
1233
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.PNG,
1234
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.GIF,
1235
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.WEBP,
1236
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.SVG,
1237
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.PDF,
1238
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.AUDIO,
1239
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.VIDEO,
1240
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.BINARY,
1241
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.ZIP,
1242
+ __use_tusk_drift_schemas_core_json_schema.DecodedType.GZIP
1243
+ ]);
1126
1244
 
1127
1245
  //#endregion
1128
1246
  //#region src/instrumentation/libraries/http/mocks/TdHttpMockSocket.ts
@@ -1597,6 +1715,28 @@ async function findMockResponseAsync({ mockRequestData, tuskDrift, inputValueSch
1597
1715
  return null;
1598
1716
  }
1599
1717
  }
1718
+ function findMockResponseSync({ mockRequestData, tuskDrift, inputValueSchemaMerges }) {
1719
+ const outboundSpan = convertMockRequestDataToCleanSpanData(mockRequestData, tuskDrift, inputValueSchemaMerges);
1720
+ try {
1721
+ const replayTraceId = SpanUtils.getCurrentReplayTraceId();
1722
+ logger.debug(`Finding ${outboundSpan.traceId} mock for replay trace ID: ${replayTraceId}`);
1723
+ const mockResponse = tuskDrift.requestMockSync({
1724
+ outboundSpan,
1725
+ testId: replayTraceId || ""
1726
+ });
1727
+ if (!mockResponse || !mockResponse.found) {
1728
+ logger.debug(`No matching mock found for ${outboundSpan.traceId} with input value: ${JSON.stringify(outboundSpan.inputValue)}`, replayTraceId);
1729
+ return null;
1730
+ }
1731
+ const responseBody = mockResponse.response?.response?.body;
1732
+ logger.debug(`Found ${outboundSpan.traceId} mock response and timestamp:`, responseBody, { timestamp: mockResponse.response?.timestamp });
1733
+ if (mockResponse.response?.timestamp) DateTracker.updateLatestTimestamp(replayTraceId || "", mockResponse.response.timestamp);
1734
+ return { result: responseBody };
1735
+ } catch (error) {
1736
+ logger.error(`Error finding ${outboundSpan.traceId} mock response:`, error);
1737
+ return null;
1738
+ }
1739
+ }
1600
1740
 
1601
1741
  //#endregion
1602
1742
  //#region src/instrumentation/libraries/http/mocks/TdMockClientRequest.ts
@@ -2009,7 +2149,7 @@ function wrap(target, propertyName, wrapper) {
2009
2149
  * Helper functions for capturing stack traces in replay mode
2010
2150
  *
2011
2151
  * TODO: Consider using a structured format for stack frames:
2012
- *
2152
+ *
2013
2153
  * {
2014
2154
  * "frames": [
2015
2155
  * {
@@ -2025,7 +2165,7 @@ function wrap(target, propertyName, wrapper) {
2025
2165
  * It would also allow for more accurate stack trace reconstruction in replay mode.
2026
2166
  */
2027
2167
  /**
2028
- *
2168
+ *
2029
2169
  * @param excludeClassNames - Class names to exclude from the stack trace
2030
2170
  * @returns The stack trace as a string
2031
2171
  */
@@ -2438,7 +2578,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2438
2578
  logger.debug(`[HttpInstrumentation] ${protocolUpper} module already patched, skipping`);
2439
2579
  return httpModule;
2440
2580
  }
2441
- if (httpModule[Symbol.toStringTag] === "Module") {
2581
+ if (isEsm(httpModule)) {
2442
2582
  if (httpModule.default) {
2443
2583
  this._wrap(httpModule.default, "request", this._getRequestPatchFn(protocol));
2444
2584
  this._wrap(httpModule.default, "get", this._getGetPatchFn(protocol));
@@ -2457,11 +2597,18 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2457
2597
  return httpModule;
2458
2598
  }
2459
2599
  _createServerSpan({ req, res, originalHandler, protocol }) {
2600
+ if (isNextJsRuntime()) {
2601
+ logger.debug(`[HttpInstrumentation] Skipping recording/replaying for nextJs runtime, handled by nextJs instrumentation`);
2602
+ return originalHandler.call(this);
2603
+ }
2460
2604
  const method = req.method || "GET";
2461
2605
  const url = req.url || "/";
2462
2606
  const target = req.url || "/";
2463
2607
  const spanProtocol = this._normalizeProtocol(protocol, "http");
2464
- if (isTuskDriftIngestionUrl(url) || isTuskDriftIngestionUrl(target)) return originalHandler.call(this);
2608
+ if (isTuskDriftIngestionUrl(url) || isTuskDriftIngestionUrl(target)) {
2609
+ logger.debug(`[HttpInstrumentation] Ignoring drift ingestion endpoints`);
2610
+ return originalHandler.call(this);
2611
+ }
2465
2612
  if (this.transformEngine.shouldDropInboundRequest(method, url, req.headers)) {
2466
2613
  logger.debug(`[HttpInstrumentation] Dropping inbound request due to transforms: ${method} ${url}`);
2467
2614
  return originalHandler.call(this);
@@ -2479,7 +2626,10 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2479
2626
  remotePort: req.socket?.remotePort
2480
2627
  };
2481
2628
  const replayTraceId = this.replayHooks.extractTraceIdFromHeaders(req);
2482
- if (!replayTraceId) return originalHandler.call(this);
2629
+ if (!replayTraceId) {
2630
+ logger.debug(`[HttpInstrumentation] No trace id found in headers`, req.headers);
2631
+ return originalHandler.call(this);
2632
+ }
2483
2633
  logger.debug(`[HttpInstrumentation] Setting replay trace id`, replayTraceId);
2484
2634
  const envVars = this.replayHooks.extractEnvVarsFromHeaders(req);
2485
2635
  if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
@@ -2511,13 +2661,6 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2511
2661
  } });
2512
2662
  else if (this.mode === TuskDriftMode.RECORD) {
2513
2663
  if (method.toUpperCase() === "OPTIONS" || !!req.headers["access-control-request-method"]) return originalHandler.call(this);
2514
- if (!shouldSample({
2515
- samplingRate: this.tuskDrift.getSamplingRate(),
2516
- isAppReady: this.tuskDrift.isAppReady()
2517
- })) {
2518
- logger.debug(`Skipping server span due to sampling rate`, url, this.tuskDrift.getSamplingRate());
2519
- return originalHandler.call(this);
2520
- }
2521
2664
  logger.debug(`[HttpInstrumentation] Creating server span for ${method} ${url}`);
2522
2665
  return handleRecordMode({
2523
2666
  originalFunctionCall: () => originalHandler.call(this),
@@ -2611,7 +2754,6 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2611
2754
  outputValue.bodySize = responseBuffer.length;
2612
2755
  } catch (error) {
2613
2756
  logger.error(`[HttpInstrumentation] Error processing server response body:`, error);
2614
- outputValue.bodyProcessingError = error instanceof Error ? error.message : String(error);
2615
2757
  }
2616
2758
  try {
2617
2759
  const spanData = {
@@ -2629,7 +2771,7 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2629
2771
  outputSchemaMerges: {
2630
2772
  body: {
2631
2773
  encoding: __use_tusk_drift_schemas_core_json_schema.EncodingType.BASE64,
2632
- decodedType: getDecodedType(spanData.outputValue.headers?.["content-type"] || "")
2774
+ decodedType: getDecodedType(spanData.outputValue?.headers?.["content-type"] || "")
2633
2775
  },
2634
2776
  headers: { matchImportance: 0 }
2635
2777
  },
@@ -2642,6 +2784,11 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
2642
2784
  message: `HTTP ${statusCode}`
2643
2785
  } : { code: __opentelemetry_api.SpanStatusCode.OK };
2644
2786
  SpanUtils.setStatus(spanInfo.span, status);
2787
+ const decodedType = getDecodedType(outputValue.headers?.["content-type"] || "");
2788
+ if (decodedType && STATIC_ASSET_TYPES.has(decodedType)) {
2789
+ TraceBlockingManager.getInstance().blockTrace(spanInfo.traceId);
2790
+ logger.debug(`[HttpInstrumentation] Blocking trace ${spanInfo.traceId} because it is an static asset response. Decoded type: ${decodedType}`);
2791
+ }
2645
2792
  SpanUtils.endSpan(spanInfo.span);
2646
2793
  } catch (error) {
2647
2794
  logger.error(`[HttpInstrumentation] Error adding response attributes to span:`, error);
@@ -3143,6 +3290,12 @@ var HttpInstrumentation = class extends TdInstrumentationBase {
3143
3290
  return (originalEmit) => {
3144
3291
  return function(eventName, ...args) {
3145
3292
  if (eventName === "request") {
3293
+ if (self.mode === TuskDriftMode.RECORD) {
3294
+ if (!shouldSample({
3295
+ samplingRate: self.tuskDrift.getSamplingRate(),
3296
+ isAppReady: self.tuskDrift.isAppReady()
3297
+ })) return originalEmit.apply(this, [eventName, ...args]);
3298
+ }
3146
3299
  const req = args[0];
3147
3300
  const res = args[1];
3148
3301
  return self._createServerSpan({
@@ -4055,7 +4208,7 @@ var PostgresInstrumentation = class extends TdInstrumentationBase {
4055
4208
  return postgresModule;
4056
4209
  }
4057
4210
  const self = this;
4058
- if (postgresModule[Symbol.toStringTag] === "Module") {
4211
+ if (isEsm(postgresModule)) {
4059
4212
  logger.debug(`[PostgresInstrumentation] Wrapping ESM default export`);
4060
4213
  this._wrap(postgresModule, "default", (originalFunction) => {
4061
4214
  return function(...args) {
@@ -5821,6 +5974,7 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
5821
5974
  super("tcp", config);
5822
5975
  this.loggedSpans = /* @__PURE__ */ new Set();
5823
5976
  this.mode = config.mode || TuskDriftMode.DISABLED;
5977
+ this._patchLoadedModules();
5824
5978
  }
5825
5979
  init() {
5826
5980
  return [new TdInstrumentationNodeModule({
@@ -5829,6 +5983,17 @@ var TcpInstrumentation = class extends TdInstrumentationBase {
5829
5983
  patch: (moduleExports) => this._patchNetModule(moduleExports)
5830
5984
  })];
5831
5985
  }
5986
+ _patchLoadedModules() {
5987
+ if (isNextJsRuntime()) {
5988
+ logger.debug(`[TcpInstrumentation] Next.js environment detected - force-loading net module to ensure patching`);
5989
+ try {
5990
+ require("net");
5991
+ logger.debug(`[TcpInstrumentation] net module force-loaded`);
5992
+ } catch (err) {
5993
+ logger.error(`[TcpInstrumentation] Error force-loading net module:`, err);
5994
+ }
5995
+ } else logger.debug(`[TcpInstrumentation] Regular Node.js environment - hooks will catch net module on first require`);
5996
+ }
5832
5997
  _patchNetModule(netModule) {
5833
5998
  logger.debug(`[TcpInstrumentation] Patching NET module in ${this.mode} mode`);
5834
5999
  if (this.isModulePatched(netModule)) {
@@ -6981,7 +7146,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
6981
7146
  logger.debug(`[IORedisInstrumentation] IORedis module already patched, skipping`);
6982
7147
  return moduleExports;
6983
7148
  }
6984
- const actualExports = moduleExports[Symbol.toStringTag] === "Module" ? moduleExports.default : moduleExports;
7149
+ const actualExports = isEsm(moduleExports) ? moduleExports.default : moduleExports;
6985
7150
  if (!actualExports || !actualExports.prototype) {
6986
7151
  logger.error(`[IORedisInstrumentation] Invalid module exports, cannot patch`);
6987
7152
  return moduleExports;
@@ -7020,7 +7185,7 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
7020
7185
  logger.debug(`[IORedisInstrumentation] Pipeline module already patched, skipping`);
7021
7186
  return moduleExports;
7022
7187
  }
7023
- const actualExports = moduleExports[Symbol.toStringTag] === "Module" ? moduleExports.default : moduleExports;
7188
+ const actualExports = isEsm(moduleExports) ? moduleExports.default : moduleExports;
7024
7189
  if (!actualExports || !actualExports.prototype) {
7025
7190
  logger.debug(`[IORedisInstrumentation] Invalid Pipeline module exports, cannot patch`);
7026
7191
  return moduleExports;
@@ -7613,6 +7778,7 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7613
7778
  return;
7614
7779
  }
7615
7780
  this._wrap(clientPrototype, "makeUnaryRequest", this._getMakeUnaryRequestPatchFn(version$1));
7781
+ this._wrap(clientPrototype, "makeServerStreamRequest", this._getMakeServerStreamRequestPatchFn(version$1));
7616
7782
  this._wrap(clientPrototype, "waitForReady", this._getWaitForReadyPatchFn());
7617
7783
  this._wrap(clientPrototype, "close", this._getClosePatchFn());
7618
7784
  this._wrap(clientPrototype, "getChannel", this._getGetChannelPatchFn());
@@ -7662,6 +7828,7 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7662
7828
  logger.debug(`[GrpcInstrumentation] _getMakeUnaryRequestPatchFn called for version: ${version$1}`);
7663
7829
  return (original) => {
7664
7830
  return function makeUnaryRequest(...args) {
7831
+ logger.debug(`[GrpcInstrumentation] makeUnaryRequest called! args length: ${args.length}`);
7665
7832
  const MetadataConstructor = GrpcInstrumentation.metadataStore.get(version$1) || self.Metadata;
7666
7833
  if (!MetadataConstructor) {
7667
7834
  logger.warn(`[GrpcInstrumentation] Metadata constructor not found for version ${version$1}`);
@@ -7737,6 +7904,115 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7737
7904
  };
7738
7905
  };
7739
7906
  }
7907
+ _getMakeServerStreamRequestPatchFn(version$1) {
7908
+ const self = this;
7909
+ logger.debug(`[GrpcInstrumentation] _getMakeServerStreamRequestPatchFn called for version: ${version$1}`);
7910
+ return (original) => {
7911
+ return function makeServerStreamRequest(...args) {
7912
+ logger.debug(`[GrpcInstrumentation] makeServerStreamRequest called! args length: ${args.length}`);
7913
+ const MetadataConstructor = GrpcInstrumentation.metadataStore.get(version$1) || self.Metadata;
7914
+ if (!MetadataConstructor) {
7915
+ logger.warn(`[GrpcInstrumentation] Metadata constructor not found for version ${version$1}`);
7916
+ return original.apply(this, args);
7917
+ }
7918
+ let parsedParams;
7919
+ try {
7920
+ parsedParams = self.extractServerStreamRequestParameters(args, MetadataConstructor);
7921
+ } catch (error) {
7922
+ logger.error(`[GrpcInstrumentation] Error parsing makeServerStreamRequest arguments:`, error);
7923
+ return original.apply(this, args);
7924
+ }
7925
+ const { method: path$5, argument, metadata, options } = parsedParams;
7926
+ let method;
7927
+ let service;
7928
+ let readableBody;
7929
+ let bufferMap;
7930
+ let jsonableStringMap;
7931
+ let readableMetadata;
7932
+ try {
7933
+ ({method, service} = parseGrpcPath(path$5));
7934
+ ({readableBody, bufferMap, jsonableStringMap} = serializeGrpcPayload(argument));
7935
+ readableMetadata = serializeGrpcMetadata(metadata);
7936
+ } catch (error) {
7937
+ logger.error(`[GrpcInstrumentation] Error parsing makeServerStreamRequest arguments:`, error);
7938
+ return original.apply(this, args);
7939
+ }
7940
+ const inputValue = {
7941
+ method,
7942
+ service,
7943
+ body: readableBody,
7944
+ metadata: readableMetadata,
7945
+ inputMeta: {
7946
+ bufferMap,
7947
+ jsonableStringMap
7948
+ }
7949
+ };
7950
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
7951
+ return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
7952
+ name: "grpc.client.server_stream",
7953
+ kind: __opentelemetry_api.SpanKind.CLIENT,
7954
+ submodule: "client",
7955
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.GRPC,
7956
+ packageName: GRPC_MODULE_NAME,
7957
+ instrumentationName: self.INSTRUMENTATION_NAME,
7958
+ inputValue,
7959
+ isPreAppStart: false
7960
+ }, (spanInfo) => {
7961
+ return self._handleReplayServerStreamRequest(spanInfo, inputValue, MetadataConstructor);
7962
+ });
7963
+ } });
7964
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
7965
+ originalFunctionCall: () => original.apply(this, args),
7966
+ recordModeHandler: ({ isPreAppStart }) => {
7967
+ return SpanUtils.createAndExecuteSpan(self.mode, () => original.apply(this, args), {
7968
+ name: "grpc.client.server_stream",
7969
+ kind: __opentelemetry_api.SpanKind.CLIENT,
7970
+ submodule: "client",
7971
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.GRPC,
7972
+ packageName: GRPC_MODULE_NAME,
7973
+ instrumentationName: self.INSTRUMENTATION_NAME,
7974
+ inputValue,
7975
+ isPreAppStart
7976
+ }, (spanInfo) => {
7977
+ return self._handleRecordServerStreamRequest(spanInfo, original, this, parsedParams);
7978
+ });
7979
+ },
7980
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
7981
+ });
7982
+ else return original.apply(this, args);
7983
+ };
7984
+ };
7985
+ }
7986
+ extractServerStreamRequestParameters(args, MetadataConstructor) {
7987
+ const method = args[0];
7988
+ const serialize = args[1];
7989
+ const deserialize = args[2];
7990
+ const argument = args[3];
7991
+ let metadata;
7992
+ let options;
7993
+ if (args.length === 6) {
7994
+ metadata = args[4];
7995
+ options = args[5];
7996
+ } else if (args.length === 5) if (args[4] instanceof MetadataConstructor) {
7997
+ metadata = args[4];
7998
+ options = {};
7999
+ } else {
8000
+ metadata = new MetadataConstructor();
8001
+ options = args[4] || {};
8002
+ }
8003
+ else {
8004
+ metadata = new MetadataConstructor();
8005
+ options = {};
8006
+ }
8007
+ return {
8008
+ method,
8009
+ serialize,
8010
+ deserialize,
8011
+ argument,
8012
+ metadata,
8013
+ options
8014
+ };
8015
+ }
7740
8016
  _handleRecordUnaryRequest(spanInfo, original, context$4, parsedParams, callback) {
7741
8017
  let isResponseReceived = false;
7742
8018
  let isStatusEmitted = false;
@@ -7847,105 +8123,1160 @@ var GrpcInstrumentation = class GrpcInstrumentation extends TdInstrumentationBas
7847
8123
  isGrpcErrorOutput(result) {
7848
8124
  return "error" in result;
7849
8125
  }
7850
- async _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace) {
8126
+ _handleReplayUnaryRequest(spanInfo, inputValue, callback, MetadataConstructor, stackTrace) {
7851
8127
  logger.debug(`[GrpcInstrumentation] Replaying gRPC unary request`);
7852
- const mockData = await findMockResponseAsync({
8128
+ const emitter = Object.assign(new events.EventEmitter(), {
8129
+ cancel() {},
8130
+ getPeer: () => "0.0.0.0:0000",
8131
+ call: void 0
8132
+ });
8133
+ findMockResponseAsync({
7853
8134
  mockRequestData: {
7854
8135
  traceId: spanInfo.traceId,
7855
8136
  spanId: spanInfo.spanId,
7856
8137
  name: "grpc.client.unary",
7857
8138
  inputValue,
7858
8139
  packageName: GRPC_MODULE_NAME,
8140
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.GRPC,
7859
8141
  instrumentationName: this.INSTRUMENTATION_NAME,
7860
8142
  submoduleName: "client",
7861
8143
  kind: __opentelemetry_api.SpanKind.CLIENT,
7862
8144
  stackTrace
7863
8145
  },
7864
8146
  tuskDrift: this.tuskDrift
7865
- });
7866
- if (!mockData) {
7867
- logger.warn(`[GrpcInstrumentation] No mock data found for gRPC request: ${inputValue.service}/${inputValue.method}`);
7868
- callback(/* @__PURE__ */ new Error("No mock data found"));
7869
- SpanUtils.endSpan(spanInfo.span, {
7870
- code: __opentelemetry_api.SpanStatusCode.ERROR,
7871
- message: "No mock data found"
8147
+ }).then((mockData) => {
8148
+ if (!mockData) {
8149
+ logger.warn(`[GrpcInstrumentation] No mock data found for gRPC request: ${inputValue.service}/${inputValue.method}`, inputValue);
8150
+ callback(/* @__PURE__ */ new Error("No mock data found"));
8151
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.ERROR });
8152
+ return;
8153
+ }
8154
+ const mockResult = mockData.result;
8155
+ let status;
8156
+ if (this.isGrpcErrorOutput(mockResult)) {
8157
+ const { error, status: errorStatus } = mockResult;
8158
+ status = {
8159
+ code: errorStatus.code,
8160
+ details: errorStatus.details,
8161
+ metadata: deserializeGrpcMetadata(MetadataConstructor, errorStatus.metadata)
8162
+ };
8163
+ const errorObj = Object.assign(new Error(error.message), {
8164
+ name: error.name,
8165
+ stack: error.stack,
8166
+ ...status
8167
+ });
8168
+ callback(errorObj);
8169
+ } else {
8170
+ const { body, status: successStatus, bufferMap, jsonableStringMap } = mockResult;
8171
+ const bufferMapToUse = bufferMap || {};
8172
+ const jsonableStringMapToUse = jsonableStringMap || {};
8173
+ status = {
8174
+ code: successStatus.code,
8175
+ details: successStatus.details,
8176
+ metadata: deserializeGrpcMetadata(MetadataConstructor, successStatus.metadata)
8177
+ };
8178
+ const realResponse = deserializeGrpcPayload(body, bufferMapToUse, jsonableStringMapToUse);
8179
+ callback(null, realResponse);
8180
+ }
8181
+ process.nextTick(() => {
8182
+ if (mockResult.metadata) emitter.emit("metadata", deserializeGrpcMetadata(MetadataConstructor, mockResult.metadata));
8183
+ emitter.emit("status", status);
7872
8184
  });
7873
- return;
7874
- }
7875
- const mockResult = mockData.result;
8185
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: mockResult });
8186
+ SpanUtils.endSpan(spanInfo.span, { code: mockResult.error ? __opentelemetry_api.SpanStatusCode.ERROR : __opentelemetry_api.SpanStatusCode.OK });
8187
+ }).catch((error) => {
8188
+ logger.error(`[GrpcInstrumentation] Error fetching mock data:`, error);
8189
+ callback(error);
8190
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.ERROR });
8191
+ });
8192
+ return emitter;
8193
+ }
8194
+ _handleRecordServerStreamRequest(spanInfo, original, context$4, parsedParams) {
8195
+ let hasErrorOccurred = false;
8196
+ let isSpanCompleted = false;
8197
+ let streamResponses = [];
7876
8198
  let status;
7877
- if (this.isGrpcErrorOutput(mockResult)) {
7878
- const { error, status: errorStatus } = mockResult;
7879
- status = {
7880
- code: errorStatus.code,
7881
- details: errorStatus.details,
7882
- metadata: deserializeGrpcMetadata(MetadataConstructor, errorStatus.metadata)
7883
- };
7884
- const errorObj = Object.assign(new Error(error.message), {
7885
- name: error.name,
7886
- stack: error.stack,
7887
- ...status
7888
- });
7889
- callback(errorObj);
7890
- } else {
7891
- const { body, status: successStatus, bufferMap, jsonableStringMap } = mockResult;
7892
- const bufferMapToUse = bufferMap || {};
7893
- const jsonableStringMapToUse = jsonableStringMap || {};
8199
+ let responseMetadataInitial = {};
8200
+ let serviceError;
8201
+ /**
8202
+ * Completes the span exactly once
8203
+ */
8204
+ const completeSpan = (output, statusCode, errorMessage) => {
8205
+ if (isSpanCompleted) return;
8206
+ isSpanCompleted = true;
8207
+ try {
8208
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: output });
8209
+ SpanUtils.endSpan(spanInfo.span, { code: statusCode });
8210
+ } catch (e) {
8211
+ logger.error(`[GrpcInstrumentation] Error completing span:`, e);
8212
+ }
8213
+ };
8214
+ const inputArgs = [
8215
+ parsedParams.method,
8216
+ parsedParams.serialize,
8217
+ parsedParams.deserialize,
8218
+ parsedParams.argument,
8219
+ parsedParams.metadata,
8220
+ parsedParams.options
8221
+ ];
8222
+ const stream$1 = original.apply(context$4, inputArgs);
8223
+ stream$1.on("data", (data) => {
8224
+ try {
8225
+ const { readableBody, bufferMap, jsonableStringMap } = serializeGrpcPayload(data);
8226
+ streamResponses.push({
8227
+ body: readableBody,
8228
+ bufferMap,
8229
+ jsonableStringMap
8230
+ });
8231
+ } catch (e) {
8232
+ logger.error(`[GrpcInstrumentation] Error serializing stream data:`, e);
8233
+ }
8234
+ });
8235
+ stream$1.on("metadata", (initialMetadata) => {
8236
+ responseMetadataInitial = serializeGrpcMetadata(initialMetadata);
8237
+ });
8238
+ stream$1.on("error", (err) => {
8239
+ serviceError = err;
8240
+ hasErrorOccurred = true;
8241
+ });
8242
+ stream$1.on("status", (responseStatus) => {
7894
8243
  status = {
7895
- code: successStatus.code,
7896
- details: successStatus.details,
7897
- metadata: deserializeGrpcMetadata(MetadataConstructor, successStatus.metadata)
8244
+ code: responseStatus.code,
8245
+ details: responseStatus.details,
8246
+ metadata: serializeGrpcMetadata(responseStatus.metadata)
7898
8247
  };
7899
- const realResponse = deserializeGrpcPayload(body, bufferMapToUse, jsonableStringMapToUse);
7900
- callback(null, realResponse);
7901
- }
7902
- const emitter = Object.assign(new events.EventEmitter(), {
8248
+ if (!hasErrorOccurred && streamResponses.length > 0) completeSpan({
8249
+ body: streamResponses,
8250
+ metadata: responseMetadataInitial,
8251
+ status,
8252
+ bufferMap: {},
8253
+ jsonableStringMap: {}
8254
+ }, __opentelemetry_api.SpanStatusCode.OK);
8255
+ else if (!hasErrorOccurred && streamResponses.length === 0) completeSpan({
8256
+ body: [],
8257
+ metadata: responseMetadataInitial,
8258
+ status,
8259
+ bufferMap: {},
8260
+ jsonableStringMap: {}
8261
+ }, __opentelemetry_api.SpanStatusCode.OK);
8262
+ else if (hasErrorOccurred) {
8263
+ const errorOutput = {
8264
+ error: {
8265
+ message: serviceError.message,
8266
+ name: serviceError.name,
8267
+ stack: serviceError.stack
8268
+ },
8269
+ status,
8270
+ metadata: responseMetadataInitial
8271
+ };
8272
+ completeSpan(errorOutput, __opentelemetry_api.SpanStatusCode.ERROR, serviceError.message);
8273
+ }
8274
+ });
8275
+ return stream$1;
8276
+ }
8277
+ _handleReplayServerStreamRequest(spanInfo, inputValue, MetadataConstructor) {
8278
+ logger.debug(`[GrpcInstrumentation] Replaying gRPC server stream request`);
8279
+ const stream$1 = new stream.Readable({
8280
+ objectMode: true,
8281
+ read() {}
8282
+ });
8283
+ Object.assign(stream$1, {
7903
8284
  cancel() {},
7904
8285
  getPeer: () => "0.0.0.0:0000",
7905
8286
  call: void 0
7906
8287
  });
7907
- process.nextTick(() => {
7908
- if (mockResult.metadata) emitter.emit("metadata", deserializeGrpcMetadata(MetadataConstructor, mockResult.metadata));
7909
- emitter.emit("status", status);
7910
- });
7911
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: mockResult });
7912
- SpanUtils.endSpan(spanInfo.span, { code: mockResult.error ? __opentelemetry_api.SpanStatusCode.ERROR : __opentelemetry_api.SpanStatusCode.OK });
7913
- return emitter;
7914
- }
7915
- _getWaitForReadyPatchFn() {
7916
- const self = this;
7917
- return (original) => {
7918
- return function waitForReady(deadline, callback) {
7919
- if (self.mode === TuskDriftMode.REPLAY) {
7920
- process.nextTick(() => callback());
8288
+ findMockResponseAsync({
8289
+ mockRequestData: {
8290
+ traceId: spanInfo.traceId,
8291
+ spanId: spanInfo.spanId,
8292
+ name: "grpc.client.server_stream",
8293
+ inputValue,
8294
+ packageName: GRPC_MODULE_NAME,
8295
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.GRPC,
8296
+ instrumentationName: this.INSTRUMENTATION_NAME,
8297
+ submoduleName: "client",
8298
+ kind: __opentelemetry_api.SpanKind.CLIENT
8299
+ },
8300
+ tuskDrift: this.tuskDrift
8301
+ }).then((mockData) => {
8302
+ if (!mockData) {
8303
+ logger.warn(`[GrpcInstrumentation] No mock data found for gRPC server stream request: ${inputValue.service}/${inputValue.method}`, inputValue);
8304
+ const error = /* @__PURE__ */ new Error("No mock data found");
8305
+ process.nextTick(() => {
8306
+ stream$1.emit("error", error);
8307
+ stream$1.emit("status", {
8308
+ code: 2,
8309
+ details: "No mock data found",
8310
+ metadata: new MetadataConstructor()
8311
+ });
8312
+ stream$1.push(null);
8313
+ });
8314
+ SpanUtils.endSpan(spanInfo.span, {
8315
+ code: __opentelemetry_api.SpanStatusCode.ERROR,
8316
+ message: "No mock data found"
8317
+ });
8318
+ return;
8319
+ }
8320
+ const mockResult = mockData.result;
8321
+ process.nextTick(() => {
8322
+ if (this.isGrpcErrorOutput(mockResult)) {
8323
+ const { error, status: errorStatus } = mockResult;
8324
+ const status = {
8325
+ code: errorStatus.code,
8326
+ details: errorStatus.details,
8327
+ metadata: deserializeGrpcMetadata(MetadataConstructor, errorStatus.metadata)
8328
+ };
8329
+ if (mockResult.metadata) stream$1.emit("metadata", deserializeGrpcMetadata(MetadataConstructor, mockResult.metadata));
8330
+ const errorObj = Object.assign(new Error(error.message), {
8331
+ name: error.name,
8332
+ stack: error.stack,
8333
+ ...status
8334
+ });
8335
+ stream$1.emit("error", errorObj);
8336
+ stream$1.emit("status", status);
8337
+ stream$1.push(null);
8338
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: mockResult });
8339
+ SpanUtils.endSpan(spanInfo.span, {
8340
+ code: __opentelemetry_api.SpanStatusCode.ERROR,
8341
+ message: error.message
8342
+ });
8343
+ } else {
8344
+ const { body, status: successStatus } = mockResult;
8345
+ const status = {
8346
+ code: successStatus.code,
8347
+ details: successStatus.details,
8348
+ metadata: deserializeGrpcMetadata(MetadataConstructor, successStatus.metadata)
8349
+ };
8350
+ if (mockResult.metadata) stream$1.emit("metadata", deserializeGrpcMetadata(MetadataConstructor, mockResult.metadata));
8351
+ if (Array.isArray(body)) body.forEach((item) => {
8352
+ const bufferMapToUse = item.bufferMap || {};
8353
+ const jsonableStringMapToUse = item.jsonableStringMap || {};
8354
+ const realResponse = deserializeGrpcPayload(item.body, bufferMapToUse, jsonableStringMapToUse);
8355
+ stream$1.push(realResponse);
8356
+ });
8357
+ stream$1.push(null);
8358
+ stream$1.emit("status", status);
8359
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: mockResult });
8360
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
8361
+ }
8362
+ });
8363
+ }).catch((error) => {
8364
+ logger.error(`[GrpcInstrumentation] Error fetching mock data for server stream:`, error);
8365
+ process.nextTick(() => {
8366
+ stream$1.emit("error", error);
8367
+ stream$1.emit("status", {
8368
+ code: 2,
8369
+ details: error.message,
8370
+ metadata: new MetadataConstructor()
8371
+ });
8372
+ stream$1.push(null);
8373
+ });
8374
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.ERROR });
8375
+ });
8376
+ return stream$1;
8377
+ }
8378
+ _getWaitForReadyPatchFn() {
8379
+ const self = this;
8380
+ return (original) => {
8381
+ return function waitForReady(deadline, callback) {
8382
+ if (self.mode === TuskDriftMode.REPLAY) {
8383
+ process.nextTick(() => callback());
7921
8384
  return;
7922
8385
  } else return original.apply(this, [deadline, callback]);
7923
8386
  };
7924
8387
  };
7925
8388
  }
7926
- _getClosePatchFn() {
8389
+ _getClosePatchFn() {
8390
+ const self = this;
8391
+ return (original) => {
8392
+ return function close() {
8393
+ if (self.mode === TuskDriftMode.REPLAY) return;
8394
+ else return original.apply(this, arguments);
8395
+ };
8396
+ };
8397
+ }
8398
+ _getGetChannelPatchFn() {
8399
+ const self = this;
8400
+ return (original) => {
8401
+ return function getChannel() {
8402
+ if (self.mode === TuskDriftMode.REPLAY) return {};
8403
+ else return original.apply(this, arguments);
8404
+ };
8405
+ };
8406
+ }
8407
+ _wrap(target, propertyName, wrapper) {
8408
+ wrap(target, propertyName, wrapper);
8409
+ }
8410
+ };
8411
+ GrpcInstrumentation.metadataStore = /* @__PURE__ */ new Map();
8412
+
8413
+ //#endregion
8414
+ //#region src/instrumentation/libraries/firestore/mocks/TdFirestoreDocumentMock.ts
8415
+ /**
8416
+ * Mock Firestore DocumentSnapshot for replay mode
8417
+ * Mimics the Firestore DocumentSnapshot API
8418
+ */
8419
+ var TdFirestoreDocumentMock = class {
8420
+ constructor(documentData) {
8421
+ this.documentData = documentData;
8422
+ }
8423
+ /**
8424
+ * The document's identifier within its collection
8425
+ */
8426
+ get id() {
8427
+ return this.documentData.id;
8428
+ }
8429
+ /**
8430
+ * Whether the document exists
8431
+ */
8432
+ get exists() {
8433
+ return this.documentData.exists;
8434
+ }
8435
+ /**
8436
+ * A DocumentReference to the document location
8437
+ */
8438
+ get ref() {
8439
+ return {
8440
+ id: this.documentData.id,
8441
+ path: this.documentData.path
8442
+ };
8443
+ }
8444
+ /**
8445
+ * The time the document was created (if available)
8446
+ */
8447
+ get createTime() {
8448
+ return this.documentData.createTime ? {
8449
+ seconds: this.documentData.createTime.seconds,
8450
+ nanoseconds: this.documentData.createTime.nanoseconds,
8451
+ toDate: () => /* @__PURE__ */ new Date(this.documentData.createTime.seconds * 1e3 + this.documentData.createTime.nanoseconds / 1e6)
8452
+ } : null;
8453
+ }
8454
+ /**
8455
+ * The time the document was last updated (if available)
8456
+ */
8457
+ get updateTime() {
8458
+ return this.documentData.updateTime ? {
8459
+ seconds: this.documentData.updateTime.seconds,
8460
+ nanoseconds: this.documentData.updateTime.nanoseconds,
8461
+ toDate: () => /* @__PURE__ */ new Date(this.documentData.updateTime.seconds * 1e3 + this.documentData.updateTime.nanoseconds / 1e6)
8462
+ } : null;
8463
+ }
8464
+ /**
8465
+ * The time the document was read (if available)
8466
+ */
8467
+ get readTime() {
8468
+ return this.documentData.readTime ? {
8469
+ seconds: this.documentData.readTime.seconds,
8470
+ nanoseconds: this.documentData.readTime.nanoseconds,
8471
+ toDate: () => /* @__PURE__ */ new Date(this.documentData.readTime.seconds * 1e3 + this.documentData.readTime.nanoseconds / 1e6)
8472
+ } : null;
8473
+ }
8474
+ /**
8475
+ * Retrieves all fields in the document as an object
8476
+ */
8477
+ data() {
8478
+ return this.documentData.data;
8479
+ }
8480
+ /**
8481
+ * Retrieves the field specified by fieldPath
8482
+ */
8483
+ get(fieldPath) {
8484
+ if (!this.documentData.data) return;
8485
+ return this.documentData.data[fieldPath];
8486
+ }
8487
+ /**
8488
+ * Returns true if the document's data and path are equal to the provided value
8489
+ */
8490
+ isEqual(other) {
8491
+ return this.documentData.path === other.documentData.path;
8492
+ }
8493
+ };
8494
+
8495
+ //#endregion
8496
+ //#region src/instrumentation/libraries/firestore/mocks/TdFirestoreQueryMock.ts
8497
+ /**
8498
+ * Mock Firestore QuerySnapshot for replay mode
8499
+ * Mimics the Firestore QuerySnapshot API
8500
+ */
8501
+ var TdFirestoreQueryMock = class {
8502
+ constructor(queryResult) {
8503
+ this.queryResult = queryResult;
8504
+ this._docs = queryResult.docs.map((doc) => new TdFirestoreDocumentMock(doc));
8505
+ }
8506
+ /**
8507
+ * An array of all the documents in the QuerySnapshot
8508
+ */
8509
+ get docs() {
8510
+ return this._docs;
8511
+ }
8512
+ /**
8513
+ * The number of documents in the QuerySnapshot
8514
+ */
8515
+ get size() {
8516
+ return this.queryResult.size;
8517
+ }
8518
+ /**
8519
+ * True if there are no documents in the QuerySnapshot
8520
+ */
8521
+ get empty() {
8522
+ return this.queryResult.empty;
8523
+ }
8524
+ /**
8525
+ * The time the query snapshot was read
8526
+ */
8527
+ get readTime() {
8528
+ return this.queryResult.readTime ? {
8529
+ seconds: this.queryResult.readTime.seconds,
8530
+ nanoseconds: this.queryResult.readTime.nanoseconds,
8531
+ toDate: () => /* @__PURE__ */ new Date(this.queryResult.readTime.seconds * 1e3 + this.queryResult.readTime.nanoseconds / 1e6)
8532
+ } : null;
8533
+ }
8534
+ /**
8535
+ * The query on which you called get or onSnapshot
8536
+ */
8537
+ get query() {
8538
+ return {};
8539
+ }
8540
+ /**
8541
+ * Enumerates all of the documents in the QuerySnapshot
8542
+ */
8543
+ forEach(callback, thisArg) {
8544
+ this._docs.forEach(callback, thisArg);
8545
+ }
8546
+ /**
8547
+ * Returns an array of the documents changes since the last snapshot
8548
+ */
8549
+ docChanges() {
8550
+ return [];
8551
+ }
8552
+ /**
8553
+ * Returns true if the document data and path are equal to this QuerySnapshot
8554
+ */
8555
+ isEqual(other) {
8556
+ if (this.size !== other.size) return false;
8557
+ return this.size === other.size && this.empty === other.empty;
8558
+ }
8559
+ };
8560
+
8561
+ //#endregion
8562
+ //#region src/instrumentation/libraries/firestore/mocks/TdFirestoreWriteResultMock.ts
8563
+ /**
8564
+ * Mock Firestore WriteResult for replay mode
8565
+ * Mimics the Firestore WriteResult API
8566
+ */
8567
+ var TdFirestoreWriteResultMock = class {
8568
+ constructor(result) {
8569
+ this.result = result;
8570
+ }
8571
+ /**
8572
+ * The write time as reported by the server
8573
+ */
8574
+ get writeTime() {
8575
+ return this.result.writeTime ? {
8576
+ seconds: this.result.writeTime.seconds,
8577
+ nanoseconds: this.result.writeTime.nanoseconds,
8578
+ toDate: () => /* @__PURE__ */ new Date(this.result.writeTime.seconds * 1e3 + this.result.writeTime.nanoseconds / 1e6)
8579
+ } : null;
8580
+ }
8581
+ /**
8582
+ * Returns true if this WriteResult is equal to the provided one
8583
+ */
8584
+ isEqual(other) {
8585
+ if (!this.writeTime || !other.writeTime) return this.writeTime === other.writeTime;
8586
+ return this.writeTime.seconds === other.writeTime.seconds && this.writeTime.nanoseconds === other.writeTime.nanoseconds;
8587
+ }
8588
+ };
8589
+
8590
+ //#endregion
8591
+ //#region src/instrumentation/libraries/firestore/Instrumentation.ts
8592
+ const FIRESTORE_VERSION = "7.*";
8593
+ const PACKAGE_NAME = "@google-cloud/firestore";
8594
+ var FirestoreInstrumentation = class extends TdInstrumentationBase {
8595
+ constructor(config = {}) {
8596
+ super(PACKAGE_NAME, config);
8597
+ this.INSTRUMENTATION_NAME = "FirestoreInstrumentation";
8598
+ this.originalCollectionDocFn = null;
8599
+ this.mode = config.mode || TuskDriftMode.DISABLED;
8600
+ this.tuskDrift = TuskDriftCore.getInstance();
8601
+ }
8602
+ init() {
8603
+ return [new TdInstrumentationNodeModule({
8604
+ name: PACKAGE_NAME,
8605
+ supportedVersions: [FIRESTORE_VERSION],
8606
+ files: [
8607
+ new TdInstrumentationNodeModuleFile({
8608
+ name: "@google-cloud/firestore/build/src/reference/document-reference.js",
8609
+ supportedVersions: [FIRESTORE_VERSION],
8610
+ patch: (moduleExports) => this._patchDocumentReference(moduleExports)
8611
+ }),
8612
+ new TdInstrumentationNodeModuleFile({
8613
+ name: "@google-cloud/firestore/build/src/reference/collection-reference.js",
8614
+ supportedVersions: [FIRESTORE_VERSION],
8615
+ patch: (moduleExports) => this._patchCollectionReference(moduleExports)
8616
+ }),
8617
+ new TdInstrumentationNodeModuleFile({
8618
+ name: "@google-cloud/firestore/build/src/reference/query.js",
8619
+ supportedVersions: [FIRESTORE_VERSION],
8620
+ patch: (moduleExports) => this._patchQuery(moduleExports)
8621
+ })
8622
+ ]
8623
+ })];
8624
+ }
8625
+ _patchDocumentReference(moduleExports) {
8626
+ logger.debug(`[FirestoreInstrumentation] Patching DocumentReference in ${this.mode} mode`);
8627
+ if (this.isModulePatched(moduleExports)) {
8628
+ logger.debug(`[FirestoreInstrumentation] DocumentReference already patched, skipping`);
8629
+ return moduleExports;
8630
+ }
8631
+ const DocumentReference = moduleExports.DocumentReference;
8632
+ if (!DocumentReference || !DocumentReference.prototype) {
8633
+ logger.warn(`[FirestoreInstrumentation] DocumentReference.prototype not found`);
8634
+ return moduleExports;
8635
+ }
8636
+ this._wrap(DocumentReference.prototype, "get", this._getDocumentGetPatchFn());
8637
+ this._wrap(DocumentReference.prototype, "create", this._getDocumentCreatePatchFn());
8638
+ this._wrap(DocumentReference.prototype, "set", this._getDocumentSetPatchFn());
8639
+ this._wrap(DocumentReference.prototype, "update", this._getDocumentUpdatePatchFn());
8640
+ this._wrap(DocumentReference.prototype, "delete", this._getDocumentDeletePatchFn());
8641
+ this.markModuleAsPatched(moduleExports);
8642
+ logger.debug(`[FirestoreInstrumentation] DocumentReference patching complete`);
8643
+ return moduleExports;
8644
+ }
8645
+ _patchCollectionReference(moduleExports) {
8646
+ logger.debug(`[FirestoreInstrumentation] Patching CollectionReference in ${this.mode} mode`);
8647
+ if (this.isModulePatched(moduleExports)) {
8648
+ logger.debug(`[FirestoreInstrumentation] CollectionReference already patched, skipping`);
8649
+ return moduleExports;
8650
+ }
8651
+ const CollectionReference = moduleExports.CollectionReference;
8652
+ if (!CollectionReference || !CollectionReference.prototype) {
8653
+ logger.warn(`[FirestoreInstrumentation] CollectionReference.prototype not found`);
8654
+ return moduleExports;
8655
+ }
8656
+ this.originalCollectionDocFn = CollectionReference.prototype.doc;
8657
+ this._wrap(CollectionReference.prototype, "add", this._getCollectionAddPatchFn());
8658
+ this._wrap(CollectionReference.prototype, "doc", this._getCollectionDocPatchFn());
8659
+ this.markModuleAsPatched(moduleExports);
8660
+ logger.debug(`[FirestoreInstrumentation] CollectionReference patching complete`);
8661
+ return moduleExports;
8662
+ }
8663
+ _patchQuery(moduleExports) {
8664
+ logger.debug(`[FirestoreInstrumentation] Patching Query in ${this.mode} mode`);
8665
+ if (this.isModulePatched(moduleExports)) {
8666
+ logger.debug(`[FirestoreInstrumentation] Query already patched, skipping`);
8667
+ return moduleExports;
8668
+ }
8669
+ const Query = moduleExports.Query;
8670
+ if (!Query || !Query.prototype) {
8671
+ logger.warn(`[FirestoreInstrumentation] Query.prototype not found`);
8672
+ return moduleExports;
8673
+ }
8674
+ this._wrap(Query.prototype, "get", this._getQueryGetPatchFn());
8675
+ this.markModuleAsPatched(moduleExports);
8676
+ logger.debug(`[FirestoreInstrumentation] Query patching complete`);
8677
+ return moduleExports;
8678
+ }
8679
+ _getDocumentGetPatchFn() {
8680
+ const self = this;
8681
+ return (originalGet) => {
8682
+ return function() {
8683
+ const inputValue = {
8684
+ operation: "document.get",
8685
+ path: this.path
8686
+ };
8687
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
8688
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalGet.call(this), {
8689
+ name: "firestore.document.get",
8690
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8691
+ submodule: "document",
8692
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8693
+ packageName: PACKAGE_NAME,
8694
+ instrumentationName: self.INSTRUMENTATION_NAME,
8695
+ inputValue,
8696
+ isPreAppStart: false,
8697
+ stopRecordingChildSpans: true
8698
+ }, (spanInfo) => {
8699
+ return self._handleReplayDocumentGet(spanInfo, inputValue);
8700
+ });
8701
+ } });
8702
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
8703
+ originalFunctionCall: () => originalGet.call(this),
8704
+ recordModeHandler: ({ isPreAppStart }) => {
8705
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalGet.call(this), {
8706
+ name: "firestore.document.get",
8707
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8708
+ submodule: "document",
8709
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8710
+ packageName: PACKAGE_NAME,
8711
+ instrumentationName: self.INSTRUMENTATION_NAME,
8712
+ inputValue,
8713
+ isPreAppStart,
8714
+ stopRecordingChildSpans: true
8715
+ }, (spanInfo) => {
8716
+ return self._handleRecordDocumentGet(spanInfo, originalGet, this);
8717
+ });
8718
+ },
8719
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
8720
+ });
8721
+ else return originalGet.call(this);
8722
+ };
8723
+ };
8724
+ }
8725
+ async _handleRecordDocumentGet(spanInfo, originalGet, context$4) {
8726
+ const snapshot = await originalGet.call(context$4);
8727
+ const documentResult = {
8728
+ id: snapshot.id,
8729
+ path: snapshot.ref.path,
8730
+ exists: snapshot.exists,
8731
+ data: snapshot.exists ? snapshot.data() : void 0,
8732
+ createTime: snapshot.createTime ? {
8733
+ seconds: snapshot.createTime.seconds,
8734
+ nanoseconds: snapshot.createTime.nanoseconds
8735
+ } : void 0,
8736
+ updateTime: snapshot.updateTime ? {
8737
+ seconds: snapshot.updateTime.seconds,
8738
+ nanoseconds: snapshot.updateTime.nanoseconds
8739
+ } : void 0,
8740
+ readTime: snapshot.readTime ? {
8741
+ seconds: snapshot.readTime.seconds,
8742
+ nanoseconds: snapshot.readTime.nanoseconds
8743
+ } : void 0
8744
+ };
8745
+ try {
8746
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: documentResult });
8747
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
8748
+ } catch {
8749
+ logger.error(`[FirestoreInstrumentation] Error updating span attributes for document.get`);
8750
+ }
8751
+ return snapshot;
8752
+ }
8753
+ async _handleReplayDocumentGet(spanInfo, inputValue) {
8754
+ logger.debug(`[FirestoreInstrumentation] Replaying document.get`);
8755
+ const mockData = await findMockResponseAsync({
8756
+ mockRequestData: {
8757
+ traceId: spanInfo.traceId,
8758
+ spanId: spanInfo.spanId,
8759
+ name: "firestore.document.get",
8760
+ inputValue: createMockInputValue(inputValue),
8761
+ packageName: PACKAGE_NAME,
8762
+ instrumentationName: this.INSTRUMENTATION_NAME,
8763
+ submoduleName: "document",
8764
+ kind: __opentelemetry_api.SpanKind.CLIENT
8765
+ },
8766
+ tuskDrift: this.tuskDrift
8767
+ });
8768
+ if (!mockData) {
8769
+ logger.warn(`[FirestoreInstrumentation] No mock data found for document.get: ${inputValue.path}`);
8770
+ return Promise.reject(/* @__PURE__ */ new Error("No mock data found"));
8771
+ }
8772
+ const documentResult = mockData.result;
8773
+ return new TdFirestoreDocumentMock(documentResult);
8774
+ }
8775
+ _getDocumentCreatePatchFn() {
8776
+ const self = this;
8777
+ return (originalCreate) => {
8778
+ return function(data) {
8779
+ const inputValue = {
8780
+ operation: "document.create",
8781
+ path: this.path,
8782
+ data
8783
+ };
8784
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
8785
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalCreate.call(this, data), {
8786
+ name: "firestore.document.create",
8787
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8788
+ submodule: "document",
8789
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8790
+ packageName: PACKAGE_NAME,
8791
+ instrumentationName: self.INSTRUMENTATION_NAME,
8792
+ inputValue,
8793
+ isPreAppStart: false,
8794
+ stopRecordingChildSpans: true
8795
+ }, (spanInfo) => {
8796
+ return self._handleReplayDocumentWrite(spanInfo, inputValue);
8797
+ });
8798
+ } });
8799
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
8800
+ originalFunctionCall: () => originalCreate.call(this, data),
8801
+ recordModeHandler: ({ isPreAppStart }) => {
8802
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalCreate.call(this, data), {
8803
+ name: "firestore.document.create",
8804
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8805
+ submodule: "document",
8806
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8807
+ packageName: PACKAGE_NAME,
8808
+ instrumentationName: self.INSTRUMENTATION_NAME,
8809
+ inputValue,
8810
+ isPreAppStart,
8811
+ stopRecordingChildSpans: true
8812
+ }, (spanInfo) => {
8813
+ return self._handleRecordDocumentWrite(spanInfo, originalCreate, this, data);
8814
+ });
8815
+ },
8816
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
8817
+ });
8818
+ else return originalCreate.call(this, data);
8819
+ };
8820
+ };
8821
+ }
8822
+ _getDocumentSetPatchFn() {
8823
+ const self = this;
8824
+ return (originalSet) => {
8825
+ return function(data, options) {
8826
+ const inputValue = {
8827
+ operation: "document.set",
8828
+ path: this.path,
8829
+ data,
8830
+ options
8831
+ };
8832
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
8833
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalSet.call(this, data, options), {
8834
+ name: "firestore.document.set",
8835
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8836
+ submodule: "document",
8837
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8838
+ packageName: PACKAGE_NAME,
8839
+ instrumentationName: self.INSTRUMENTATION_NAME,
8840
+ inputValue,
8841
+ isPreAppStart: false,
8842
+ stopRecordingChildSpans: true
8843
+ }, (spanInfo) => {
8844
+ return self._handleReplayDocumentWrite(spanInfo, inputValue);
8845
+ });
8846
+ } });
8847
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
8848
+ originalFunctionCall: () => originalSet.call(this, data, options),
8849
+ recordModeHandler: ({ isPreAppStart }) => {
8850
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalSet.call(this, data, options), {
8851
+ name: "firestore.document.set",
8852
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8853
+ submodule: "document",
8854
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8855
+ packageName: PACKAGE_NAME,
8856
+ instrumentationName: self.INSTRUMENTATION_NAME,
8857
+ inputValue,
8858
+ isPreAppStart,
8859
+ stopRecordingChildSpans: true
8860
+ }, (spanInfo) => {
8861
+ return self._handleRecordDocumentWrite(spanInfo, originalSet, this, data, options);
8862
+ });
8863
+ },
8864
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
8865
+ });
8866
+ else return originalSet.call(this, data, options);
8867
+ };
8868
+ };
8869
+ }
8870
+ _getDocumentUpdatePatchFn() {
8871
+ const self = this;
8872
+ return (originalUpdate) => {
8873
+ return function(...args) {
8874
+ const inputValue = {
8875
+ operation: "document.update",
8876
+ path: this.path,
8877
+ data: args.length === 1 ? args[0] : args
8878
+ };
8879
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
8880
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalUpdate.apply(this, args), {
8881
+ name: "firestore.document.update",
8882
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8883
+ submodule: "document",
8884
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8885
+ packageName: PACKAGE_NAME,
8886
+ instrumentationName: self.INSTRUMENTATION_NAME,
8887
+ inputValue,
8888
+ isPreAppStart: false,
8889
+ stopRecordingChildSpans: true
8890
+ }, (spanInfo) => {
8891
+ return self._handleReplayDocumentWrite(spanInfo, inputValue);
8892
+ });
8893
+ } });
8894
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
8895
+ originalFunctionCall: () => originalUpdate.apply(this, args),
8896
+ recordModeHandler: ({ isPreAppStart }) => {
8897
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalUpdate.apply(this, args), {
8898
+ name: "firestore.document.update",
8899
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8900
+ submodule: "document",
8901
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8902
+ packageName: PACKAGE_NAME,
8903
+ instrumentationName: self.INSTRUMENTATION_NAME,
8904
+ inputValue,
8905
+ isPreAppStart,
8906
+ stopRecordingChildSpans: true
8907
+ }, (spanInfo) => {
8908
+ return self._handleRecordDocumentWrite(spanInfo, originalUpdate, this, ...args);
8909
+ });
8910
+ },
8911
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
8912
+ });
8913
+ else return originalUpdate.apply(this, args);
8914
+ };
8915
+ };
8916
+ }
8917
+ _getDocumentDeletePatchFn() {
7927
8918
  const self = this;
7928
- return (original) => {
7929
- return function close() {
7930
- if (self.mode === TuskDriftMode.REPLAY) return;
7931
- else return original.apply(this, arguments);
8919
+ return (originalDelete) => {
8920
+ return function(precondition) {
8921
+ const inputValue = {
8922
+ operation: "document.delete",
8923
+ path: this.path,
8924
+ options: precondition
8925
+ };
8926
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
8927
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalDelete.call(this, precondition), {
8928
+ name: "firestore.document.delete",
8929
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8930
+ submodule: "document",
8931
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8932
+ packageName: PACKAGE_NAME,
8933
+ instrumentationName: self.INSTRUMENTATION_NAME,
8934
+ inputValue,
8935
+ isPreAppStart: false,
8936
+ stopRecordingChildSpans: true
8937
+ }, (spanInfo) => {
8938
+ return self._handleReplayDocumentWrite(spanInfo, inputValue);
8939
+ });
8940
+ } });
8941
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
8942
+ originalFunctionCall: () => originalDelete.call(this, precondition),
8943
+ recordModeHandler: ({ isPreAppStart }) => {
8944
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalDelete.call(this, precondition), {
8945
+ name: "firestore.document.delete",
8946
+ kind: __opentelemetry_api.SpanKind.CLIENT,
8947
+ submodule: "document",
8948
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
8949
+ packageName: PACKAGE_NAME,
8950
+ instrumentationName: self.INSTRUMENTATION_NAME,
8951
+ inputValue,
8952
+ isPreAppStart,
8953
+ stopRecordingChildSpans: true
8954
+ }, (spanInfo) => {
8955
+ return self._handleRecordDocumentWrite(spanInfo, originalDelete, this, precondition);
8956
+ });
8957
+ },
8958
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
8959
+ });
8960
+ else return originalDelete.call(this, precondition);
7932
8961
  };
7933
8962
  };
7934
8963
  }
7935
- _getGetChannelPatchFn() {
8964
+ async _handleRecordDocumentWrite(spanInfo, originalWrite, context$4, ...args) {
8965
+ const writeResult = await originalWrite.apply(context$4, args);
8966
+ const result = { writeTime: writeResult.writeTime ? {
8967
+ seconds: writeResult.writeTime.seconds,
8968
+ nanoseconds: writeResult.writeTime.nanoseconds
8969
+ } : void 0 };
8970
+ try {
8971
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: result });
8972
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
8973
+ } catch {
8974
+ logger.error(`[FirestoreInstrumentation] Error updating span attributes for document.write`);
8975
+ }
8976
+ return writeResult;
8977
+ }
8978
+ async _handleReplayDocumentWrite(spanInfo, inputValue) {
8979
+ logger.debug(`[FirestoreInstrumentation] Replaying document write: ${inputValue.operation}`);
8980
+ const mockData = await findMockResponseAsync({
8981
+ mockRequestData: {
8982
+ traceId: spanInfo.traceId,
8983
+ spanId: spanInfo.spanId,
8984
+ name: `firestore.${inputValue.operation}`,
8985
+ inputValue: createMockInputValue(inputValue),
8986
+ packageName: PACKAGE_NAME,
8987
+ instrumentationName: this.INSTRUMENTATION_NAME,
8988
+ submoduleName: "document",
8989
+ kind: __opentelemetry_api.SpanKind.CLIENT
8990
+ },
8991
+ tuskDrift: this.tuskDrift
8992
+ });
8993
+ if (!mockData) {
8994
+ logger.warn(`[FirestoreInstrumentation] No mock data found for ${inputValue.operation}: ${inputValue.path}`);
8995
+ return Promise.reject(/* @__PURE__ */ new Error("No mock data found"));
8996
+ }
8997
+ const writeResult = mockData.result;
8998
+ return new TdFirestoreWriteResultMock(writeResult);
8999
+ }
9000
+ _getCollectionAddPatchFn() {
7936
9001
  const self = this;
7937
- return (original) => {
7938
- return function getChannel() {
7939
- if (self.mode === TuskDriftMode.REPLAY) return {};
7940
- else return original.apply(this, arguments);
9002
+ return (originalAdd) => {
9003
+ return function(data) {
9004
+ const inputValue = {
9005
+ operation: "collection.add",
9006
+ path: this.path,
9007
+ data
9008
+ };
9009
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
9010
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalAdd.call(this, data), {
9011
+ name: "firestore.collection.add",
9012
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9013
+ submodule: "collection",
9014
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9015
+ packageName: PACKAGE_NAME,
9016
+ instrumentationName: self.INSTRUMENTATION_NAME,
9017
+ inputValue,
9018
+ isPreAppStart: false,
9019
+ stopRecordingChildSpans: true
9020
+ }, (spanInfo) => {
9021
+ return self._handleReplayCollectionAdd(spanInfo, inputValue, this);
9022
+ });
9023
+ } });
9024
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
9025
+ originalFunctionCall: () => originalAdd.call(this, data),
9026
+ recordModeHandler: ({ isPreAppStart }) => {
9027
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalAdd.call(this, data), {
9028
+ name: "firestore.collection.add",
9029
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9030
+ submodule: "collection",
9031
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9032
+ packageName: PACKAGE_NAME,
9033
+ instrumentationName: self.INSTRUMENTATION_NAME,
9034
+ inputValue,
9035
+ isPreAppStart,
9036
+ stopRecordingChildSpans: true
9037
+ }, (spanInfo) => {
9038
+ return self._handleRecordCollectionAdd(spanInfo, originalAdd, this, data);
9039
+ });
9040
+ },
9041
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
9042
+ });
9043
+ else return originalAdd.call(this, data);
9044
+ };
9045
+ };
9046
+ }
9047
+ async _handleRecordCollectionAdd(spanInfo, originalAdd, context$4, data) {
9048
+ const docRef = await originalAdd.call(context$4, data);
9049
+ const result = {
9050
+ id: docRef.id,
9051
+ path: docRef.path
9052
+ };
9053
+ try {
9054
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: result });
9055
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
9056
+ } catch {
9057
+ logger.error(`[FirestoreInstrumentation] Error updating span attributes for collection.add`);
9058
+ }
9059
+ return docRef;
9060
+ }
9061
+ async _handleReplayCollectionAdd(spanInfo, inputValue, collectionRef) {
9062
+ logger.debug(`[FirestoreInstrumentation] Replaying collection.add`);
9063
+ const mockData = await findMockResponseAsync({
9064
+ mockRequestData: {
9065
+ traceId: spanInfo.traceId,
9066
+ spanId: spanInfo.spanId,
9067
+ name: "firestore.collection.add",
9068
+ inputValue: createMockInputValue(inputValue),
9069
+ packageName: PACKAGE_NAME,
9070
+ instrumentationName: this.INSTRUMENTATION_NAME,
9071
+ submoduleName: "collection",
9072
+ kind: __opentelemetry_api.SpanKind.CLIENT
9073
+ },
9074
+ tuskDrift: this.tuskDrift
9075
+ });
9076
+ if (!mockData) {
9077
+ logger.warn(`[FirestoreInstrumentation] No mock data found for collection.add: ${inputValue.path}`);
9078
+ return Promise.reject(/* @__PURE__ */ new Error("No mock data found"));
9079
+ }
9080
+ const recordedId = mockData.result.id;
9081
+ if (!this.originalCollectionDocFn) {
9082
+ logger.error(`[FirestoreInstrumentation] Original doc function not available`);
9083
+ return Promise.reject(/* @__PURE__ */ new Error("Original doc function not available"));
9084
+ }
9085
+ return this.originalCollectionDocFn.call(collectionRef, recordedId);
9086
+ }
9087
+ _getCollectionDocPatchFn() {
9088
+ const self = this;
9089
+ return (originalDoc) => {
9090
+ return function(documentPath) {
9091
+ const collectionPath = this.path;
9092
+ const inputValue = {
9093
+ operation: "collection.doc",
9094
+ path: collectionPath,
9095
+ documentId: documentPath
9096
+ };
9097
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
9098
+ return SpanUtils.createAndExecuteSpan(self.mode, () => documentPath ? originalDoc.call(this, documentPath) : originalDoc.call(this), {
9099
+ name: "firestore.collection.doc",
9100
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9101
+ submodule: "collection",
9102
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9103
+ packageName: PACKAGE_NAME,
9104
+ instrumentationName: self.INSTRUMENTATION_NAME,
9105
+ inputValue,
9106
+ isPreAppStart: false,
9107
+ stopRecordingChildSpans: true
9108
+ }, (spanInfo) => {
9109
+ const mockData = findMockResponseSync({
9110
+ mockRequestData: {
9111
+ traceId: spanInfo.traceId,
9112
+ spanId: spanInfo.spanId,
9113
+ name: "firestore.collection.doc",
9114
+ inputValue: createMockInputValue(inputValue),
9115
+ packageName: PACKAGE_NAME,
9116
+ instrumentationName: self.INSTRUMENTATION_NAME,
9117
+ submoduleName: "collection",
9118
+ kind: __opentelemetry_api.SpanKind.CLIENT
9119
+ },
9120
+ tuskDrift: self.tuskDrift
9121
+ });
9122
+ if (!mockData) {
9123
+ logger.warn(`[FirestoreInstrumentation] No mock data found for collection.doc: ${collectionPath}`);
9124
+ throw new Error("No mock data found for collection.doc");
9125
+ }
9126
+ const recordedId = mockData.result.id;
9127
+ logger.debug(`[FirestoreInstrumentation] replaying doc call with recorded id: ${recordedId}`);
9128
+ const docRef = originalDoc.call(this, recordedId);
9129
+ logger.debug(`[FirestoreInstrumentation] doc ref, id`, docRef, recordedId);
9130
+ return docRef;
9131
+ });
9132
+ } });
9133
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
9134
+ originalFunctionCall: () => documentPath ? originalDoc.call(this, documentPath) : originalDoc.call(this),
9135
+ recordModeHandler: ({ isPreAppStart }) => {
9136
+ return SpanUtils.createAndExecuteSpan(self.mode, () => documentPath ? originalDoc.call(this, documentPath) : originalDoc.call(this), {
9137
+ name: "firestore.collection.doc",
9138
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9139
+ submodule: "collection",
9140
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9141
+ packageName: PACKAGE_NAME,
9142
+ instrumentationName: self.INSTRUMENTATION_NAME,
9143
+ inputValue,
9144
+ isPreAppStart,
9145
+ stopRecordingChildSpans: true
9146
+ }, (spanInfo) => {
9147
+ const docRef = documentPath ? originalDoc.call(this, documentPath) : originalDoc.call(this);
9148
+ const result = {
9149
+ id: docRef.id,
9150
+ path: docRef.path
9151
+ };
9152
+ try {
9153
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: result });
9154
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
9155
+ } catch {
9156
+ logger.error(`[FirestoreInstrumentation] Error updating span attributes for collection.doc`);
9157
+ }
9158
+ return docRef;
9159
+ });
9160
+ },
9161
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
9162
+ });
9163
+ else return documentPath ? originalDoc.call(this, documentPath) : originalDoc.call(this);
9164
+ };
9165
+ };
9166
+ }
9167
+ _getQueryGetPatchFn() {
9168
+ const self = this;
9169
+ return (originalGet) => {
9170
+ return function() {
9171
+ const inputValue = {
9172
+ operation: "query.get",
9173
+ path: this._queryOptions?.parentPath?.formattedName || "unknown"
9174
+ };
9175
+ if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({ replayModeHandler: () => {
9176
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalGet.call(this), {
9177
+ name: "firestore.query.get",
9178
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9179
+ submodule: "query",
9180
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9181
+ packageName: PACKAGE_NAME,
9182
+ instrumentationName: self.INSTRUMENTATION_NAME,
9183
+ inputValue,
9184
+ isPreAppStart: false,
9185
+ stopRecordingChildSpans: true
9186
+ }, (spanInfo) => {
9187
+ return self._handleReplayQueryGet(spanInfo, inputValue);
9188
+ });
9189
+ } });
9190
+ else if (self.mode === TuskDriftMode.RECORD) return handleRecordMode({
9191
+ originalFunctionCall: () => originalGet.call(this),
9192
+ recordModeHandler: ({ isPreAppStart }) => {
9193
+ return SpanUtils.createAndExecuteSpan(self.mode, () => originalGet.call(this), {
9194
+ name: "firestore.query.get",
9195
+ kind: __opentelemetry_api.SpanKind.CLIENT,
9196
+ submodule: "query",
9197
+ packageType: __use_tusk_drift_schemas_core_span.PackageType.FIRESTORE,
9198
+ packageName: PACKAGE_NAME,
9199
+ instrumentationName: self.INSTRUMENTATION_NAME,
9200
+ inputValue,
9201
+ isPreAppStart,
9202
+ stopRecordingChildSpans: true
9203
+ }, (spanInfo) => {
9204
+ return self._handleRecordQueryGet(spanInfo, originalGet, this);
9205
+ });
9206
+ },
9207
+ spanKind: __opentelemetry_api.SpanKind.CLIENT
9208
+ });
9209
+ else return originalGet.call(this);
7941
9210
  };
7942
9211
  };
7943
9212
  }
9213
+ async _handleRecordQueryGet(spanInfo, originalGet, context$4) {
9214
+ const querySnapshot = await originalGet.call(context$4);
9215
+ const queryResult = {
9216
+ docs: querySnapshot.docs.map((doc) => ({
9217
+ id: doc.id,
9218
+ path: doc.ref.path,
9219
+ exists: doc.exists,
9220
+ data: doc.exists ? doc.data() : void 0,
9221
+ createTime: doc.createTime ? {
9222
+ seconds: doc.createTime.seconds,
9223
+ nanoseconds: doc.createTime.nanoseconds
9224
+ } : void 0,
9225
+ updateTime: doc.updateTime ? {
9226
+ seconds: doc.updateTime.seconds,
9227
+ nanoseconds: doc.updateTime.nanoseconds
9228
+ } : void 0,
9229
+ readTime: doc.readTime ? {
9230
+ seconds: doc.readTime.seconds,
9231
+ nanoseconds: doc.readTime.nanoseconds
9232
+ } : void 0
9233
+ })),
9234
+ size: querySnapshot.size,
9235
+ empty: querySnapshot.empty,
9236
+ readTime: querySnapshot.readTime ? {
9237
+ seconds: querySnapshot.readTime.seconds,
9238
+ nanoseconds: querySnapshot.readTime.nanoseconds
9239
+ } : void 0
9240
+ };
9241
+ try {
9242
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: queryResult });
9243
+ SpanUtils.endSpan(spanInfo.span, { code: __opentelemetry_api.SpanStatusCode.OK });
9244
+ } catch {
9245
+ logger.error(`[FirestoreInstrumentation] Error updating span attributes for query.get`);
9246
+ }
9247
+ return querySnapshot;
9248
+ }
9249
+ async _handleReplayQueryGet(spanInfo, inputValue) {
9250
+ logger.debug(`[FirestoreInstrumentation] Replaying query.get`);
9251
+ const mockData = await findMockResponseAsync({
9252
+ mockRequestData: {
9253
+ traceId: spanInfo.traceId,
9254
+ spanId: spanInfo.spanId,
9255
+ name: "firestore.query.get",
9256
+ inputValue: createMockInputValue(inputValue),
9257
+ packageName: PACKAGE_NAME,
9258
+ instrumentationName: this.INSTRUMENTATION_NAME,
9259
+ submoduleName: "query",
9260
+ kind: __opentelemetry_api.SpanKind.CLIENT
9261
+ },
9262
+ tuskDrift: this.tuskDrift
9263
+ });
9264
+ if (!mockData) {
9265
+ logger.warn(`[FirestoreInstrumentation] No mock data found for query.get: ${inputValue.path}`);
9266
+ return Promise.reject(/* @__PURE__ */ new Error("No mock data found"));
9267
+ }
9268
+ const queryResult = mockData.result;
9269
+ return new TdFirestoreQueryMock(queryResult);
9270
+ }
7944
9271
  _wrap(target, propertyName, wrapper) {
7945
- wrap(target, propertyName, wrapper);
9272
+ if (!target || typeof target[propertyName] !== "function") {
9273
+ logger.warn(`[FirestoreInstrumentation] Cannot wrap ${propertyName}: not a function or target is undefined`);
9274
+ return;
9275
+ }
9276
+ const original = target[propertyName];
9277
+ target[propertyName] = wrapper(original);
7946
9278
  }
7947
9279
  };
7948
- GrpcInstrumentation.metadataStore = /* @__PURE__ */ new Map();
7949
9280
 
7950
9281
  //#endregion
7951
9282
  //#region src/instrumentation/libraries/nextjs/Instrumentation.ts
@@ -8005,6 +9336,12 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8005
9336
  const self = this;
8006
9337
  return (originalHandleRequest) => {
8007
9338
  return async function(req, res, parsedUrl) {
9339
+ if (self.mode === TuskDriftMode.RECORD) {
9340
+ if (!shouldSample({
9341
+ samplingRate: self.tuskDrift.getSamplingRate(),
9342
+ isAppReady: self.tuskDrift.isAppReady()
9343
+ })) return originalHandleRequest.call(this, req, res, parsedUrl);
9344
+ }
8008
9345
  const method = req.method || "GET";
8009
9346
  const url = req.url || "/";
8010
9347
  logger.debug(`[NextjsInstrumentation] Intercepted Next.js request: ${method} ${url}`);
@@ -8020,6 +9357,7 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8020
9357
  logger.debug(`[NextjsInstrumentation] No trace ID found, calling original handler`);
8021
9358
  return originalHandleRequest.call(this, req, res, parsedUrl);
8022
9359
  }
9360
+ logger.debug(`[NextjsInstrumentation] Setting replay trace id`, replayTraceId);
8023
9361
  const envVars = self.replayHooks.extractEnvVarsFromHeaders(req);
8024
9362
  if (envVars) EnvVarTracker.setEnvVars(replayTraceId, envVars);
8025
9363
  const ctxWithReplayTraceId = SpanUtils.setCurrentReplayTraceId(replayTraceId);
@@ -8050,13 +9388,6 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8050
9388
  } });
8051
9389
  else if (self.mode === TuskDriftMode.RECORD) {
8052
9390
  if (method.toUpperCase() === "OPTIONS" || !!req.headers["access-control-request-method"]) return originalHandleRequest.call(this, req, res, parsedUrl);
8053
- if (!shouldSample({
8054
- samplingRate: self.tuskDrift.getSamplingRate(),
8055
- isAppReady: self.tuskDrift.isAppReady()
8056
- })) {
8057
- logger.debug(`[NextjsInstrumentation] Skipping server span due to sampling rate: ${url}`);
8058
- return originalHandleRequest.call(this, req, res, parsedUrl);
8059
- }
8060
9391
  logger.debug(`[NextjsInstrumentation] Creating server span for ${method} ${url}`);
8061
9392
  return handleRecordMode({
8062
9393
  originalFunctionCall: () => originalHandleRequest.call(this, req, res, parsedUrl),
@@ -8135,6 +9466,19 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8135
9466
  };
8136
9467
  try {
8137
9468
  await originalHandleRequest.call(thisContext, req, res, parsedUrl);
9469
+ } catch (error) {
9470
+ logger.error(`[NextjsInstrumentation] Error in Next.js request: ${error instanceof Error ? error.message : "Unknown error"}`);
9471
+ try {
9472
+ SpanUtils.endSpan(spanInfo.span, {
9473
+ code: __opentelemetry_api.SpanStatusCode.ERROR,
9474
+ message: error instanceof Error ? error.message : "Unknown error"
9475
+ });
9476
+ } catch (e) {
9477
+ logger.error(`[NextjsInstrumentation] Error ending span:`, e);
9478
+ }
9479
+ throw error;
9480
+ }
9481
+ try {
8138
9482
  if (!capturedStatusCode) {
8139
9483
  capturedStatusCode = res.statusCode;
8140
9484
  capturedStatusMessage = res.statusMessage;
@@ -8159,7 +9503,6 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8159
9503
  outputValue.bodySize = responseBuffer.length;
8160
9504
  } catch (error) {
8161
9505
  logger.error(`[NextjsInstrumentation] Error processing response body:`, error);
8162
- outputValue.bodyProcessingError = error instanceof Error ? error.message : String(error);
8163
9506
  }
8164
9507
  SpanUtils.addSpanAttributes(spanInfo.span, {
8165
9508
  inputValue: completeInputValue,
@@ -8179,6 +9522,11 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8179
9522
  message: `HTTP ${capturedStatusCode}`
8180
9523
  } : { code: __opentelemetry_api.SpanStatusCode.OK };
8181
9524
  SpanUtils.setStatus(spanInfo.span, status);
9525
+ const decodedType = getDecodedType(outputValue.headers?.["content-type"] || "");
9526
+ if (decodedType && STATIC_ASSET_TYPES.has(decodedType)) {
9527
+ TraceBlockingManager.getInstance().blockTrace(spanInfo.traceId);
9528
+ logger.debug(`[NextjsInstrumentation] Blocking trace ${spanInfo.traceId} because it is an static asset response. Decoded type: ${decodedType}`);
9529
+ }
8182
9530
  SpanUtils.endSpan(spanInfo.span);
8183
9531
  if (self.mode === TuskDriftMode.REPLAY) try {
8184
9532
  const now = OriginalGlobalUtils.getOriginalDate();
@@ -8236,12 +9584,15 @@ var NextjsInstrumentation = class extends TdInstrumentationBase {
8236
9584
  logger.error("[NextjsInstrumentation] Failed to build/send inbound replay span:", e);
8237
9585
  }
8238
9586
  } catch (error) {
8239
- logger.error(`[NextjsInstrumentation] Error in Next.js request: ${error instanceof Error ? error.message : String(error)}`);
8240
- SpanUtils.endSpan(spanInfo.span, {
8241
- code: __opentelemetry_api.SpanStatusCode.ERROR,
8242
- message: error instanceof Error ? error.message : String(error)
8243
- });
8244
- throw error;
9587
+ logger.error(`[NextjsInstrumentation] Error in Next.js request: ${error instanceof Error ? error.message : "Unknown error"}`);
9588
+ try {
9589
+ SpanUtils.endSpan(spanInfo.span, {
9590
+ code: __opentelemetry_api.SpanStatusCode.ERROR,
9591
+ message: error instanceof Error ? error.message : "Unknown error"
9592
+ });
9593
+ } catch (e) {
9594
+ logger.error(`[NextjsInstrumentation] Error ending span:`, e);
9595
+ }
8245
9596
  }
8246
9597
  }
8247
9598
  /**
@@ -10468,20 +11819,21 @@ var SpanTransformer = class SpanTransformer {
10468
11819
  const inputData = JSON.parse(inputValueString);
10469
11820
  const inputSchemaMergesString = attributes[TdSpanAttributes.INPUT_SCHEMA_MERGES];
10470
11821
  const inputSchemaMerges = inputSchemaMergesString ? JSON.parse(inputSchemaMergesString) : void 0;
10471
- const { schema: inputSchema, decodedValueHash: inputValueHash } = JsonSchemaHelper.generateSchemaAndHash(inputData, inputSchemaMerges);
11822
+ const { schema: inputSchema, decodedValueHash: inputValueHash, decodedSchemaHash: inputSchemaHash } = JsonSchemaHelper.generateSchemaAndHash(inputData, inputSchemaMerges);
10472
11823
  let outputData = {};
10473
11824
  let outputSchema = {
10474
11825
  type: __use_tusk_drift_schemas_core_json_schema.JsonSchemaType.OBJECT,
10475
11826
  properties: {}
10476
11827
  };
10477
11828
  let outputValueHash = "";
11829
+ let outputSchemaHash = "";
10478
11830
  if (attributes[TdSpanAttributes.OUTPUT_VALUE]) {
10479
11831
  const outputValueString = attributes[TdSpanAttributes.OUTPUT_VALUE];
10480
11832
  outputData = JSON.parse(outputValueString);
10481
11833
  const outputSchemaMergesString = attributes[TdSpanAttributes.OUTPUT_SCHEMA_MERGES];
10482
11834
  const outputSchemaMerges = outputSchemaMergesString ? JSON.parse(outputSchemaMergesString) : void 0;
10483
- ({schema: outputSchema, decodedValueHash: outputValueHash} = JsonSchemaHelper.generateSchemaAndHash(outputData, outputSchemaMerges));
10484
- } else ({schema: outputSchema, decodedSchemaHash: outputValueHash} = JsonSchemaHelper.generateSchemaAndHash(outputData));
11835
+ ({schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = JsonSchemaHelper.generateSchemaAndHash(outputData, outputSchemaMerges));
11836
+ } else ({schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = JsonSchemaHelper.generateSchemaAndHash(outputData));
10485
11837
  let metadata = void 0;
10486
11838
  if (attributes[TdSpanAttributes.METADATA]) metadata = JSON.parse(attributes[TdSpanAttributes.METADATA]);
10487
11839
  let transformMetadata;
@@ -10505,8 +11857,8 @@ var SpanTransformer = class SpanTransformer {
10505
11857
  outputValue: outputData,
10506
11858
  inputSchema,
10507
11859
  outputSchema,
10508
- inputSchemaHash: JsonSchemaHelper.generateDeterministicHash(inputSchema),
10509
- outputSchemaHash: JsonSchemaHelper.generateDeterministicHash(outputSchema),
11860
+ inputSchemaHash,
11861
+ outputSchemaHash,
10510
11862
  inputValueHash,
10511
11863
  outputValueHash,
10512
11864
  kind: span.kind,
@@ -10749,18 +12101,35 @@ var TdSpanExporter = class {
10749
12101
  */
10750
12102
  export(spans, resultCallback) {
10751
12103
  logger.debug(`TdSpanExporter.export() called with ${spans.length} span(s)`);
10752
- const filteredSpans = spans.filter((span) => {
10753
- if (span.instrumentationLibrary?.name === "next.js") return false;
12104
+ const traceBlockingManager = TraceBlockingManager.getInstance();
12105
+ const filteredSpansBasedOnLibraryName = spans.filter((span) => {
12106
+ if (span.instrumentationLibrary.name === TD_INSTRUMENTATION_LIBRARY_NAME) return true;
12107
+ return false;
12108
+ });
12109
+ logger.debug(`After filtering based on library name: ${filteredSpansBasedOnLibraryName.length} span(s) remaining`);
12110
+ const MAX_SPAN_SIZE_MB = 1;
12111
+ const MAX_SPAN_SIZE_BYTES = MAX_SPAN_SIZE_MB * 1024 * 1024;
12112
+ const filteredSpansBasedOnSize = filteredSpansBasedOnLibraryName.filter((span) => {
12113
+ const traceId = span.spanContext().traceId;
12114
+ if (traceBlockingManager.isTraceBlocked(traceId)) {
12115
+ logger.debug(`Skipping span '${span.name}' (${span.spanContext().spanId}) - trace ${traceId} is blocked`);
12116
+ return false;
12117
+ }
12118
+ const inputValueString = span.attributes[TdSpanAttributes.INPUT_VALUE] || "";
12119
+ const outputValueString = span.attributes[TdSpanAttributes.OUTPUT_VALUE] || "";
12120
+ const inputSize = Buffer.byteLength(inputValueString, "utf8");
12121
+ const outputSize = Buffer.byteLength(outputValueString, "utf8");
12122
+ const estimatedTotalSize = inputSize + outputSize + 5e4;
12123
+ const estimatedSizeMB = estimatedTotalSize / (1024 * 1024);
12124
+ if (estimatedTotalSize > MAX_SPAN_SIZE_BYTES) {
12125
+ traceBlockingManager.blockTrace(traceId);
12126
+ logger.warn(`Blocking trace ${traceId} - span '${span.name}' (${span.spanContext().spanId}) has estimated size ${estimatedSizeMB.toFixed(2)} MB exceeding limit of ${MAX_SPAN_SIZE_MB} MB. Future spans for this trace will be prevented.`);
12127
+ return false;
12128
+ }
10754
12129
  return true;
10755
12130
  });
10756
- logger.debug(`After filtering: ${filteredSpans.length} span(s) remaining`);
10757
- let cleanSpans;
10758
- try {
10759
- cleanSpans = filteredSpans.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
10760
- } catch (error) {
10761
- logger.error("Error transforming spans to CleanSpanData", error);
10762
- throw error;
10763
- }
12131
+ logger.debug(`Filtered ${filteredSpansBasedOnLibraryName.length - filteredSpansBasedOnSize.length} oversized span(s), ${filteredSpansBasedOnSize.length} remaining`);
12132
+ const cleanSpans = filteredSpansBasedOnSize.map((span) => SpanTransformer.transformSpanToCleanJSON(span));
10764
12133
  if (this.adapters.length === 0) {
10765
12134
  logger.debug("No adapters configured");
10766
12135
  resultCallback({ code: import_src.ExportResultCode.SUCCESS });
@@ -10895,15 +12264,12 @@ var ProtobufCommunicator = class {
10895
12264
  });
10896
12265
  }
10897
12266
  /**
10898
- * This function uses a Node.js script to communicate with the CLI over a socket.
10899
- * The script is called using execSync, which will block the main thread until the child process exits. This makes requesting mocks from the CLI synchronous.
10900
- *
10901
- * Since this function blocks the main thread, there is a perfomance impact for using this. We should use requestMockAsync whenever possilbe and only use this function
10902
- * for instrumentations that request fetching mocks synchronously.
12267
+ * This function uses a separate Node.js child process to communicate with the CLI over a socket.
12268
+ * The child process creates its own connection and event loop, allowing proper async socket handling.
12269
+ * The parent process blocks synchronously waiting for the child to complete.
10903
12270
  *
10904
- * (10/9/2025) Currently not using this function since we are not actually fetching mocks for the only sync instrumentation (Date)
10905
- * NOTE: This function probably doesn't work. plus, nc might not be installed on all machines (especially windows)
10906
- * Better approach is replacing nc command with pure Node.js implementation
12271
+ * Since this function blocks the main thread, there is a performance impact. We should use requestMockAsync whenever possible and only use this function
12272
+ * for instrumentations that require fetching mocks synchronously.
10907
12273
  */
10908
12274
  requestMockSync(mockRequest) {
10909
12275
  const requestId = this.generateRequestId();
@@ -10926,10 +12292,15 @@ var ProtobufCommunicator = class {
10926
12292
  getMockRequest: protoMockRequest
10927
12293
  }
10928
12294
  });
12295
+ logger.debug("Sending protobuf request to CLI (sync)", {
12296
+ outboundSpan: mockRequest.outboundSpan,
12297
+ testId: mockRequest.testId
12298
+ });
10929
12299
  const messageBytes = __use_tusk_drift_schemas_core_communication.SDKMessage.toBinary(sdkMessage);
10930
12300
  const tempDir = os.default.tmpdir();
10931
- const requestFile = path.default.join(tempDir, `tusk-request-${requestId}.bin`);
10932
- const responseFile = path.default.join(tempDir, `tusk-response-${requestId}.bin`);
12301
+ const requestFile = path.default.join(tempDir, `tusk-sync-request-${requestId}.bin`);
12302
+ const responseFile = path.default.join(tempDir, `tusk-sync-response-${requestId}.bin`);
12303
+ const scriptFile = path.default.join(tempDir, `tusk-sync-script-${requestId}.js`);
10933
12304
  try {
10934
12305
  const lengthBuffer = Buffer.allocUnsafe(4);
10935
12306
  lengthBuffer.writeUInt32BE(messageBytes.length, 0);
@@ -10938,19 +12309,112 @@ var ProtobufCommunicator = class {
10938
12309
  const mockSocket = OriginalGlobalUtils.getOriginalProcessEnvVar("TUSK_MOCK_SOCKET");
10939
12310
  const mockHost = OriginalGlobalUtils.getOriginalProcessEnvVar("TUSK_MOCK_HOST");
10940
12311
  const mockPort = OriginalGlobalUtils.getOriginalProcessEnvVar("TUSK_MOCK_PORT");
10941
- let command;
10942
- if (mockSocket) {
10943
- if (!fs.default.existsSync(mockSocket)) throw new Error(`Socket file does not exist: ${mockSocket}`);
10944
- command = `nc -U -w 10 "${mockSocket}" < "${requestFile}" > "${responseFile}"`;
10945
- } else if (mockHost && mockPort) command = `nc -w 10 "${mockHost}" ${mockPort} < "${requestFile}" > "${responseFile}"`;
10946
- else {
10947
- const socketPath = path.default.join(os.default.tmpdir(), "tusk-connect.sock");
10948
- if (!fs.default.existsSync(socketPath)) throw new Error(`Socket file does not exist: ${socketPath}`);
10949
- command = `nc -U -w 10 "${socketPath}" < "${requestFile}" > "${responseFile}"`;
10950
- }
12312
+ let connectionConfig;
12313
+ if (mockSocket) connectionConfig = {
12314
+ type: "unix",
12315
+ path: mockSocket
12316
+ };
12317
+ else if (mockHost && mockPort) connectionConfig = {
12318
+ type: "tcp",
12319
+ host: mockHost,
12320
+ port: parseInt(mockPort, 10)
12321
+ };
12322
+ else connectionConfig = {
12323
+ type: "unix",
12324
+ path: path.default.join(os.default.tmpdir(), "tusk-connect.sock")
12325
+ };
12326
+ fs.default.writeFileSync(scriptFile, `
12327
+ const net = require('net');
12328
+ const fs = require('fs');
12329
+
12330
+ const requestFile = process.argv[2];
12331
+ const responseFile = process.argv[3];
12332
+ const config = JSON.parse(process.argv[4]);
12333
+
12334
+ let responseReceived = false;
12335
+ let timeoutId;
12336
+
12337
+ function cleanup(exitCode) {
12338
+ if (timeoutId) clearTimeout(timeoutId);
12339
+ process.exit(exitCode);
12340
+ }
12341
+
12342
+ try {
12343
+ // Read the request data
12344
+ const requestData = fs.readFileSync(requestFile);
12345
+
12346
+ // Create connection based on config
12347
+ const client = config.type === 'unix'
12348
+ ? net.createConnection({ path: config.path })
12349
+ : net.createConnection({ host: config.host, port: config.port });
12350
+
12351
+ const incomingChunks = [];
12352
+ let incomingBuffer = Buffer.alloc(0);
12353
+
12354
+ // Set timeout
12355
+ timeoutId = setTimeout(() => {
12356
+ if (!responseReceived) {
12357
+ console.error('Timeout waiting for response');
12358
+ client.destroy();
12359
+ cleanup(1);
12360
+ }
12361
+ }, 10000);
12362
+
12363
+ client.on('connect', () => {
12364
+ // Send the request
12365
+ client.write(requestData);
12366
+ });
12367
+
12368
+ client.on('data', (data) => {
12369
+ incomingBuffer = Buffer.concat([incomingBuffer, data]);
12370
+
12371
+ // Try to parse complete message (4 byte length prefix + message)
12372
+ while (incomingBuffer.length >= 4) {
12373
+ const messageLength = incomingBuffer.readUInt32BE(0);
12374
+
12375
+ if (incomingBuffer.length < 4 + messageLength) {
12376
+ // Incomplete message, wait for more data
12377
+ break;
12378
+ }
12379
+
12380
+ // We have a complete message
12381
+ const messageData = incomingBuffer.slice(4, 4 + messageLength);
12382
+ incomingBuffer = incomingBuffer.slice(4 + messageLength);
12383
+
12384
+ // Write the complete response (including length prefix)
12385
+ const lengthPrefix = Buffer.allocUnsafe(4);
12386
+ lengthPrefix.writeUInt32BE(messageLength, 0);
12387
+ fs.writeFileSync(responseFile, Buffer.concat([lengthPrefix, messageData]));
12388
+
12389
+ responseReceived = true;
12390
+ client.destroy();
12391
+ cleanup(0);
12392
+ break;
12393
+ }
12394
+ });
12395
+
12396
+ client.on('error', (err) => {
12397
+ if (!responseReceived) {
12398
+ console.error('Connection error:', err.message);
12399
+ cleanup(1);
12400
+ }
12401
+ });
12402
+
12403
+ client.on('close', () => {
12404
+ if (!responseReceived) {
12405
+ console.error('Connection closed without response');
12406
+ cleanup(1);
12407
+ }
12408
+ });
12409
+
12410
+ } catch (err) {
12411
+ console.error('Script error:', err.message);
12412
+ cleanup(1);
12413
+ }
12414
+ `);
10951
12415
  try {
10952
- (0, child_process.execSync)(command, {
10953
- timeout: 1e4,
12416
+ (0, child_process.execSync)(`node "${scriptFile}" "${requestFile}" "${responseFile}" '${JSON.stringify(connectionConfig)}'`, {
12417
+ timeout: 12e3,
10954
12418
  stdio: "pipe"
10955
12419
  });
10956
12420
  const responseBuffer = fs.default.readFileSync(responseFile);
@@ -10975,22 +12439,27 @@ var ProtobufCommunicator = class {
10975
12439
  error: mockResponse.error || "Mock not found"
10976
12440
  };
10977
12441
  } catch (error) {
10978
- logger.error("[ProtobufCommunicator] error sending request to CLI:", error);
12442
+ logger.error("[ProtobufCommunicator] error in sync request child process:", error);
10979
12443
  throw error;
10980
12444
  }
10981
12445
  } catch (error) {
10982
12446
  throw new Error(`Sync request failed: ${error.message}`);
10983
12447
  } finally {
10984
12448
  try {
10985
- fs.default.unlinkSync(requestFile);
12449
+ if (fs.default.existsSync(requestFile)) fs.default.unlinkSync(requestFile);
10986
12450
  } catch (e) {
10987
12451
  logger.error("[ProtobufCommunicator] error cleaning up request file:", e);
10988
12452
  }
10989
12453
  try {
10990
- fs.default.unlinkSync(responseFile);
12454
+ if (fs.default.existsSync(responseFile)) fs.default.unlinkSync(responseFile);
10991
12455
  } catch (e) {
10992
12456
  logger.error("[ProtobufCommunicator] error cleaning up response file:", e);
10993
12457
  }
12458
+ try {
12459
+ if (fs.default.existsSync(scriptFile)) fs.default.unlinkSync(scriptFile);
12460
+ } catch (e) {
12461
+ logger.error("[ProtobufCommunicator] error cleaning up script file:", e);
12462
+ }
10994
12463
  }
10995
12464
  }
10996
12465
  async sendProtobufMessage(message) {
@@ -11158,7 +12627,8 @@ const TuskDriftInstrumentationModuleNames = [
11158
12627
  "jwks-rsa",
11159
12628
  "mysql2",
11160
12629
  "ioredis",
11161
- "@grpc/grpc-js"
12630
+ "@grpc/grpc-js",
12631
+ "@google-cloud/firestore"
11162
12632
  ];
11163
12633
 
11164
12634
  //#endregion
@@ -11280,6 +12750,10 @@ var TuskDriftCore = class TuskDriftCore {
11280
12750
  enabled: true,
11281
12751
  mode: this.mode
11282
12752
  });
12753
+ new FirestoreInstrumentation({
12754
+ enabled: true,
12755
+ mode: this.mode
12756
+ });
11283
12757
  new NextjsInstrumentation({
11284
12758
  enabled: true,
11285
12759
  mode: this.mode
@@ -11452,6 +12926,10 @@ var TuskDriftCore = class TuskDriftCore {
11452
12926
  }
11453
12927
  }
11454
12928
  requestMockSync(mockRequest) {
12929
+ if (!this.isConnectedWithCLI) {
12930
+ logger.error("Requesting sync mock but CLI is not ready yet");
12931
+ throw new Error("Requesting sync mock but CLI is not ready yet");
12932
+ }
11455
12933
  const mockRequestCore = this.createMockRequestCore(mockRequest);
11456
12934
  if (mockRequestCore) return mockRequestCore;
11457
12935
  return this.requestMockFromCLISync(mockRequest);
@@ -11499,7 +12977,7 @@ var TuskDriftCore = class TuskDriftCore {
11499
12977
  return this.initParams;
11500
12978
  }
11501
12979
  getTracer() {
11502
- return __opentelemetry_api.trace.getTracer("tusk-drift-sdk", "1.0.0");
12980
+ return __opentelemetry_api.trace.getTracer(TD_INSTRUMENTATION_LIBRARY_NAME);
11503
12981
  }
11504
12982
  };
11505
12983
  var TuskDriftSDK = class {