@reproapp/node-sdk 0.0.47 → 0.0.49

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
@@ -2,7 +2,7 @@ import type { Request, Response, NextFunction } from 'express';
2
2
  import type { Schema, Model, Query } from 'mongoose';
3
3
  import * as mongoose from 'mongoose';
4
4
  import { AsyncLocalStorage } from 'async_hooks';
5
- import { randomUUID } from 'crypto';
5
+ import { createHash, createHmac, randomUUID } from 'crypto';
6
6
  import { createRequire } from 'module';
7
7
  import { join } from 'path';
8
8
  import { pathToFileURL } from 'url';
@@ -28,6 +28,7 @@ import {
28
28
  type RuntimePrivacySurface,
29
29
  type RuntimePrivacyTraceContext,
30
30
  } from './privacy';
31
+ import { projectSemanticInfrastructureValue } from './semantic-serialization';
31
32
 
32
33
  /**
33
34
  * ============================= IMPORTANT =============================
@@ -418,6 +419,7 @@ type TraceValueBatchEntry = {
418
419
  };
419
420
 
420
421
  type TraceMaterializationConfig = {
422
+ tenantId: string;
421
423
  appId: string;
422
424
  appSecret?: string;
423
425
  appName?: string;
@@ -431,6 +433,7 @@ type TraceMaterializationWorkerPayload = {
431
433
  masking: NormalizedMaskingConfig | null;
432
434
  privacy: NormalizedRuntimePrivacyPolicy | null;
433
435
  fullValueCaptureEnabled: boolean;
436
+ valueNamespace?: string;
434
437
  };
435
438
 
436
439
  type TraceMaterializationWorkerRequest = {
@@ -1224,6 +1227,8 @@ type RequestCaptureScopeState = {
1224
1227
  privacyMode: 'policy' | 'fallback' | 'raw';
1225
1228
  privacyPolicyFingerprint?: string;
1226
1229
  privacyStatementCount: number;
1230
+ emittedValueDefinitionIds: Set<string>;
1231
+ traceObservationSnapshots: Map<string, any>;
1227
1232
  };
1228
1233
 
1229
1234
  function createRequestCaptureScope(
@@ -1251,6 +1256,8 @@ function createRequestCaptureScope(
1251
1256
  privacyMode,
1252
1257
  privacyPolicyFingerprint: privacy?.fingerprint,
1253
1258
  privacyStatementCount: privacy?.statements.length ?? 0,
1259
+ emittedValueDefinitionIds: new Set(),
1260
+ traceObservationSnapshots: new Map(),
1254
1261
  };
1255
1262
  }
1256
1263
 
@@ -2828,7 +2835,11 @@ const TRACE_MAX_EVENTS_PER_REQUEST = (() => {
2828
2835
  const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
2829
2836
  const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
2830
2837
  if (Number.isFinite(env) && env > 0) return Math.trunc(env);
2831
- return Math.ceil(TRACE_MAX_EVENTS_PER_REQUEST / TRACE_BATCH_SIZE);
2838
+ // Byte-based splitting can legitimately produce one batch per event when
2839
+ // every event owns a distinct large value. The event limit remains the
2840
+ // hard upper bound; deriving this from the count-only batch size silently
2841
+ // dropped otherwise valid events after ten batches.
2842
+ return TRACE_MAX_EVENTS_PER_REQUEST;
2832
2843
  })();
2833
2844
  const TRACE_FLUSH_DELAY_MS = 20;
2834
2845
  // Choose how to order trace events in payloads.
@@ -3180,6 +3191,17 @@ function sanitizeTraceValue(
3180
3191
  const mongoPlaceholder = describeMongoPlaceholder(value);
3181
3192
  if (mongoPlaceholder) return mongoPlaceholder;
3182
3193
 
3194
+ const semanticProjection = projectSemanticInfrastructureValue(value);
3195
+ if (semanticProjection) {
3196
+ return sanitizeTraceValue(
3197
+ semanticProjection.value,
3198
+ depth,
3199
+ seen,
3200
+ options,
3201
+ valuePath,
3202
+ );
3203
+ }
3204
+
3183
3205
  if (isThenable(value)) {
3184
3206
  return { __type: 'Promise', state: 'pending' };
3185
3207
  }
@@ -3383,6 +3405,107 @@ function hasMeaningfulRawTraceError(error: any): boolean {
3383
3405
  return error !== undefined && error !== null;
3384
3406
  }
3385
3407
 
3408
+ const TRACE_OBSERVATION_SNAPSHOT = Symbol('repro.traceObservationSnapshot');
3409
+
3410
+ // Trace events can remain queued after user code mutates the observed objects.
3411
+ // Intern immutable semantic snapshots per request so temporal evidence stays
3412
+ // correct without retaining another full copy for repeated unchanged values.
3413
+ function internTraceObservationSnapshot(
3414
+ snapshot: any,
3415
+ captureScope?: RequestCaptureScopeState | null,
3416
+ ): any {
3417
+ if (!captureScope || snapshot === null || typeof snapshot !== 'object') {
3418
+ return snapshot;
3419
+ }
3420
+
3421
+ const serialized = stableSerializeTraceValue(snapshot);
3422
+ if (serialized === null) return snapshot;
3423
+
3424
+ const digest = createHash('sha256')
3425
+ .update('repro-trace-observation-v1\0')
3426
+ .update(serialized)
3427
+ .digest('hex');
3428
+ const existing = captureScope.traceObservationSnapshots.get(digest);
3429
+ if (existing !== undefined) return existing;
3430
+
3431
+ captureScope.traceObservationSnapshots.set(digest, snapshot);
3432
+ return snapshot;
3433
+ }
3434
+
3435
+ function snapshotTraceValueAtObservation(
3436
+ value: any,
3437
+ captureScope?: RequestCaptureScopeState | null,
3438
+ valuePath = '$',
3439
+ target = 'trace.value',
3440
+ ): any {
3441
+ try {
3442
+ const snapshot = sanitizeTraceValue(
3443
+ value,
3444
+ 0,
3445
+ new WeakMap(),
3446
+ { preserveLongStrings: true, disableTruncation: true },
3447
+ valuePath,
3448
+ );
3449
+ return internTraceObservationSnapshot(snapshot, captureScope);
3450
+ } catch {
3451
+ recordRequestCaptureFailure(
3452
+ captureScope,
3453
+ 'trace.snapshot',
3454
+ target,
3455
+ 'snapshot_failed',
3456
+ );
3457
+ return makeOmittedValue('capture-snapshot-failed');
3458
+ }
3459
+ }
3460
+
3461
+ function snapshotTraceArgsAtObservation(
3462
+ values: any,
3463
+ captureScope?: RequestCaptureScopeState | null,
3464
+ ): any[] {
3465
+ const snapshot = snapshotTraceValueAtObservation(
3466
+ normalizeRawTraceArgs(values),
3467
+ captureScope,
3468
+ '$',
3469
+ 'trace.args',
3470
+ );
3471
+ return Array.isArray(snapshot) ? snapshot : [snapshot];
3472
+ }
3473
+
3474
+ function snapshotRawTraceEventAtObservation(
3475
+ raw: any,
3476
+ captureScope?: RequestCaptureScopeState | null,
3477
+ ): any {
3478
+ if (!raw || typeof raw !== 'object' || raw[TRACE_OBSERVATION_SNAPSHOT] === true) {
3479
+ return raw;
3480
+ }
3481
+
3482
+ const snapshot = { ...raw };
3483
+ if (raw.args !== undefined) {
3484
+ snapshot.args = snapshotTraceArgsAtObservation(raw.args, captureScope);
3485
+ }
3486
+ if (raw.returnValue !== undefined) {
3487
+ snapshot.returnValue = snapshotTraceValueAtObservation(
3488
+ raw.returnValue,
3489
+ captureScope,
3490
+ '$',
3491
+ 'trace.returnValue',
3492
+ );
3493
+ }
3494
+ if (hasMeaningfulRawTraceError(raw.error)) {
3495
+ snapshot.error = snapshotTraceValueAtObservation(
3496
+ raw.error,
3497
+ captureScope,
3498
+ '$',
3499
+ 'trace.error',
3500
+ );
3501
+ }
3502
+ Object.defineProperty(snapshot, TRACE_OBSERVATION_SNAPSHOT, {
3503
+ value: true,
3504
+ enumerable: false,
3505
+ });
3506
+ return snapshot;
3507
+ }
3508
+
3386
3509
  function attachTraceValueEntry(
3387
3510
  event: TraceEventRecord | PendingTraceEventRecord,
3388
3511
  entry: TraceValueBatchEntry,
@@ -3403,6 +3526,7 @@ async function materializePendingTraceEvents(
3403
3526
  masking: NormalizedMaskingConfig | null;
3404
3527
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3405
3528
  captureScope?: RequestCaptureScopeState | null;
3529
+ valueNamespace?: string;
3406
3530
  },
3407
3531
  ): Promise<TraceEventRecord[]> {
3408
3532
  for (const evt of events) {
@@ -3420,6 +3544,7 @@ async function materializePendingTraceEvents(
3420
3544
  trace: candidate,
3421
3545
  masking: params.masking,
3422
3546
  privacy: activePrivacy,
3547
+ valueNamespace: params.valueNamespace,
3423
3548
  };
3424
3549
  const traceValueMetadata = {
3425
3550
  fn: evt.fn,
@@ -3556,6 +3681,7 @@ async function materializePendingTraceEventsAsync(
3556
3681
  masking: NormalizedMaskingConfig | null;
3557
3682
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3558
3683
  captureScope?: RequestCaptureScopeState | null;
3684
+ valueNamespace?: string;
3559
3685
  },
3560
3686
  ): Promise<TraceEventRecord[]> {
3561
3687
  if (!events.some((evt) => evt.__reproPending)) {
@@ -3583,6 +3709,7 @@ async function materializePendingTraceEventsAsync(
3583
3709
  return await materializePendingTraceEventsOffThread(events, {
3584
3710
  events,
3585
3711
  cfg: {
3712
+ tenantId: params.cfg.tenantId,
3586
3713
  appId: params.cfg.appId,
3587
3714
  appSecret: params.cfg.appSecret,
3588
3715
  appName: params.cfg.appName,
@@ -3592,6 +3719,7 @@ async function materializePendingTraceEventsAsync(
3592
3719
  masking: params.masking,
3593
3720
  privacy,
3594
3721
  fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
3722
+ valueNamespace: params.valueNamespace,
3595
3723
  });
3596
3724
  } catch (err) {
3597
3725
  try {
@@ -3627,6 +3755,7 @@ export async function __materializePendingTraceEventsForWorker(
3627
3755
  maskReq: payload.maskReq,
3628
3756
  masking: payload.masking,
3629
3757
  resolvePrivacy: () => payload.privacy,
3758
+ valueNamespace: payload.valueNamespace,
3630
3759
  });
3631
3760
  return materialized.map((event) => {
3632
3761
  const entries = getEventTraceValueEntries(event);
@@ -4435,6 +4564,7 @@ type FullValueCaptureContext = {
4435
4564
  masking: NormalizedMaskingConfig | null;
4436
4565
  privacy: NormalizedRuntimePrivacyPolicy | null;
4437
4566
  db?: RuntimePrivacyDbContext | null;
4567
+ valueNamespace?: string;
4438
4568
  };
4439
4569
 
4440
4570
  const TRACE_FULL_VALUE_ENTRY_SYMBOL = Symbol('repro.traceFullValueEntries');
@@ -4840,6 +4970,18 @@ function cloneFullValueCandidate(
4840
4970
  const mongoPlaceholder = describeMongoPlaceholder(value);
4841
4971
  if (mongoPlaceholder) return undefined;
4842
4972
 
4973
+ const semanticProjection = projectSemanticInfrastructureValue(value);
4974
+ if (semanticProjection) {
4975
+ return cloneFullValueCandidate(
4976
+ semanticProjection.value,
4977
+ ctx,
4978
+ limits,
4979
+ depth,
4980
+ seen,
4981
+ valuePath,
4982
+ );
4983
+ }
4984
+
4843
4985
  if (isThenable(value)) return undefined;
4844
4986
  if (shouldSkipFullValueObject(value)) return undefined;
4845
4987
 
@@ -5067,6 +5209,18 @@ async function cloneFullValueCandidateAsync(
5067
5209
  const mongoPlaceholder = describeMongoPlaceholder(value);
5068
5210
  if (mongoPlaceholder) return undefined;
5069
5211
 
5212
+ const semanticProjection = projectSemanticInfrastructureValue(value);
5213
+ if (semanticProjection) {
5214
+ return cloneFullValueCandidateAsync(
5215
+ semanticProjection.value,
5216
+ ctx,
5217
+ limits,
5218
+ depth,
5219
+ seen,
5220
+ valuePath,
5221
+ );
5222
+ }
5223
+
5070
5224
  if (isThenable(value)) return undefined;
5071
5225
  if (shouldSkipFullValueObject(value)) return undefined;
5072
5226
 
@@ -5213,12 +5367,18 @@ function getEventTraceValueEntries(event: TraceEventRecord | PendingTraceEventRe
5213
5367
  return undefined;
5214
5368
  }
5215
5369
 
5216
- function collectBatchTraceValueEntries(batch: TraceEventRecord[], batchIndex: number): TraceValueBatchEntry[] {
5370
+ function collectBatchTraceValueEntries(
5371
+ batch: TraceEventRecord[],
5372
+ batchIndex: number,
5373
+ emittedValueIds: Set<string> = new Set(),
5374
+ ): TraceValueBatchEntry[] {
5217
5375
  const collected: TraceValueBatchEntry[] = [];
5218
5376
  batch.forEach((event, eventIndex) => {
5219
5377
  const entries = getEventTraceValueEntries(event);
5220
5378
  if (!Array.isArray(entries) || !entries.length) return;
5221
5379
  entries.forEach((entry) => {
5380
+ if (emittedValueIds.has(entry.id)) return;
5381
+ emittedValueIds.add(entry.id);
5222
5382
  collected.push({
5223
5383
  ...entry,
5224
5384
  batchIndex,
@@ -5237,13 +5397,44 @@ function serializedByteLength(value: any): number {
5237
5397
  }
5238
5398
  }
5239
5399
 
5400
+ function contentAddressedValueId(
5401
+ target: TraceValueBatchEntry['target'],
5402
+ candidateSerialized: string,
5403
+ capture: FullValueCaptureContext,
5404
+ ): string {
5405
+ const config = capture.runtimeConfig;
5406
+ const signingKey = String(
5407
+ config.appSecret ||
5408
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
5409
+ );
5410
+ const namespace = String(
5411
+ capture.valueNamespace ||
5412
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
5413
+ );
5414
+ const digest = createHmac('sha256', signingKey)
5415
+ .update('repro-captured-value-v1\0')
5416
+ .update(namespace)
5417
+ .update('\0')
5418
+ .update(target)
5419
+ .update('\0')
5420
+ .update(candidateSerialized)
5421
+ .digest('hex')
5422
+ .slice(0, 40);
5423
+ return `rv1_${digest}`;
5424
+ }
5425
+
5240
5426
  function estimateTraceBatchSerializedBytes(
5241
5427
  batch: TraceEventRecord[],
5242
5428
  requestRid: string,
5243
5429
  actionId: string | null | undefined,
5244
5430
  batchIndex: number,
5431
+ previouslyEmittedValueIds: ReadonlySet<string> = new Set(),
5245
5432
  ): number {
5246
- const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
5433
+ const traceValues = collectBatchTraceValueEntries(
5434
+ batch,
5435
+ batchIndex,
5436
+ new Set(previouslyEmittedValueIds),
5437
+ );
5247
5438
  return serializedByteLength({
5248
5439
  actionId: actionId ?? null,
5249
5440
  trace: batch,
@@ -5267,15 +5458,26 @@ function chunkTraceEventsForTransport(
5267
5458
  const boundedEvents = boundTraceEventsForTransport(events);
5268
5459
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
5269
5460
  const sizedBatches: TraceEventRecord[][] = [];
5461
+ const emittedValueIds = new Set<string>();
5270
5462
  let current: TraceEventRecord[] = [];
5271
5463
 
5464
+ const commitCurrent = (): void => {
5465
+ if (!current.length) return;
5466
+ collectBatchTraceValueEntries(
5467
+ current,
5468
+ sizedBatches.length,
5469
+ emittedValueIds,
5470
+ );
5471
+ sizedBatches.push(current);
5472
+ current = [];
5473
+ };
5474
+
5272
5475
  for (const event of countBatches.flat()) {
5273
5476
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5274
5477
  break;
5275
5478
  }
5276
5479
  if (current.length >= TRACE_BATCH_SIZE) {
5277
- sizedBatches.push(current);
5278
- current = [];
5480
+ commitCurrent();
5279
5481
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5280
5482
  break;
5281
5483
  }
@@ -5287,15 +5489,15 @@ function chunkTraceEventsForTransport(
5287
5489
  requestRid,
5288
5490
  actionId,
5289
5491
  batchIndex,
5492
+ emittedValueIds,
5290
5493
  );
5291
5494
 
5292
5495
  if (
5293
5496
  current.length > 0 &&
5294
5497
  estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES
5295
5498
  ) {
5296
- sizedBatches.push(current);
5499
+ commitCurrent();
5297
5500
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5298
- current = [];
5299
5501
  break;
5300
5502
  }
5301
5503
  current = [event];
@@ -5309,7 +5511,7 @@ function chunkTraceEventsForTransport(
5309
5511
  current.length > 0 &&
5310
5512
  sizedBatches.length < TRACE_MAX_BATCHES_PER_REQUEST
5311
5513
  ) {
5312
- sizedBatches.push(current);
5514
+ commitCurrent();
5313
5515
  }
5314
5516
 
5315
5517
  return sizedBatches;
@@ -5381,7 +5583,11 @@ function createCapturedValueEntry(
5381
5583
  return undefined;
5382
5584
  }
5383
5585
 
5384
- const valueId = randomUUID();
5586
+ const valueId = contentAddressedValueId(
5587
+ params.target,
5588
+ candidateSerialized,
5589
+ params.capture,
5590
+ );
5385
5591
  const projectedKind =
5386
5592
  candidate && typeof candidate === 'object'
5387
5593
  ? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
@@ -5463,7 +5669,11 @@ async function createCapturedValueEntryAsync(
5463
5669
  return undefined;
5464
5670
  }
5465
5671
 
5466
- const valueId = randomUUID();
5672
+ const valueId = contentAddressedValueId(
5673
+ params.target,
5674
+ candidateSerialized,
5675
+ params.capture,
5676
+ );
5467
5677
  const projectedKind =
5468
5678
  candidate && typeof candidate === 'object'
5469
5679
  ? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
@@ -5486,6 +5696,18 @@ async function createCapturedValueEntryAsync(
5486
5696
  };
5487
5697
  }
5488
5698
 
5699
+ function appendCapturedValueDefinition(
5700
+ sink: TraceValueBatchEntry[],
5701
+ entry: TraceValueBatchEntry | undefined,
5702
+ captureScope?: RequestCaptureScopeState | null,
5703
+ ): void {
5704
+ if (!entry) return;
5705
+ const emitted = captureScope?.emittedValueDefinitionIds;
5706
+ if (emitted?.has(entry.id)) return;
5707
+ emitted?.add(entry.id);
5708
+ sink.push(entry);
5709
+ }
5710
+
5489
5711
  function maybeCaptureRequestValue(
5490
5712
  params: {
5491
5713
  target: 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body';
@@ -5497,9 +5719,7 @@ function maybeCaptureRequestValue(
5497
5719
  ): TraceValueCaptureRef | undefined {
5498
5720
  const result = createCapturedValueEntry(params);
5499
5721
  if (!result) return undefined;
5500
- if (result.entry) {
5501
- sink.push(result.entry);
5502
- }
5722
+ appendCapturedValueDefinition(sink, result.entry);
5503
5723
  return result.captureRef;
5504
5724
  }
5505
5725
 
@@ -5526,9 +5746,7 @@ async function maybeCaptureRequestValueAsync(
5526
5746
  return { status: 'failed-policy' };
5527
5747
  }
5528
5748
  if (!result) return undefined;
5529
- if (result.entry) {
5530
- sink.push(result.entry);
5531
- }
5749
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5532
5750
  return result.captureRef;
5533
5751
  }
5534
5752
 
@@ -5578,9 +5796,7 @@ function maybeCaptureDbValue(
5578
5796
  ): TraceValueCaptureRef | undefined {
5579
5797
  const result = createCapturedValueEntry(params);
5580
5798
  if (!result) return undefined;
5581
- if (result.entry) {
5582
- sink.push(result.entry);
5583
- }
5799
+ appendCapturedValueDefinition(sink, result.entry);
5584
5800
  return result.captureRef;
5585
5801
  }
5586
5802
 
@@ -5607,9 +5823,7 @@ async function maybeCaptureDbValueAsync(
5607
5823
  return { status: 'failed-policy' };
5608
5824
  }
5609
5825
  if (!result) return undefined;
5610
- if (result.entry) {
5611
- sink.push(result.entry);
5612
- }
5826
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5613
5827
  return result.captureRef;
5614
5828
  }
5615
5829
 
@@ -5664,7 +5878,7 @@ async function maybeCaptureDbValueFromCandidatesAsync(
5664
5878
  }
5665
5879
  if (!result) continue;
5666
5880
  if (result.entry) {
5667
- sink.push(result.entry);
5881
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5668
5882
  return result.captureRef;
5669
5883
  }
5670
5884
  if (!skippedRef) {
@@ -5935,21 +6149,24 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
5935
6149
  res.on('close', endTrackedClientRequest);
5936
6150
  return next(); // only capture tagged requests
5937
6151
  }
5938
- const captureCfg: ReproMiddlewareConfig = backendLiveChild
5939
- ? {
6152
+ let captureCfg: ReproMiddlewareConfig = cfg;
6153
+ if (backendLiveChild) {
6154
+ captureCfg = {
5940
6155
  ...cfg,
5941
6156
  actorId: backendLiveChild.actorId,
5942
6157
  actorType: backendLiveChild.actorType,
5943
6158
  actorLabels: backendLiveChild.actorLabels,
5944
- }
5945
- : taggedActorIdentity
5946
- ? {
5947
- ...cfg,
5948
- actorId: taggedActorIdentity.actorId,
5949
- actorType: taggedActorIdentity.actorType,
5950
- actorLabels: taggedActorIdentity.actorLabels,
5951
- }
5952
- : cfg;
6159
+ };
6160
+ shareRuntimePrivacyState(cfg, captureCfg);
6161
+ } else if (taggedActorIdentity) {
6162
+ captureCfg = {
6163
+ ...cfg,
6164
+ actorId: taggedActorIdentity.actorId,
6165
+ actorType: taggedActorIdentity.actorType,
6166
+ actorLabels: taggedActorIdentity.actorLabels,
6167
+ };
6168
+ shareRuntimePrivacyState(cfg, captureCfg);
6169
+ }
5953
6170
  if (taggedActorIdentity) {
5954
6171
  post(captureCfg, sid, {
5955
6172
  entries: [{
@@ -6240,6 +6457,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6240
6457
  trace: endpointTraceCtx,
6241
6458
  masking,
6242
6459
  privacy: activePrivacy,
6460
+ valueNamespace: sid,
6243
6461
  },
6244
6462
  }, requestValueEntries, captureScope);
6245
6463
  const paramsValueCapture = await maybeCaptureRequestValueAsync({
@@ -6253,6 +6471,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6253
6471
  trace: endpointTraceCtx,
6254
6472
  masking,
6255
6473
  privacy: activePrivacy,
6474
+ valueNamespace: sid,
6256
6475
  },
6257
6476
  }, requestValueEntries, captureScope);
6258
6477
  const queryValueCapture = await maybeCaptureRequestValueAsync({
@@ -6266,6 +6485,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6266
6485
  trace: endpointTraceCtx,
6267
6486
  masking,
6268
6487
  privacy: activePrivacy,
6488
+ valueNamespace: sid,
6269
6489
  },
6270
6490
  }, requestValueEntries, captureScope);
6271
6491
  const headersValueCapture = await maybeCaptureRequestValueAsync({
@@ -6279,6 +6499,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6279
6499
  trace: endpointTraceCtx,
6280
6500
  masking,
6281
6501
  privacy: activePrivacy,
6502
+ valueNamespace: sid,
6282
6503
  },
6283
6504
  }, requestValueEntries, captureScope);
6284
6505
  const respBodyValueCapture = capturedBody === undefined
@@ -6294,6 +6515,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6294
6515
  trace: endpointTraceCtx,
6295
6516
  masking,
6296
6517
  privacy: activePrivacy,
6518
+ valueNamespace: sid,
6297
6519
  },
6298
6520
  }, requestValueEntries, captureScope);
6299
6521
 
@@ -6302,7 +6524,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6302
6524
  ? Math.max(0, finishedAt - t0)
6303
6525
  : Date.now() - t0;
6304
6526
  const requestPayload: Record<string, any> = {
6305
- rid,
6527
+ rid,
6306
6528
  method: req.method,
6307
6529
  url,
6308
6530
  path,
@@ -6472,17 +6694,27 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6472
6694
  evt.__reproPending = {
6473
6695
  ...(ev.args !== undefined
6474
6696
  ? {
6475
- argsRaw: normalizeRawTraceArgs(ev.args),
6697
+ argsRaw: snapshotTraceArgsAtObservation(ev.args, captureScope),
6476
6698
  }
6477
6699
  : {}),
6478
6700
  ...(ev.returnValue !== undefined
6479
6701
  ? {
6480
- returnValueRaw: ev.returnValue,
6702
+ returnValueRaw: snapshotTraceValueAtObservation(
6703
+ ev.returnValue,
6704
+ captureScope,
6705
+ '$',
6706
+ 'trace.returnValue',
6707
+ ),
6481
6708
  }
6482
6709
  : {}),
6483
6710
  ...(hasErrorPayload
6484
6711
  ? {
6485
- errorRaw: ev.error,
6712
+ errorRaw: snapshotTraceValueAtObservation(
6713
+ ev.error,
6714
+ captureScope,
6715
+ '$',
6716
+ 'trace.error',
6717
+ ),
6486
6718
  }
6487
6719
  : {}),
6488
6720
  };
@@ -6516,6 +6748,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6516
6748
  masking,
6517
6749
  resolvePrivacy,
6518
6750
  captureScope,
6751
+ valueNamespace: sid,
6519
6752
  });
6520
6753
  const baseEvents = balanceTraceEvents(materializedEvents.slice());
6521
6754
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
@@ -6550,9 +6783,14 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6550
6783
  emitRequestCaptureManifest(traceBatches.length);
6551
6784
 
6552
6785
  try {
6786
+ const emittedValueIds = new Set<string>();
6553
6787
  for (let i = 0; i < traceBatches.length; i++) {
6554
6788
  const batch = traceBatches[i];
6555
- const traceValues = collectBatchTraceValueEntries(batch, i);
6789
+ const traceValues = collectBatchTraceValueEntries(
6790
+ batch,
6791
+ i,
6792
+ emittedValueIds,
6793
+ );
6556
6794
 
6557
6795
  post(captureCfg, sid, {
6558
6796
  entries: [{
@@ -7162,12 +7400,6 @@ function dehydrateComplexValue(value: any) {
7162
7400
  }
7163
7401
  } catch {}
7164
7402
 
7165
- const ctor = (value as any)?.constructor?.name;
7166
- if (ctor && ctor !== 'Object') {
7167
- const plain = safeJson(value);
7168
- if (plain !== undefined) return plain;
7169
- }
7170
-
7171
7403
  return value;
7172
7404
  }
7173
7405
 
@@ -7242,6 +7474,7 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
7242
7474
  masking: null,
7243
7475
  privacy,
7244
7476
  db: dbContext,
7477
+ valueNamespace: sid,
7245
7478
  };
7246
7479
  const dbValues: TraceValueBatchEntry[] = [];
7247
7480
  const pkValueCapture = pkMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
@@ -7249,37 +7482,37 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
7249
7482
  rawValue: payload.pk,
7250
7483
  previewValue: pkPreview,
7251
7484
  capture,
7252
- }, dbValues);
7485
+ }, dbValues, ctx.captureScope);
7253
7486
  const beforeValueCapture = beforeMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7254
7487
  target: 'db.before',
7255
7488
  rawValue: payload.before,
7256
7489
  previewValue: beforePreview,
7257
7490
  capture,
7258
- }, dbValues);
7491
+ }, dbValues, ctx.captureScope);
7259
7492
  const afterValueCapture = afterMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7260
7493
  target: 'db.after',
7261
7494
  rawValue: payload.after,
7262
7495
  previewValue: afterPreview,
7263
7496
  capture,
7264
- }, dbValues);
7497
+ }, dbValues, ctx.captureScope);
7265
7498
  const queryValueCapture = queryMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7266
7499
  target: 'db.query',
7267
7500
  rawValue: querySource,
7268
7501
  previewValue: queryPreview,
7269
7502
  capture,
7270
- }, dbValues);
7503
+ }, dbValues, ctx.captureScope);
7271
7504
  const resultMetaValueCapture = resultMetaMaterialization.skipped ? undefined : await maybeCaptureDbValueFromCandidatesAsync({
7272
7505
  target: 'db.resultMeta',
7273
7506
  rawValues: buildResultMetaFullValueCandidates(payload.resultMetaRaw, resultMetaSource),
7274
7507
  previewValue: resultMetaPreview,
7275
7508
  capture,
7276
- }, dbValues);
7509
+ }, dbValues, ctx.captureScope);
7277
7510
  const errorValueCapture = errorMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7278
7511
  target: 'db.error',
7279
7512
  rawValue: payload.error,
7280
7513
  previewValue: errorPreview,
7281
7514
  capture,
7282
- }, dbValues);
7515
+ }, dbValues, ctx.captureScope);
7283
7516
  const dbEntry = attachSpanContext({
7284
7517
  type: payload.type ?? 'mongo',
7285
7518
  collection: payload.collection,
@@ -7884,6 +8117,7 @@ function recordKafkaTraceEvent(
7884
8117
  trace: candidate,
7885
8118
  masking,
7886
8119
  privacy,
8120
+ valueNamespace: getCtx().sid,
7887
8121
  };
7888
8122
  const traceValueMetadata = {
7889
8123
  fn: evt.fn,
@@ -7976,6 +8210,8 @@ function recordKafkaTraceEvent(
7976
8210
  function buildPendingKafkaTraceEvent(raw: any): PendingTraceEventRecord | null {
7977
8211
  if (!raw || (raw.type !== 'enter' && raw.type !== 'exit')) return null;
7978
8212
 
8213
+ raw = snapshotRawTraceEventAtObservation(raw, getCtx().captureScope);
8214
+
7979
8215
  const sourceFileForLibrary =
7980
8216
  typeof raw.sourceFile === 'string' && raw.sourceFile
7981
8217
  ? String(raw.sourceFile)
@@ -8052,6 +8288,7 @@ async function recordKafkaTraceEventAsync(
8052
8288
  maskReq,
8053
8289
  masking,
8054
8290
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8291
+ valueNamespace: getCtx().sid,
8055
8292
  });
8056
8293
  sink.push(...materializedEvents);
8057
8294
  }
@@ -8072,10 +8309,11 @@ function buildKafkaTraceEntries(
8072
8309
  requestRid,
8073
8310
  actionId,
8074
8311
  );
8312
+ const emittedValueIds = new Set<string>();
8075
8313
  return batches.map((batch, index) => ({
8076
8314
  actionId: actionId ?? null,
8077
8315
  trace: batch,
8078
- traceValues: collectBatchTraceValueEntries(batch, index),
8316
+ traceValues: collectBatchTraceValueEntries(batch, index, emittedValueIds),
8079
8317
  traceBatch: {
8080
8318
  rid: requestRid,
8081
8319
  index,
@@ -8117,6 +8355,7 @@ function scheduleKafkaTraceFlush(
8117
8355
  masking,
8118
8356
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8119
8357
  captureScope,
8358
+ valueNamespace: sid,
8120
8359
  });
8121
8360
  entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
8122
8361
  }
@@ -8453,7 +8692,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
8453
8692
  try {
8454
8693
  unsubscribe = __TRACER__.tracer.on((event: any) => {
8455
8694
  if (!event || event.traceId !== traceId) return;
8456
- rawTraceEvents.push(event);
8695
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
8457
8696
  });
8458
8697
  } catch {}
8459
8698
  }
@@ -8563,7 +8802,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
8563
8802
  try {
8564
8803
  unsubscribe = __TRACER__.tracer.on((event: any) => {
8565
8804
  if (!event || event.traceId !== traceId) return;
8566
- rawTraceEvents.push(event);
8805
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
8567
8806
  });
8568
8807
  } catch {}
8569
8808
  }
@@ -9371,7 +9610,7 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
9371
9610
  try {
9372
9611
  unsubscribe = __TRACER__.tracer.on((event: any) => {
9373
9612
  if (!event || event.traceId !== traceId) return;
9374
- rawTraceEvents.push(event);
9613
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
9375
9614
  });
9376
9615
  } catch {}
9377
9616
  };
@@ -11773,5 +12012,6 @@ export const __reproTestHooks = {
11773
12012
  estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
11774
12013
  sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
11775
12014
  sanitizeMaterializedTraceValueForTest: sanitizeMaterializedTraceValue,
12015
+ projectSemanticInfrastructureValueForTest: projectSemanticInfrastructureValue,
11776
12016
  getEventTraceValueEntriesForTest: getEventTraceValueEntries,
11777
12017
  };