@reproapp/node-sdk 0.0.45 → 0.0.47

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/src/index.ts CHANGED
@@ -341,6 +341,7 @@ export type TraceEventForFilter = {
341
341
  fn?: string;
342
342
  wrapperClass?: string | null;
343
343
  file?: string | null;
344
+ sourceFile?: string | null;
344
345
  line?: number | null;
345
346
  depth?: number;
346
347
  library?: string | null;
@@ -378,7 +379,11 @@ type PendingTraceEventRecord = TraceEventRecord & {
378
379
  };
379
380
 
380
381
  type TraceValueCaptureRef = {
381
- status: 'stored' | 'truncated-for-storage' | 'skipped-non-meaningful';
382
+ status:
383
+ | 'stored'
384
+ | 'truncated-for-storage'
385
+ | 'skipped-non-meaningful'
386
+ | 'failed-policy';
382
387
  valueId?: string;
383
388
  projectedKind?: string;
384
389
  };
@@ -606,6 +611,7 @@ function refreshDisabledFunctionTraceRules() {
606
611
  let disabledFunctionTraceRules: DisableFunctionTraceConfig[] = computeDisabledFunctionTraceRules();
607
612
  let disabledFunctionTypePatterns: Array<string | number | RegExp> = [];
608
613
  let disabledTraceFilePatterns: Array<string | number | RegExp> = [];
614
+ let excludedInstrumentationFilePatterns: Array<string | number | RegExp> = [];
609
615
  let __TRACE_LOG_PREF: boolean | null = null;
610
616
  let __FULL_VALUE_CAPTURE_ENABLED = true;
611
617
 
@@ -684,7 +690,7 @@ function matchesRule(rule: DisableFunctionTraceRule, event: TraceEventForFilter)
684
690
  const wrapperPattern = rule.wrapper ?? rule.wrapperClass ?? rule.className ?? rule.owner;
685
691
  if (!matchesPattern(event.wrapperClass, wrapperPattern)) return false;
686
692
 
687
- if (!matchesPattern(event.file, rule.file)) return false;
693
+ if (!matchesTraceFile(event, rule.file)) return false;
688
694
  if (!matchesPattern(event.line == null ? null : String(event.line), rule.line)) return false;
689
695
 
690
696
  const libPattern = rule.lib ?? rule.library;
@@ -699,9 +705,29 @@ function matchesRule(rule: DisableFunctionTraceRule, event: TraceEventForFilter)
699
705
  return true;
700
706
  }
701
707
 
708
+ function matchesTraceFile(
709
+ event: Pick<TraceEventForFilter, 'file' | 'sourceFile'>,
710
+ pattern?: TraceRulePattern,
711
+ defaultWhenEmpty: boolean = true,
712
+ ): boolean {
713
+ if (pattern === undefined || pattern === null) return defaultWhenEmpty;
714
+ const candidates = [event.file, event.sourceFile].filter(
715
+ (value): value is string => typeof value === 'string' && value.length > 0,
716
+ );
717
+ if (!candidates.length) {
718
+ return matchesPattern(null, pattern, defaultWhenEmpty);
719
+ }
720
+ return candidates.some((value) => matchesPattern(value, pattern, false));
721
+ }
722
+
702
723
  function shouldDropTraceEvent(event: TraceEventForFilter): boolean {
703
724
  if (disabledTraceFilePatterns.length) {
704
- if (matchesPattern(event.file, disabledTraceFilePatterns, false)) {
725
+ if (matchesTraceFile(event, disabledTraceFilePatterns, false)) {
726
+ return true;
727
+ }
728
+ }
729
+ if (excludedInstrumentationFilePatterns.length) {
730
+ if (matchesTraceFile(event, excludedInstrumentationFilePatterns, false)) {
705
731
  return true;
706
732
  }
707
733
  }
@@ -948,6 +974,9 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
948
974
  if (hasOwn(options, 'disableTraceFiles')) {
949
975
  setDisabledTraceFiles(options.disableTraceFiles ?? null);
950
976
  }
977
+ if (hasOwn(options, 'exclude')) {
978
+ excludedInstrumentationFilePatterns = normalizePatternArray(options.exclude);
979
+ }
951
980
  if (hasOwn(options, 'logFunctionCalls') && typeof options.logFunctionCalls === 'boolean') {
952
981
  setReproTraceLogsEnabled(options.logFunctionCalls);
953
982
  }
@@ -971,7 +1000,17 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
971
1000
  traceInterceptors: _traceInterceptors,
972
1001
  ...rest
973
1002
  } = options;
974
- const initOpts = { ...defaultTracerInitOpts(), ...(rest as TracerInitOpts) };
1003
+ const defaults = defaultTracerInitOpts();
1004
+ const overrides = rest as TracerInitOpts;
1005
+ const initOpts = {
1006
+ ...defaults,
1007
+ ...overrides,
1008
+ include: overrides.include ?? defaults.include,
1009
+ exclude: [
1010
+ ...(defaults.exclude ?? []),
1011
+ ...(overrides.exclude ?? []),
1012
+ ],
1013
+ };
975
1014
  tracerPkg.init?.(initOpts);
976
1015
  tracerPkg.patchHttp?.();
977
1016
  applyTraceLogPreference(tracerPkg);
@@ -1094,6 +1133,7 @@ function traceEventCandidateFromRecord(event: PendingTraceEventRecord): TraceEve
1094
1133
  fn: event.fn,
1095
1134
  wrapperClass: inferWrapperClassFromFn(event.fn),
1096
1135
  file: event.file ?? null,
1136
+ sourceFile: sourceFileForLibrary,
1097
1137
  line: event.line ?? null,
1098
1138
  depth: event.depth,
1099
1139
  library: inferLibraryNameFromFile(sourceFileForLibrary ?? event.file),
@@ -1176,20 +1216,68 @@ type RequestCaptureScopeState = {
1176
1216
  traceBatchCount: number;
1177
1217
  dbChangeCount: number;
1178
1218
  emailEventCount: number;
1219
+ captureFailures: Array<{
1220
+ stage: string;
1221
+ target: string;
1222
+ reason: string;
1223
+ }>;
1224
+ privacyMode: 'policy' | 'fallback' | 'raw';
1225
+ privacyPolicyFingerprint?: string;
1226
+ privacyStatementCount: number;
1179
1227
  };
1180
1228
 
1181
1229
  function createRequestCaptureScope(
1182
1230
  requestRid: string,
1183
1231
  transport: RequestCaptureScopeState['transport'],
1232
+ cfg?: ReproMiddlewareConfig,
1184
1233
  ): RequestCaptureScopeState {
1234
+ const privacy = cfg ? getRuntimePrivacyState(cfg).policy ?? null : null;
1235
+ const privacyMode =
1236
+ cfg && shouldCapturePrivacyRaw(cfg)
1237
+ ? 'raw'
1238
+ : privacy
1239
+ ? 'policy'
1240
+ : 'fallback';
1185
1241
  return {
1186
1242
  requestRid,
1187
1243
  transport,
1188
- backendRequestCount: 0,
1244
+ // Every scope represents one captured transport request. This is an
1245
+ // observed expectation, not a count of successful enqueue operations.
1246
+ backendRequestCount: 1,
1189
1247
  traceBatchCount: 0,
1190
1248
  dbChangeCount: 0,
1191
1249
  emailEventCount: 0,
1250
+ captureFailures: [],
1251
+ privacyMode,
1252
+ privacyPolicyFingerprint: privacy?.fingerprint,
1253
+ privacyStatementCount: privacy?.statements.length ?? 0,
1254
+ };
1255
+ }
1256
+
1257
+ function recordRequestCaptureFailure(
1258
+ scope: RequestCaptureScopeState | null | undefined,
1259
+ stage: string,
1260
+ target: string,
1261
+ reason: string,
1262
+ ): void {
1263
+ if (!scope) return;
1264
+ const normalized = {
1265
+ stage: String(stage || 'capture').slice(0, 80),
1266
+ target: String(target || 'unknown').slice(0, 120),
1267
+ reason: String(reason || 'capture_failed').slice(0, 120),
1192
1268
  };
1269
+ if (
1270
+ scope.captureFailures.some((failure) =>
1271
+ failure.stage === normalized.stage &&
1272
+ failure.target === normalized.target &&
1273
+ failure.reason === normalized.reason,
1274
+ )
1275
+ ) {
1276
+ return;
1277
+ }
1278
+ if (scope.captureFailures.length < 25) {
1279
+ scope.captureFailures.push(normalized);
1280
+ }
1193
1281
  }
1194
1282
 
1195
1283
  function buildRequestCaptureManifestPayload(
@@ -1219,6 +1307,20 @@ function buildRequestCaptureManifestPayload(
1219
1307
  to: expectedTraceBatches - 1,
1220
1308
  }
1221
1309
  : undefined,
1310
+ privacy: {
1311
+ mode: scope.privacyMode,
1312
+ status:
1313
+ scope.captureFailures.length > 0
1314
+ ? 'failed'
1315
+ : scope.privacyMode === 'raw'
1316
+ ? 'bypassed'
1317
+ : 'applied',
1318
+ policyFingerprint: scope.privacyPolicyFingerprint,
1319
+ statementCount: scope.privacyStatementCount,
1320
+ },
1321
+ captureFailures: scope.captureFailures.length
1322
+ ? scope.captureFailures.map((failure) => ({ ...failure }))
1323
+ : undefined,
1222
1324
  closedAt,
1223
1325
  };
1224
1326
  }
@@ -1403,12 +1505,6 @@ const SDK_BACKGROUND_MAX_DEFER_MS = (() => {
1403
1505
  return Number.POSITIVE_INFINITY;
1404
1506
  })();
1405
1507
 
1406
- const SDK_BACKGROUND_WORK_TIMEOUT_MS = (() => {
1407
- const env = Number(process.env.REPRO_SDK_BACKGROUND_WORK_TIMEOUT_MS);
1408
- if (Number.isFinite(env) && env >= 0) return Math.trunc(env);
1409
- return 20000;
1410
- })();
1411
-
1412
1508
  const TRACE_MATERIALIZE_OFFTHREAD_ENABLED = (() => {
1413
1509
  const env = String(process.env.REPRO_TRACE_MATERIALIZE_OFFTHREAD || '').trim().toLowerCase();
1414
1510
  if (!env) return true;
@@ -1738,24 +1834,9 @@ function scheduleSdkBackgroundDrain(delayMs = 0): void {
1738
1834
  }
1739
1835
 
1740
1836
  async function runSdkBackgroundWork(work: () => Promise<void> | void): Promise<void> {
1741
- if (!Number.isFinite(SDK_BACKGROUND_WORK_TIMEOUT_MS) || SDK_BACKGROUND_WORK_TIMEOUT_MS <= 0) {
1742
- await work();
1743
- return;
1744
- }
1745
-
1746
- let timeout: NodeJS.Timeout | null = null;
1747
- const workPromise = Promise.resolve()
1837
+ await Promise.resolve()
1748
1838
  .then(work)
1749
1839
  .catch(() => undefined);
1750
- const timeoutPromise = new Promise<void>((resolve) => {
1751
- timeout = setTimeout(resolve, SDK_BACKGROUND_WORK_TIMEOUT_MS);
1752
- try { (timeout as any).unref?.(); } catch {}
1753
- });
1754
-
1755
- await Promise.race([workPromise, timeoutPromise]);
1756
- if (timeout) {
1757
- try { clearTimeout(timeout); } catch {}
1758
- }
1759
1840
  }
1760
1841
 
1761
1842
  async function drainQueuedIngestBeforeBackgroundWork(): Promise<void> {
@@ -2747,7 +2828,7 @@ const TRACE_MAX_EVENTS_PER_REQUEST = (() => {
2747
2828
  const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
2748
2829
  const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
2749
2830
  if (Number.isFinite(env) && env > 0) return Math.trunc(env);
2750
- return 8;
2831
+ return Math.ceil(TRACE_MAX_EVENTS_PER_REQUEST / TRACE_BATCH_SIZE);
2751
2832
  })();
2752
2833
  const TRACE_FLUSH_DELAY_MS = 20;
2753
2834
  // Choose how to order trace events in payloads.
@@ -3321,6 +3402,7 @@ async function materializePendingTraceEvents(
3321
3402
  maskReq: MaskRequestContext;
3322
3403
  masking: NormalizedMaskingConfig | null;
3323
3404
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3405
+ captureScope?: RequestCaptureScopeState | null;
3324
3406
  },
3325
3407
  ): Promise<TraceEventRecord[]> {
3326
3408
  for (const evt of events) {
@@ -3367,7 +3449,7 @@ async function materializePendingTraceEvents(
3367
3449
  previewValue: evt.args,
3368
3450
  capture: captureContext,
3369
3451
  metadata: traceValueMetadata,
3370
- });
3452
+ }, params.captureScope);
3371
3453
  if (argsValueCapture?.entry) {
3372
3454
  attachTraceValueEntry(evt, argsValueCapture.entry);
3373
3455
  }
@@ -3396,7 +3478,7 @@ async function materializePendingTraceEvents(
3396
3478
  previewValue: evt.returnValue,
3397
3479
  capture: captureContext,
3398
3480
  metadata: traceValueMetadata,
3399
- });
3481
+ }, params.captureScope);
3400
3482
  if (returnValueCapture?.entry) {
3401
3483
  attachTraceValueEntry(evt, returnValueCapture.entry);
3402
3484
  }
@@ -3425,7 +3507,7 @@ async function materializePendingTraceEvents(
3425
3507
  previewValue: evt.error,
3426
3508
  capture: captureContext,
3427
3509
  metadata: traceValueMetadata,
3428
- });
3510
+ }, params.captureScope);
3429
3511
  if (errorValueCapture?.entry) {
3430
3512
  attachTraceValueEntry(evt, errorValueCapture.entry);
3431
3513
  }
@@ -3443,7 +3525,20 @@ function stripPendingTraceValues(
3443
3525
  reason: string,
3444
3526
  ): TraceEventRecord[] {
3445
3527
  for (const evt of events) {
3446
- if (!evt.__reproPending) continue;
3528
+ const pending = evt.__reproPending;
3529
+ if (!pending) continue;
3530
+ if (pending.argsRaw !== undefined) {
3531
+ evt.args = makeOmittedValue(reason);
3532
+ evt.argsValueCapture = { status: 'failed-policy' };
3533
+ }
3534
+ if (pending.returnValueRaw !== undefined) {
3535
+ evt.returnValue = makeOmittedValue(reason);
3536
+ evt.returnValueCapture = { status: 'failed-policy' };
3537
+ }
3538
+ if (pending.errorRaw !== undefined) {
3539
+ evt.error = makeOmittedValue(reason);
3540
+ evt.errorValueCapture = { status: 'failed-policy' };
3541
+ }
3447
3542
  delete evt.__reproPending;
3448
3543
  }
3449
3544
  return events;
@@ -3460,6 +3555,7 @@ async function materializePendingTraceEventsAsync(
3460
3555
  maskReq: MaskRequestContext;
3461
3556
  masking: NormalizedMaskingConfig | null;
3462
3557
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3558
+ captureScope?: RequestCaptureScopeState | null;
3463
3559
  },
3464
3560
  ): Promise<TraceEventRecord[]> {
3465
3561
  if (!events.some((evt) => evt.__reproPending)) {
@@ -3474,6 +3570,12 @@ async function materializePendingTraceEventsAsync(
3474
3570
  resolvePrivacy: () => privacy,
3475
3571
  });
3476
3572
  } catch {
3573
+ recordRequestCaptureFailure(
3574
+ params.captureScope ?? getCtx().captureScope,
3575
+ 'privacy.trace',
3576
+ 'trace.values',
3577
+ 'main_thread_materialization_failed',
3578
+ );
3477
3579
  return stripPendingTraceValues(events, 'main_thread_failed');
3478
3580
  }
3479
3581
  }
@@ -3492,16 +3594,24 @@ async function materializePendingTraceEventsAsync(
3492
3594
  fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
3493
3595
  });
3494
3596
  } catch (err) {
3495
- if (isTraceMaterializerTimeoutError(err)) {
3496
- return stripPendingTraceValues(events, 'worker_timeout');
3497
- }
3498
3597
  try {
3499
3598
  return await materializePendingTraceEvents(events, {
3500
3599
  ...params,
3501
3600
  resolvePrivacy: () => privacy,
3502
3601
  });
3503
3602
  } catch {
3504
- return stripPendingTraceValues(events, 'worker_failed');
3603
+ recordRequestCaptureFailure(
3604
+ params.captureScope ?? getCtx().captureScope,
3605
+ 'privacy.trace',
3606
+ 'trace.values',
3607
+ isTraceMaterializerTimeoutError(err)
3608
+ ? 'worker_timeout_fallback_failed'
3609
+ : 'worker_and_fallback_failed',
3610
+ );
3611
+ return stripPendingTraceValues(
3612
+ events,
3613
+ isTraceMaterializerTimeoutError(err) ? 'worker_timeout_fallback_failed' : 'worker_failed',
3614
+ );
3505
3615
  }
3506
3616
  }
3507
3617
  }
@@ -4101,6 +4211,7 @@ function materializeInlinePrivacyValue(
4101
4211
  masking: NormalizedMaskingConfig | null,
4102
4212
  privacy: NormalizedRuntimePrivacyPolicy | null,
4103
4213
  db?: RuntimePrivacyDbContext | null,
4214
+ captureScope?: RequestCaptureScopeState | null,
4104
4215
  ): InlinePrivacyMaterializationResult {
4105
4216
  if (value === undefined) {
4106
4217
  return { value };
@@ -4113,6 +4224,12 @@ function materializeInlinePrivacyValue(
4113
4224
  const valueAfterPrivacy = applyPrivacyThenMask(target, value, cfg, req, trace, masking, privacy, db);
4114
4225
  return { value: valueAfterPrivacy };
4115
4226
  } catch {
4227
+ recordRequestCaptureFailure(
4228
+ captureScope ?? getCtx().captureScope,
4229
+ 'privacy.inline',
4230
+ target,
4231
+ 'privacy_transform_failed',
4232
+ );
4116
4233
  return {
4117
4234
  value: undefined,
4118
4235
  skipped: {
@@ -4133,6 +4250,7 @@ async function materializeInlinePrivacyValueAsync(
4133
4250
  masking: NormalizedMaskingConfig | null,
4134
4251
  privacy: NormalizedRuntimePrivacyPolicy | null,
4135
4252
  db?: RuntimePrivacyDbContext | null,
4253
+ captureScope?: RequestCaptureScopeState | null,
4136
4254
  ): Promise<InlinePrivacyMaterializationResult> {
4137
4255
  if (value === undefined) {
4138
4256
  return { value };
@@ -4145,6 +4263,12 @@ async function materializeInlinePrivacyValueAsync(
4145
4263
  const valueAfterPrivacy = await applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking, privacy, db);
4146
4264
  return { value: valueAfterPrivacy };
4147
4265
  } catch {
4266
+ recordRequestCaptureFailure(
4267
+ captureScope ?? getCtx().captureScope,
4268
+ 'privacy.inline',
4269
+ target,
4270
+ 'privacy_transform_failed',
4271
+ );
4148
4272
  return {
4149
4273
  value: undefined,
4150
4274
  skipped: {
@@ -5149,6 +5273,13 @@ function chunkTraceEventsForTransport(
5149
5273
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5150
5274
  break;
5151
5275
  }
5276
+ if (current.length >= TRACE_BATCH_SIZE) {
5277
+ sizedBatches.push(current);
5278
+ current = [];
5279
+ if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5280
+ break;
5281
+ }
5282
+ }
5152
5283
  const candidate = current.concat(event);
5153
5284
  const batchIndex = sizedBatches.length;
5154
5285
  const estimatedBytes = estimateTraceBatchSerializedBytes(
@@ -5380,8 +5511,20 @@ async function maybeCaptureRequestValueAsync(
5380
5511
  capture: FullValueCaptureContext;
5381
5512
  },
5382
5513
  sink: TraceValueBatchEntry[],
5514
+ captureScope?: RequestCaptureScopeState | null,
5383
5515
  ): Promise<TraceValueCaptureRef | undefined> {
5384
- const result = await createCapturedValueEntryAsync(params);
5516
+ let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
5517
+ try {
5518
+ result = await createCapturedValueEntryAsync(params);
5519
+ } catch {
5520
+ recordRequestCaptureFailure(
5521
+ captureScope ?? getCtx().captureScope,
5522
+ 'privacy.full_value',
5523
+ params.target,
5524
+ 'privacy_transform_failed',
5525
+ );
5526
+ return { status: 'failed-policy' };
5527
+ }
5385
5528
  if (!result) return undefined;
5386
5529
  if (result.entry) {
5387
5530
  sink.push(result.entry);
@@ -5409,8 +5552,19 @@ async function maybeCaptureTraceValueAsync(
5409
5552
  capture: FullValueCaptureContext;
5410
5553
  metadata?: Partial<TraceValueBatchEntry>;
5411
5554
  },
5555
+ captureScope?: RequestCaptureScopeState | null,
5412
5556
  ): Promise<{ captureRef: TraceValueCaptureRef; entry?: TraceValueBatchEntry } | undefined> {
5413
- return await createCapturedValueEntryAsync(params);
5557
+ try {
5558
+ return await createCapturedValueEntryAsync(params);
5559
+ } catch {
5560
+ recordRequestCaptureFailure(
5561
+ captureScope ?? getCtx().captureScope,
5562
+ 'privacy.full_value',
5563
+ params.target,
5564
+ 'privacy_transform_failed',
5565
+ );
5566
+ return { captureRef: { status: 'failed-policy' } };
5567
+ }
5414
5568
  }
5415
5569
 
5416
5570
  function maybeCaptureDbValue(
@@ -5438,8 +5592,20 @@ async function maybeCaptureDbValueAsync(
5438
5592
  capture: FullValueCaptureContext;
5439
5593
  },
5440
5594
  sink: TraceValueBatchEntry[],
5595
+ captureScope?: RequestCaptureScopeState | null,
5441
5596
  ): Promise<TraceValueCaptureRef | undefined> {
5442
- const result = await createCapturedValueEntryAsync(params);
5597
+ let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
5598
+ try {
5599
+ result = await createCapturedValueEntryAsync(params);
5600
+ } catch {
5601
+ recordRequestCaptureFailure(
5602
+ captureScope ?? getCtx().captureScope,
5603
+ 'privacy.full_value',
5604
+ params.target,
5605
+ 'privacy_transform_failed',
5606
+ );
5607
+ return { status: 'failed-policy' };
5608
+ }
5443
5609
  if (!result) return undefined;
5444
5610
  if (result.entry) {
5445
5611
  sink.push(result.entry);
@@ -5475,15 +5641,27 @@ async function maybeCaptureDbValueFromCandidatesAsync(
5475
5641
  capture: FullValueCaptureContext;
5476
5642
  },
5477
5643
  sink: TraceValueBatchEntry[],
5644
+ captureScope?: RequestCaptureScopeState | null,
5478
5645
  ): Promise<TraceValueCaptureRef | undefined> {
5479
5646
  let skippedRef: TraceValueCaptureRef | undefined;
5480
5647
  for (const rawValue of params.rawValues) {
5481
- const result = await createCapturedValueEntryAsync({
5482
- target: params.target,
5483
- rawValue,
5484
- previewValue: params.previewValue,
5485
- capture: params.capture,
5486
- });
5648
+ let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
5649
+ try {
5650
+ result = await createCapturedValueEntryAsync({
5651
+ target: params.target,
5652
+ rawValue,
5653
+ previewValue: params.previewValue,
5654
+ capture: params.capture,
5655
+ });
5656
+ } catch {
5657
+ recordRequestCaptureFailure(
5658
+ captureScope ?? getCtx().captureScope,
5659
+ 'privacy.full_value',
5660
+ params.target,
5661
+ 'privacy_transform_failed',
5662
+ );
5663
+ return { status: 'failed-policy' };
5664
+ }
5487
5665
  if (!result) continue;
5488
5666
  if (result.entry) {
5489
5667
  sink.push(result.entry);
@@ -5791,7 +5969,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
5791
5969
 
5792
5970
  const clockSkewMs = headerTs !== null ? headerTs - requestStartRaw : 0;
5793
5971
  const rid = nextRequestId(requestEpochMs);
5794
- const captureScope = createRequestCaptureScope(rid, 'http');
5972
+ const captureScope = createRequestCaptureScope(rid, 'http', captureCfg);
5795
5973
  emitRequestCaptureStart(captureCfg, sid, aid, captureScope, requestEpochMs);
5796
5974
  const t0 = requestStartRaw;
5797
5975
  const url = (req as any).originalUrl || req.url || '/';
@@ -5993,9 +6171,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
5993
6171
  endpointTraceCtx,
5994
6172
  masking,
5995
6173
  activePrivacy,
6174
+ undefined,
6175
+ captureScope,
5996
6176
  );
5997
6177
  const requestBody = requestBodyMaterialization.value;
5998
- const requestParams = await applyPrivacyThenMaskAsync(
6178
+ const requestParamsMaterialization = await materializeInlinePrivacyValueAsync(
5999
6179
  'request.params',
6000
6180
  sanitizeRequestSnapshot((req as any).params),
6001
6181
  captureCfg,
@@ -6003,8 +6183,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6003
6183
  endpointTraceCtx,
6004
6184
  masking,
6005
6185
  activePrivacy,
6186
+ undefined,
6187
+ captureScope,
6006
6188
  );
6007
- const requestQuery = await applyPrivacyThenMaskAsync(
6189
+ const requestParams = requestParamsMaterialization.value;
6190
+ const requestQueryMaterialization = await materializeInlinePrivacyValueAsync(
6008
6191
  'request.query',
6009
6192
  sanitizeRequestSnapshot((req as any).query),
6010
6193
  captureCfg,
@@ -6012,8 +6195,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6012
6195
  endpointTraceCtx,
6013
6196
  masking,
6014
6197
  activePrivacy,
6198
+ undefined,
6199
+ captureScope,
6015
6200
  );
6016
- const maskedHeaders = await applyPrivacyThenMaskAsync(
6201
+ const requestQuery = requestQueryMaterialization.value;
6202
+ const requestHeadersMaterialization = await materializeInlinePrivacyValueAsync(
6017
6203
  'request.headers',
6018
6204
  requestHeaders,
6019
6205
  captureCfg,
@@ -6021,7 +6207,10 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6021
6207
  endpointTraceCtx,
6022
6208
  masking,
6023
6209
  activePrivacy,
6210
+ undefined,
6211
+ captureScope,
6024
6212
  );
6213
+ const maskedHeaders = requestHeadersMaterialization.value;
6025
6214
  const responseBodyMaterialization = capturedBody === undefined
6026
6215
  ? { value: undefined }
6027
6216
  : limitRawInlinePrivacyValue('response.body', capturedBody)
@@ -6033,6 +6222,8 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6033
6222
  endpointTraceCtx,
6034
6223
  masking,
6035
6224
  activePrivacy,
6225
+ undefined,
6226
+ captureScope,
6036
6227
  );
6037
6228
  const responseBody = responseBodyMaterialization.value;
6038
6229
  const requestValueEntries: TraceValueBatchEntry[] = [];
@@ -6050,7 +6241,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6050
6241
  masking,
6051
6242
  privacy: activePrivacy,
6052
6243
  },
6053
- }, requestValueEntries);
6244
+ }, requestValueEntries, captureScope);
6054
6245
  const paramsValueCapture = await maybeCaptureRequestValueAsync({
6055
6246
  target: 'request.params',
6056
6247
  rawValue: (req as any).params,
@@ -6063,7 +6254,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6063
6254
  masking,
6064
6255
  privacy: activePrivacy,
6065
6256
  },
6066
- }, requestValueEntries);
6257
+ }, requestValueEntries, captureScope);
6067
6258
  const queryValueCapture = await maybeCaptureRequestValueAsync({
6068
6259
  target: 'request.query',
6069
6260
  rawValue: (req as any).query,
@@ -6076,7 +6267,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6076
6267
  masking,
6077
6268
  privacy: activePrivacy,
6078
6269
  },
6079
- }, requestValueEntries);
6270
+ }, requestValueEntries, captureScope);
6080
6271
  const headersValueCapture = await maybeCaptureRequestValueAsync({
6081
6272
  target: 'request.headers',
6082
6273
  rawValue: req.headers,
@@ -6089,7 +6280,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6089
6280
  masking,
6090
6281
  privacy: activePrivacy,
6091
6282
  },
6092
- }, requestValueEntries);
6283
+ }, requestValueEntries, captureScope);
6093
6284
  const respBodyValueCapture = capturedBody === undefined
6094
6285
  ? undefined
6095
6286
  : await maybeCaptureRequestValueAsync({
@@ -6104,7 +6295,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6104
6295
  masking,
6105
6296
  privacy: activePrivacy,
6106
6297
  },
6107
- }, requestValueEntries);
6298
+ }, requestValueEntries, captureScope);
6108
6299
 
6109
6300
  const responseDurationMs =
6110
6301
  finishedAt !== null && Number.isFinite(finishedAt)
@@ -6165,9 +6356,43 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6165
6356
  t: requestEpochMs,
6166
6357
  }]
6167
6358
  });
6168
- captureScope.backendRequestCount += 1;
6169
6359
  } catch {
6170
- // never break user code
6360
+ recordRequestCaptureFailure(
6361
+ captureScope,
6362
+ 'request.capture',
6363
+ 'backend_request',
6364
+ 'request_materialization_failed',
6365
+ );
6366
+ // Preserve non-sensitive request metadata so the manifest cannot
6367
+ // silently turn a failed policy capture into a zero-request scope.
6368
+ post(captureCfg, sid, {
6369
+ entries: [{
6370
+ actionId: aid,
6371
+ request: {
6372
+ rid,
6373
+ method: req.method,
6374
+ url,
6375
+ path,
6376
+ status: res.statusCode,
6377
+ durMs:
6378
+ finishedAt !== null && Number.isFinite(finishedAt)
6379
+ ? Math.max(0, finishedAt - t0)
6380
+ : Date.now() - t0,
6381
+ key,
6382
+ bodyMaterialization: {
6383
+ status: 'skipped',
6384
+ reason: 'request_materialization_failed',
6385
+ target: 'request.body',
6386
+ },
6387
+ respBodyMaterialization: {
6388
+ status: 'skipped',
6389
+ reason: 'request_materialization_failed',
6390
+ target: 'response.body',
6391
+ },
6392
+ },
6393
+ t: requestEpochMs,
6394
+ }],
6395
+ });
6171
6396
  }
6172
6397
  };
6173
6398
 
@@ -6290,6 +6515,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6290
6515
  maskReq,
6291
6516
  masking,
6292
6517
  resolvePrivacy,
6518
+ captureScope,
6293
6519
  });
6294
6520
  const baseEvents = balanceTraceEvents(materializedEvents.slice());
6295
6521
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
@@ -6308,12 +6534,21 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6308
6534
  }
6309
6535
 
6310
6536
  } catch {
6537
+ recordRequestCaptureFailure(
6538
+ captureScope,
6539
+ 'trace.capture',
6540
+ 'trace.batches',
6541
+ 'trace_capture_failed',
6542
+ );
6311
6543
  captureScope.traceBatchCount = 0;
6312
6544
  traceBatches = [];
6313
- } finally {
6314
- emitRequestCaptureManifest(traceBatches.length);
6315
6545
  }
6316
6546
 
6547
+ // Finalize policy/materialization status before publishing the
6548
+ // high-priority manifest. The manifest must lead its evidence
6549
+ // on the wire so a large trace queue cannot starve completion.
6550
+ emitRequestCaptureManifest(traceBatches.length);
6551
+
6317
6552
  try {
6318
6553
  for (let i = 0; i < traceBatches.length; i++) {
6319
6554
  const batch = traceBatches[i];
@@ -6334,7 +6569,12 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6334
6569
  });
6335
6570
  }
6336
6571
  } catch {
6337
- // never break user code
6572
+ recordRequestCaptureFailure(
6573
+ captureScope,
6574
+ 'trace.enqueue',
6575
+ 'trace.batches',
6576
+ 'trace_enqueue_failed',
6577
+ );
6338
6578
  }
6339
6579
  };
6340
6580
  }
@@ -6935,6 +7175,11 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
6935
7175
  const ctx = getCtx() as Ctx;
6936
7176
  const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
6937
7177
  if (!shouldCaptureDbSpan(spanContext)) return;
7178
+ if (sid && ctx.captureScope) {
7179
+ // Count the observed database operation before asynchronous privacy
7180
+ // materialization. A later failure must remain visible to completeness.
7181
+ ctx.captureScope.dbChangeCount += 1;
7182
+ }
6938
7183
  const runCtx: Ctx = {
6939
7184
  sid: ctx.sid,
6940
7185
  aid: ctx.aid,
@@ -6946,7 +7191,14 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
6946
7191
  };
6947
7192
  scheduleSdkBackgroundWork(async () => {
6948
7193
  await als.run(runCtx, async () => {
6949
- await emitDbQueryAsync(cfg, sid, aid, payload).catch(() => undefined);
7194
+ await emitDbQueryAsync(cfg, sid, aid, payload).catch(() => {
7195
+ recordRequestCaptureFailure(
7196
+ runCtx.captureScope,
7197
+ 'db.capture',
7198
+ 'db.change',
7199
+ 'db_capture_failed',
7200
+ );
7201
+ });
6950
7202
  });
6951
7203
  }, { priority: 'high' });
6952
7204
  }
@@ -7061,8 +7313,13 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
7061
7313
  t: payload.t,
7062
7314
  }]
7063
7315
  });
7064
- if (enqueued && ctx.captureScope) {
7065
- ctx.captureScope.dbChangeCount += 1;
7316
+ if (!enqueued) {
7317
+ recordRequestCaptureFailure(
7318
+ ctx.captureScope,
7319
+ 'db.enqueue',
7320
+ 'db.change',
7321
+ 'db_enqueue_failed',
7322
+ );
7066
7323
  }
7067
7324
  }
7068
7325
 
@@ -7498,6 +7755,7 @@ async function materializeKafkaRequestBody(
7498
7755
  cfg: ReproMiddlewareConfig,
7499
7756
  maskReq: MaskRequestContext,
7500
7757
  body: Record<string, any>,
7758
+ captureScope?: RequestCaptureScopeState | null,
7501
7759
  ): Promise<InlinePrivacyMaterializationResult> {
7502
7760
  return materializeInlinePrivacyValueAsync(
7503
7761
  'request.body',
@@ -7507,6 +7765,8 @@ async function materializeKafkaRequestBody(
7507
7765
  null,
7508
7766
  normalizeMaskingConfig(cfg.masking),
7509
7767
  getRuntimePrivacyState(cfg).policy ?? null,
7768
+ undefined,
7769
+ captureScope,
7510
7770
  );
7511
7771
  }
7512
7772
 
@@ -7595,6 +7855,7 @@ function recordKafkaTraceEvent(
7595
7855
  fn: raw.fn,
7596
7856
  wrapperClass: inferWrapperClassFromFn(raw.fn),
7597
7857
  file: raw.file ?? null,
7858
+ sourceFile: sourceFileForLibrary,
7598
7859
  line: typeof raw.line === 'number' ? raw.line : null,
7599
7860
  depth: typeof raw.depth === 'number' ? raw.depth : undefined,
7600
7861
  library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
@@ -7727,6 +7988,7 @@ function buildPendingKafkaTraceEvent(raw: any): PendingTraceEventRecord | null {
7727
7988
  fn: raw.fn,
7728
7989
  wrapperClass: inferWrapperClassFromFn(raw.fn),
7729
7990
  file: raw.file ?? null,
7991
+ sourceFile: sourceFileForLibrary,
7730
7992
  line: typeof raw.line === 'number' ? raw.line : null,
7731
7993
  depth: typeof raw.depth === 'number' ? raw.depth : undefined,
7732
7994
  library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
@@ -7844,22 +8106,31 @@ function scheduleKafkaTraceFlush(
7844
8106
  const evt = buildPendingKafkaTraceEvent(rawTraceEvent);
7845
8107
  if (evt) pendingEvents.push(evt);
7846
8108
  }
7847
- if (!pendingEvents.length) return;
7848
- const masking = normalizeMaskingConfig(cfg.masking);
7849
- const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
7850
- const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
7851
- cfg,
7852
- maskReq,
7853
- masking,
7854
- resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
7855
- });
7856
- const entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
8109
+ let entries: LegacyEntry[] = [];
8110
+ try {
8111
+ if (pendingEvents.length) {
8112
+ const masking = normalizeMaskingConfig(cfg.masking);
8113
+ const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
8114
+ const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
8115
+ cfg,
8116
+ maskReq,
8117
+ masking,
8118
+ resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8119
+ captureScope,
8120
+ });
8121
+ entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
8122
+ }
8123
+ } catch {
8124
+ recordRequestCaptureFailure(
8125
+ captureScope,
8126
+ 'trace.capture',
8127
+ 'trace.batches',
8128
+ 'trace_capture_failed',
8129
+ );
8130
+ }
7857
8131
  if (captureScope) {
7858
8132
  captureScope.traceBatchCount = entries.length;
7859
8133
  }
7860
- if (entries.length) {
7861
- post(cfg, sid, { entries });
7862
- }
7863
8134
  if (captureScope) {
7864
8135
  post(cfg, sid, {
7865
8136
  entries: [{
@@ -7871,7 +8142,10 @@ function scheduleKafkaTraceFlush(
7871
8142
  ),
7872
8143
  t: alignedNow(),
7873
8144
  }],
7874
- });
8145
+ }, { priority: 'high' });
8146
+ }
8147
+ if (entries.length) {
8148
+ post(cfg, sid, { entries });
7875
8149
  }
7876
8150
  });
7877
8151
  }, { priority: 'high' });
@@ -7890,7 +8164,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
7890
8164
  const aid = ctx.aid ?? fallback.aid ?? null;
7891
8165
  const span = captureSpanContextFromTracer();
7892
8166
  const publishRid = nextRequestId(alignedNow());
7893
- const captureScope = createRequestCaptureScope(publishRid, 'kafka');
8167
+ const captureScope = createRequestCaptureScope(publishRid, 'kafka', cfg);
7894
8168
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
7895
8169
  const payloadObj = payload && typeof payload === 'object' ? payload : {};
7896
8170
  const topic = sanitizeKafkaTopic((payloadObj as any).topic);
@@ -7949,7 +8223,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
7949
8223
  traceId: span?.traceId ?? null,
7950
8224
  spanId: span?.spanId ?? null,
7951
8225
  parentSpanId: span?.parentSpanId ?? null,
7952
- });
8226
+ }, captureScope);
7953
8227
 
7954
8228
  const requestPayload: Record<string, any> = {
7955
8229
  rid: publishRid,
@@ -7980,7 +8254,6 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
7980
8254
  },
7981
8255
  ],
7982
8256
  });
7983
- captureScope.backendRequestCount += 1;
7984
8257
  post(cfg, sid, {
7985
8258
  entries: [{
7986
8259
  actionId: aid ?? null,
@@ -8005,7 +8278,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
8005
8278
  const aid = ctx.aid ?? fallback.aid ?? null;
8006
8279
  const span = captureSpanContextFromTracer();
8007
8280
  const publishRid = nextRequestId(alignedNow());
8008
- const captureScope = createRequestCaptureScope(publishRid, 'kafka');
8281
+ const captureScope = createRequestCaptureScope(publishRid, 'kafka', cfg);
8009
8282
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
8010
8283
 
8011
8284
  const payloadObj = payload && typeof payload === 'object' ? payload : {};
@@ -8099,7 +8372,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
8099
8372
  traceId: span?.traceId ?? null,
8100
8373
  spanId: span?.spanId ?? null,
8101
8374
  parentSpanId: span?.parentSpanId ?? null,
8102
- });
8375
+ }, captureScope);
8103
8376
  const requestPayload: Record<string, any> = {
8104
8377
  rid: publishRid,
8105
8378
  method: 'KAFKA_PUBLISH_BATCH',
@@ -8129,7 +8402,6 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
8129
8402
  },
8130
8403
  ],
8131
8404
  });
8132
- captureScope.backendRequestCount += 1;
8133
8405
  post(cfg, sid, {
8134
8406
  entries: [{
8135
8407
  actionId: aid ?? null,
@@ -8156,7 +8428,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
8156
8428
  const aid = runtime.aid;
8157
8429
  const consumeRid = nextRequestId(`${topic}-${message?.offset ?? alignedNow()}`);
8158
8430
  const traceId = consumeRid;
8159
- const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
8431
+ const captureScope = createRequestCaptureScope(consumeRid, 'kafka', cfg);
8160
8432
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
8161
8433
  const rawTraceEvents: any[] = [];
8162
8434
  const maskReq: MaskRequestContext = {
@@ -8228,7 +8500,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
8228
8500
  valueEncoding: messagePayload.valueEncoding,
8229
8501
  valueBytes: messagePayload.valueBytes,
8230
8502
  message: messagePayload,
8231
- });
8503
+ }, captureScope);
8232
8504
  const requestPayload: Record<string, any> = {
8233
8505
  rid: consumeRid,
8234
8506
  method: 'KAFKA_CONSUME',
@@ -8250,7 +8522,6 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
8250
8522
  t: alignedNow(),
8251
8523
  }],
8252
8524
  });
8253
- captureScope.backendRequestCount += 1;
8254
8525
  scheduleKafkaTraceFlush(cfg, kafkaCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
8255
8526
  }
8256
8527
  };
@@ -8267,7 +8538,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
8267
8538
  const aid = runtime.aid;
8268
8539
  const consumeRid = nextRequestId(`${topic}-batch-${alignedNow()}`);
8269
8540
  const traceId = consumeRid;
8270
- const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
8541
+ const captureScope = createRequestCaptureScope(consumeRid, 'kafka', cfg);
8271
8542
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
8272
8543
  const rawTraceEvents: any[] = [];
8273
8544
  const maskReq: MaskRequestContext = {
@@ -8337,7 +8608,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
8337
8608
  parentSpanId: runtime.parentSpanId,
8338
8609
  firstMessageKey: firstMessagePayload?.key ?? null,
8339
8610
  messages: messages.map((message: any) => buildKafkaMessagePayload(message)),
8340
- });
8611
+ }, captureScope);
8341
8612
  const requestPayload: Record<string, any> = {
8342
8613
  rid: consumeRid,
8343
8614
  method: 'KAFKA_CONSUME_BATCH',
@@ -8359,7 +8630,6 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
8359
8630
  t: alignedNow(),
8360
8631
  }],
8361
8632
  });
8362
- captureScope.backendRequestCount += 1;
8363
8633
  scheduleKafkaTraceFlush(cfg, kafkaCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
8364
8634
  }
8365
8635
  };
@@ -8782,24 +9052,28 @@ function scheduleAmqpTraceFlush(
8782
9052
  }
8783
9053
 
8784
9054
  let entries: LegacyEntry[] = [];
8785
- if (pendingEvents.length) {
8786
- const masking = normalizeMaskingConfig(cfg.masking);
8787
- const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
8788
- const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
8789
- cfg,
8790
- maskReq,
8791
- masking,
8792
- resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8793
- });
8794
- entries = buildAmqpTraceEntries(actionId, requestRid, materializedEvents);
8795
- captureScope.traceBatchCount = entries.length;
8796
- if (entries.length) {
8797
- post(cfg, sid, { entries });
9055
+ try {
9056
+ if (pendingEvents.length) {
9057
+ const masking = normalizeMaskingConfig(cfg.masking);
9058
+ const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
9059
+ const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
9060
+ cfg,
9061
+ maskReq,
9062
+ masking,
9063
+ resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
9064
+ captureScope,
9065
+ });
9066
+ entries = buildAmqpTraceEntries(actionId, requestRid, materializedEvents);
8798
9067
  }
8799
- } else {
8800
- captureScope.traceBatchCount = 0;
9068
+ } catch {
9069
+ recordRequestCaptureFailure(
9070
+ captureScope,
9071
+ 'trace.capture',
9072
+ 'trace.batches',
9073
+ 'trace_capture_failed',
9074
+ );
8801
9075
  }
8802
-
9076
+ captureScope.traceBatchCount = entries.length;
8803
9077
  post(cfg, sid, {
8804
9078
  entries: [{
8805
9079
  actionId: actionId ?? null,
@@ -8810,7 +9084,10 @@ function scheduleAmqpTraceFlush(
8810
9084
  ),
8811
9085
  t: alignedNow(),
8812
9086
  }],
8813
- });
9087
+ }, { priority: 'high' });
9088
+ if (entries.length) {
9089
+ post(cfg, sid, { entries });
9090
+ }
8814
9091
  });
8815
9092
  }, { priority: 'high' });
8816
9093
  }
@@ -8882,7 +9159,12 @@ function scheduleAmqpPublishCapture(params: {
8882
9159
  };
8883
9160
 
8884
9161
  scheduleSdkBackgroundWork(async () => {
8885
- const bodyMaterialization = await materializeKafkaRequestBody(params.cfg, maskReq, body);
9162
+ const bodyMaterialization = await materializeKafkaRequestBody(
9163
+ params.cfg,
9164
+ maskReq,
9165
+ body,
9166
+ params.captureScope,
9167
+ );
8886
9168
  const requestPayload: Record<string, any> = {
8887
9169
  rid: params.publishRid,
8888
9170
  method: params.method,
@@ -8913,7 +9195,6 @@ function scheduleAmqpPublishCapture(params: {
8913
9195
  t: alignedNow(),
8914
9196
  }],
8915
9197
  });
8916
- params.captureScope.backendRequestCount += 1;
8917
9198
  post(params.cfg, params.sid!, {
8918
9199
  entries: [{
8919
9200
  actionId: params.aid ?? null,
@@ -8975,7 +9256,7 @@ function patchAmqplibPublishMethod(
8975
9256
  : `amqp://queue/${queue}`;
8976
9257
  const key = `${method} ${methodName === 'publish' ? `${exchange || 'default'}/${routingKey}` : queue}`;
8977
9258
  const content = methodName === 'publish' ? args[2] : args[1];
8978
- const captureScope = createRequestCaptureScope(publishRid, 'amqp');
9259
+ const captureScope = createRequestCaptureScope(publishRid, 'amqp', cfg);
8979
9260
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
8980
9261
  const t0 = Date.now();
8981
9262
  let finished = false;
@@ -9065,7 +9346,7 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
9065
9346
  const deliveryTag = message?.fields?.deliveryTag ?? alignedNow();
9066
9347
  const consumeRid = nextRequestId(`${queueName}-${deliveryTag}`);
9067
9348
  const traceId = consumeRid;
9068
- const captureScope = createRequestCaptureScope(consumeRid, 'amqp');
9349
+ const captureScope = createRequestCaptureScope(consumeRid, 'amqp', cfg);
9069
9350
  emitRequestCaptureStart(cfg, sid, aid, captureScope);
9070
9351
  const rawTraceEvents: any[] = [];
9071
9352
  const maskReq: MaskRequestContext = {
@@ -9150,7 +9431,12 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
9150
9431
  };
9151
9432
 
9152
9433
  scheduleSdkBackgroundWork(async () => {
9153
- const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, body);
9434
+ const bodyMaterialization = await materializeKafkaRequestBody(
9435
+ cfg,
9436
+ maskReq,
9437
+ body,
9438
+ captureScope,
9439
+ );
9154
9440
  const requestPayload: Record<string, any> = {
9155
9441
  rid: consumeRid,
9156
9442
  method: 'AMQP_CONSUME',
@@ -9172,7 +9458,6 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
9172
9458
  t: alignedNow(),
9173
9459
  }],
9174
9460
  });
9175
- captureScope.backendRequestCount += 1;
9176
9461
  scheduleAmqpTraceFlush(cfg, amqpCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
9177
9462
  }, { priority: 'high' });
9178
9463
  };