@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/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ import type { Request, Response, NextFunction } from 'express';
27
27
  import type { Schema } from 'mongoose';
28
28
  import { type IngestClientConfig } from './ingest/client';
29
29
  import { type NormalizedRuntimePrivacyPolicy, type RuntimePrivacyConfig, type RuntimePrivacyDbContext, type RuntimePrivacySurface } from './privacy';
30
+ import { projectSemanticInfrastructureValue } from './semantic-serialization';
30
31
  type SpanContext = {
31
32
  traceId: string | null;
32
33
  spanId: string | number | null;
@@ -106,6 +107,7 @@ type TraceValueBatchEntry = {
106
107
  eventIndex?: number;
107
108
  };
108
109
  type TraceMaterializationConfig = {
110
+ tenantId: string;
109
111
  appId: string;
110
112
  appSecret?: string;
111
113
  appName?: string;
@@ -118,6 +120,7 @@ type TraceMaterializationWorkerPayload = {
118
120
  masking: NormalizedMaskingConfig | null;
119
121
  privacy: NormalizedRuntimePrivacyPolicy | null;
120
122
  fullValueCaptureEnabled: boolean;
123
+ valueNamespace?: string;
121
124
  };
122
125
  export type HeaderRule = string | RegExp;
123
126
  export type HeaderCaptureOptions = {
@@ -295,6 +298,8 @@ type RequestCaptureScopeState = {
295
298
  privacyMode: 'policy' | 'fallback' | 'raw';
296
299
  privacyPolicyFingerprint?: string;
297
300
  privacyStatementCount: number;
301
+ emittedValueDefinitionIds: Set<string>;
302
+ traceObservationSnapshots: Map<string, any>;
298
303
  };
299
304
  type BackendLiveTimingConfig = {
300
305
  sessionMaxDurationMs: number;
@@ -334,7 +339,7 @@ type InlinePrivacyMaterializationResult = {
334
339
  };
335
340
  declare function materializeInlinePrivacyValueAsync(target: RuntimePrivacySurface, value: any, cfg: ReproMiddlewareConfig, req: MaskRequestContext, trace: TraceEventForFilter | null, masking: NormalizedMaskingConfig | null, privacy: NormalizedRuntimePrivacyPolicy | null, db?: RuntimePrivacyDbContext | null, captureScope?: RequestCaptureScopeState | null): Promise<InlinePrivacyMaterializationResult>;
336
341
  declare function getEventTraceValueEntries(event: TraceEventRecord | PendingTraceEventRecord): TraceValueBatchEntry[] | undefined;
337
- declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number): number;
342
+ declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number, previouslyEmittedValueIds?: ReadonlySet<string>): number;
338
343
  declare function chunkTraceEventsForTransport(events: TraceEventRecord[], requestRid: string, actionId: string | null | undefined): TraceEventRecord[][];
339
344
  export type ReproMiddlewareConfig = IngestClientConfig & {
340
345
  /** API base used for backend-owned live session documents. Defaults to REPRO_API_BASE_URL/REPRO_API_BASE/privacy.apiBase. */
@@ -491,6 +496,7 @@ export declare const __reproTestHooks: {
491
496
  estimateTraceBatchSerializedBytesForTest: typeof estimateTraceBatchSerializedBytes;
492
497
  sanitizeTraceValueForPrivacyForTest: typeof sanitizeTraceValueForPrivacy;
493
498
  sanitizeMaterializedTraceValueForTest: typeof sanitizeMaterializedTraceValue;
499
+ projectSemanticInfrastructureValueForTest: typeof projectSemanticInfrastructureValue;
494
500
  getEventTraceValueEntriesForTest: typeof getEventTraceValueEntries;
495
501
  };
496
502
  export {};
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ const path_1 = require("path");
9
9
  const url_1 = require("url");
10
10
  const client_1 = require("./ingest/client");
11
11
  const privacy_1 = require("./privacy");
12
+ const semantic_serialization_1 = require("./semantic-serialization");
12
13
  /**
13
14
  * ============================= IMPORTANT =============================
14
15
  * We snapshot Mongoose core methods BEFORE tracer init, then re-apply them
@@ -837,6 +838,8 @@ function createRequestCaptureScope(requestRid, transport, cfg) {
837
838
  privacyMode,
838
839
  privacyPolicyFingerprint: privacy?.fingerprint,
839
840
  privacyStatementCount: privacy?.statements.length ?? 0,
841
+ emittedValueDefinitionIds: new Set(),
842
+ traceObservationSnapshots: new Map(),
840
843
  };
841
844
  }
842
845
  function recordRequestCaptureFailure(scope, stage, target, reason) {
@@ -2275,7 +2278,11 @@ const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
2275
2278
  const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
2276
2279
  if (Number.isFinite(env) && env > 0)
2277
2280
  return Math.trunc(env);
2278
- return Math.ceil(TRACE_MAX_EVENTS_PER_REQUEST / TRACE_BATCH_SIZE);
2281
+ // Byte-based splitting can legitimately produce one batch per event when
2282
+ // every event owns a distinct large value. The event limit remains the
2283
+ // hard upper bound; deriving this from the count-only batch size silently
2284
+ // dropped otherwise valid events after ten batches.
2285
+ return TRACE_MAX_EVENTS_PER_REQUEST;
2279
2286
  })();
2280
2287
  const TRACE_FLUSH_DELAY_MS = 20;
2281
2288
  // Choose how to order trace events in payloads.
@@ -2603,6 +2610,10 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
2603
2610
  const mongoPlaceholder = describeMongoPlaceholder(value);
2604
2611
  if (mongoPlaceholder)
2605
2612
  return mongoPlaceholder;
2613
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
2614
+ if (semanticProjection) {
2615
+ return sanitizeTraceValue(semanticProjection.value, depth, seen, options, valuePath);
2616
+ }
2606
2617
  if (isThenable(value)) {
2607
2618
  return { __type: 'Promise', state: 'pending' };
2608
2619
  }
@@ -2800,6 +2811,61 @@ function normalizeRawTraceArgs(values) {
2800
2811
  function hasMeaningfulRawTraceError(error) {
2801
2812
  return error !== undefined && error !== null;
2802
2813
  }
2814
+ const TRACE_OBSERVATION_SNAPSHOT = Symbol('repro.traceObservationSnapshot');
2815
+ // Trace events can remain queued after user code mutates the observed objects.
2816
+ // Intern immutable semantic snapshots per request so temporal evidence stays
2817
+ // correct without retaining another full copy for repeated unchanged values.
2818
+ function internTraceObservationSnapshot(snapshot, captureScope) {
2819
+ if (!captureScope || snapshot === null || typeof snapshot !== 'object') {
2820
+ return snapshot;
2821
+ }
2822
+ const serialized = stableSerializeTraceValue(snapshot);
2823
+ if (serialized === null)
2824
+ return snapshot;
2825
+ const digest = (0, crypto_1.createHash)('sha256')
2826
+ .update('repro-trace-observation-v1\0')
2827
+ .update(serialized)
2828
+ .digest('hex');
2829
+ const existing = captureScope.traceObservationSnapshots.get(digest);
2830
+ if (existing !== undefined)
2831
+ return existing;
2832
+ captureScope.traceObservationSnapshots.set(digest, snapshot);
2833
+ return snapshot;
2834
+ }
2835
+ function snapshotTraceValueAtObservation(value, captureScope, valuePath = '$', target = 'trace.value') {
2836
+ try {
2837
+ const snapshot = sanitizeTraceValue(value, 0, new WeakMap(), { preserveLongStrings: true, disableTruncation: true }, valuePath);
2838
+ return internTraceObservationSnapshot(snapshot, captureScope);
2839
+ }
2840
+ catch {
2841
+ recordRequestCaptureFailure(captureScope, 'trace.snapshot', target, 'snapshot_failed');
2842
+ return makeOmittedValue('capture-snapshot-failed');
2843
+ }
2844
+ }
2845
+ function snapshotTraceArgsAtObservation(values, captureScope) {
2846
+ const snapshot = snapshotTraceValueAtObservation(normalizeRawTraceArgs(values), captureScope, '$', 'trace.args');
2847
+ return Array.isArray(snapshot) ? snapshot : [snapshot];
2848
+ }
2849
+ function snapshotRawTraceEventAtObservation(raw, captureScope) {
2850
+ if (!raw || typeof raw !== 'object' || raw[TRACE_OBSERVATION_SNAPSHOT] === true) {
2851
+ return raw;
2852
+ }
2853
+ const snapshot = { ...raw };
2854
+ if (raw.args !== undefined) {
2855
+ snapshot.args = snapshotTraceArgsAtObservation(raw.args, captureScope);
2856
+ }
2857
+ if (raw.returnValue !== undefined) {
2858
+ snapshot.returnValue = snapshotTraceValueAtObservation(raw.returnValue, captureScope, '$', 'trace.returnValue');
2859
+ }
2860
+ if (hasMeaningfulRawTraceError(raw.error)) {
2861
+ snapshot.error = snapshotTraceValueAtObservation(raw.error, captureScope, '$', 'trace.error');
2862
+ }
2863
+ Object.defineProperty(snapshot, TRACE_OBSERVATION_SNAPSHOT, {
2864
+ value: true,
2865
+ enumerable: false,
2866
+ });
2867
+ return snapshot;
2868
+ }
2803
2869
  function attachTraceValueEntry(event, entry) {
2804
2870
  const current = event[TRACE_FULL_VALUE_ENTRY_SYMBOL];
2805
2871
  if (Array.isArray(current)) {
@@ -2824,6 +2890,7 @@ async function materializePendingTraceEvents(events, params) {
2824
2890
  trace: candidate,
2825
2891
  masking: params.masking,
2826
2892
  privacy: activePrivacy,
2893
+ valueNamespace: params.valueNamespace,
2827
2894
  };
2828
2895
  const traceValueMetadata = {
2829
2896
  fn: evt.fn,
@@ -2946,6 +3013,7 @@ async function materializePendingTraceEventsAsync(events, params) {
2946
3013
  return await materializePendingTraceEventsOffThread(events, {
2947
3014
  events,
2948
3015
  cfg: {
3016
+ tenantId: params.cfg.tenantId,
2949
3017
  appId: params.cfg.appId,
2950
3018
  appSecret: params.cfg.appSecret,
2951
3019
  appName: params.cfg.appName,
@@ -2955,6 +3023,7 @@ async function materializePendingTraceEventsAsync(events, params) {
2955
3023
  masking: params.masking,
2956
3024
  privacy,
2957
3025
  fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
3026
+ valueNamespace: params.valueNamespace,
2958
3027
  });
2959
3028
  }
2960
3029
  catch (err) {
@@ -2981,6 +3050,7 @@ async function __materializePendingTraceEventsForWorker(payload) {
2981
3050
  maskReq: payload.maskReq,
2982
3051
  masking: payload.masking,
2983
3052
  resolvePrivacy: () => payload.privacy,
3053
+ valueNamespace: payload.valueNamespace,
2984
3054
  });
2985
3055
  return materialized.map((event) => {
2986
3056
  const entries = getEventTraceValueEntries(event);
@@ -3982,6 +4052,10 @@ function cloneFullValueCandidate(value, ctx, limits, depth = 0, seen = new WeakM
3982
4052
  const mongoPlaceholder = describeMongoPlaceholder(value);
3983
4053
  if (mongoPlaceholder)
3984
4054
  return undefined;
4055
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
4056
+ if (semanticProjection) {
4057
+ return cloneFullValueCandidate(semanticProjection.value, ctx, limits, depth, seen, valuePath);
4058
+ }
3985
4059
  if (isThenable(value))
3986
4060
  return undefined;
3987
4061
  if (shouldSkipFullValueObject(value))
@@ -4166,6 +4240,10 @@ async function cloneFullValueCandidateAsync(value, ctx, limits, depth = 0, seen
4166
4240
  const mongoPlaceholder = describeMongoPlaceholder(value);
4167
4241
  if (mongoPlaceholder)
4168
4242
  return undefined;
4243
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
4244
+ if (semanticProjection) {
4245
+ return cloneFullValueCandidateAsync(semanticProjection.value, ctx, limits, depth, seen, valuePath);
4246
+ }
4169
4247
  if (isThenable(value))
4170
4248
  return undefined;
4171
4249
  if (shouldSkipFullValueObject(value))
@@ -4314,13 +4392,16 @@ function getEventTraceValueEntries(event) {
4314
4392
  }
4315
4393
  return undefined;
4316
4394
  }
4317
- function collectBatchTraceValueEntries(batch, batchIndex) {
4395
+ function collectBatchTraceValueEntries(batch, batchIndex, emittedValueIds = new Set()) {
4318
4396
  const collected = [];
4319
4397
  batch.forEach((event, eventIndex) => {
4320
4398
  const entries = getEventTraceValueEntries(event);
4321
4399
  if (!Array.isArray(entries) || !entries.length)
4322
4400
  return;
4323
4401
  entries.forEach((entry) => {
4402
+ if (emittedValueIds.has(entry.id))
4403
+ return;
4404
+ emittedValueIds.add(entry.id);
4324
4405
  collected.push({
4325
4406
  ...entry,
4326
4407
  batchIndex,
@@ -4338,8 +4419,25 @@ function serializedByteLength(value) {
4338
4419
  return Number.MAX_SAFE_INTEGER;
4339
4420
  }
4340
4421
  }
4341
- function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchIndex) {
4342
- const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
4422
+ function contentAddressedValueId(target, candidateSerialized, capture) {
4423
+ const config = capture.runtimeConfig;
4424
+ const signingKey = String(config.appSecret ||
4425
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`);
4426
+ const namespace = String(capture.valueNamespace ||
4427
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`);
4428
+ const digest = (0, crypto_1.createHmac)('sha256', signingKey)
4429
+ .update('repro-captured-value-v1\0')
4430
+ .update(namespace)
4431
+ .update('\0')
4432
+ .update(target)
4433
+ .update('\0')
4434
+ .update(candidateSerialized)
4435
+ .digest('hex')
4436
+ .slice(0, 40);
4437
+ return `rv1_${digest}`;
4438
+ }
4439
+ function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchIndex, previouslyEmittedValueIds = new Set()) {
4440
+ const traceValues = collectBatchTraceValueEntries(batch, batchIndex, new Set(previouslyEmittedValueIds));
4343
4441
  return serializedByteLength({
4344
4442
  actionId: actionId ?? null,
4345
4443
  trace: batch,
@@ -4358,26 +4456,32 @@ function chunkTraceEventsForTransport(events, requestRid, actionId) {
4358
4456
  const boundedEvents = boundTraceEventsForTransport(events);
4359
4457
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
4360
4458
  const sizedBatches = [];
4459
+ const emittedValueIds = new Set();
4361
4460
  let current = [];
4461
+ const commitCurrent = () => {
4462
+ if (!current.length)
4463
+ return;
4464
+ collectBatchTraceValueEntries(current, sizedBatches.length, emittedValueIds);
4465
+ sizedBatches.push(current);
4466
+ current = [];
4467
+ };
4362
4468
  for (const event of countBatches.flat()) {
4363
4469
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4364
4470
  break;
4365
4471
  }
4366
4472
  if (current.length >= TRACE_BATCH_SIZE) {
4367
- sizedBatches.push(current);
4368
- current = [];
4473
+ commitCurrent();
4369
4474
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4370
4475
  break;
4371
4476
  }
4372
4477
  }
4373
4478
  const candidate = current.concat(event);
4374
4479
  const batchIndex = sizedBatches.length;
4375
- const estimatedBytes = estimateTraceBatchSerializedBytes(candidate, requestRid, actionId, batchIndex);
4480
+ const estimatedBytes = estimateTraceBatchSerializedBytes(candidate, requestRid, actionId, batchIndex, emittedValueIds);
4376
4481
  if (current.length > 0 &&
4377
4482
  estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES) {
4378
- sizedBatches.push(current);
4483
+ commitCurrent();
4379
4484
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4380
- current = [];
4381
4485
  break;
4382
4486
  }
4383
4487
  current = [event];
@@ -4387,7 +4491,7 @@ function chunkTraceEventsForTransport(events, requestRid, actionId) {
4387
4491
  }
4388
4492
  if (current.length > 0 &&
4389
4493
  sizedBatches.length < TRACE_MAX_BATCHES_PER_REQUEST) {
4390
- sizedBatches.push(current);
4494
+ commitCurrent();
4391
4495
  }
4392
4496
  return sizedBatches;
4393
4497
  }
@@ -4424,7 +4528,7 @@ function createCapturedValueEntry(params) {
4424
4528
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
4425
4529
  return undefined;
4426
4530
  }
4427
- const valueId = (0, crypto_1.randomUUID)();
4531
+ const valueId = contentAddressedValueId(params.target, candidateSerialized, params.capture);
4428
4532
  const projectedKind = candidate && typeof candidate === 'object'
4429
4533
  ? (candidate.__kind || candidate.__type)
4430
4534
  : undefined;
@@ -4471,7 +4575,7 @@ async function createCapturedValueEntryAsync(params) {
4471
4575
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
4472
4576
  return undefined;
4473
4577
  }
4474
- const valueId = (0, crypto_1.randomUUID)();
4578
+ const valueId = contentAddressedValueId(params.target, candidateSerialized, params.capture);
4475
4579
  const projectedKind = candidate && typeof candidate === 'object'
4476
4580
  ? (candidate.__kind || candidate.__type)
4477
4581
  : undefined;
@@ -4492,13 +4596,20 @@ async function createCapturedValueEntryAsync(params) {
4492
4596
  entry,
4493
4597
  };
4494
4598
  }
4599
+ function appendCapturedValueDefinition(sink, entry, captureScope) {
4600
+ if (!entry)
4601
+ return;
4602
+ const emitted = captureScope?.emittedValueDefinitionIds;
4603
+ if (emitted?.has(entry.id))
4604
+ return;
4605
+ emitted?.add(entry.id);
4606
+ sink.push(entry);
4607
+ }
4495
4608
  function maybeCaptureRequestValue(params, sink) {
4496
4609
  const result = createCapturedValueEntry(params);
4497
4610
  if (!result)
4498
4611
  return undefined;
4499
- if (result.entry) {
4500
- sink.push(result.entry);
4501
- }
4612
+ appendCapturedValueDefinition(sink, result.entry);
4502
4613
  return result.captureRef;
4503
4614
  }
4504
4615
  async function maybeCaptureRequestValueAsync(params, sink, captureScope) {
@@ -4512,9 +4623,7 @@ async function maybeCaptureRequestValueAsync(params, sink, captureScope) {
4512
4623
  }
4513
4624
  if (!result)
4514
4625
  return undefined;
4515
- if (result.entry) {
4516
- sink.push(result.entry);
4517
- }
4626
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4518
4627
  return result.captureRef;
4519
4628
  }
4520
4629
  function maybeCaptureTraceValue(params) {
@@ -4533,9 +4642,7 @@ function maybeCaptureDbValue(params, sink) {
4533
4642
  const result = createCapturedValueEntry(params);
4534
4643
  if (!result)
4535
4644
  return undefined;
4536
- if (result.entry) {
4537
- sink.push(result.entry);
4538
- }
4645
+ appendCapturedValueDefinition(sink, result.entry);
4539
4646
  return result.captureRef;
4540
4647
  }
4541
4648
  async function maybeCaptureDbValueAsync(params, sink, captureScope) {
@@ -4549,9 +4656,7 @@ async function maybeCaptureDbValueAsync(params, sink, captureScope) {
4549
4656
  }
4550
4657
  if (!result)
4551
4658
  return undefined;
4552
- if (result.entry) {
4553
- sink.push(result.entry);
4554
- }
4659
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4555
4660
  return result.captureRef;
4556
4661
  }
4557
4662
  function pushUniqueCaptureCandidate(candidates, value) {
@@ -4591,7 +4696,7 @@ async function maybeCaptureDbValueFromCandidatesAsync(params, sink, captureScope
4591
4696
  if (!result)
4592
4697
  continue;
4593
4698
  if (result.entry) {
4594
- sink.push(result.entry);
4699
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4595
4700
  return result.captureRef;
4596
4701
  }
4597
4702
  if (!skippedRef) {
@@ -4725,21 +4830,25 @@ function reproMiddleware(cfg) {
4725
4830
  res.on('close', endTrackedClientRequest);
4726
4831
  return next(); // only capture tagged requests
4727
4832
  }
4728
- const captureCfg = backendLiveChild
4729
- ? {
4833
+ let captureCfg = cfg;
4834
+ if (backendLiveChild) {
4835
+ captureCfg = {
4730
4836
  ...cfg,
4731
4837
  actorId: backendLiveChild.actorId,
4732
4838
  actorType: backendLiveChild.actorType,
4733
4839
  actorLabels: backendLiveChild.actorLabels,
4734
- }
4735
- : taggedActorIdentity
4736
- ? {
4737
- ...cfg,
4738
- actorId: taggedActorIdentity.actorId,
4739
- actorType: taggedActorIdentity.actorType,
4740
- actorLabels: taggedActorIdentity.actorLabels,
4741
- }
4742
- : cfg;
4840
+ };
4841
+ shareRuntimePrivacyState(cfg, captureCfg);
4842
+ }
4843
+ else if (taggedActorIdentity) {
4844
+ captureCfg = {
4845
+ ...cfg,
4846
+ actorId: taggedActorIdentity.actorId,
4847
+ actorType: taggedActorIdentity.actorType,
4848
+ actorLabels: taggedActorIdentity.actorLabels,
4849
+ };
4850
+ shareRuntimePrivacyState(cfg, captureCfg);
4851
+ }
4743
4852
  if (taggedActorIdentity) {
4744
4853
  post(captureCfg, sid, {
4745
4854
  entries: [{
@@ -4987,6 +5096,7 @@ function reproMiddleware(cfg) {
4987
5096
  trace: endpointTraceCtx,
4988
5097
  masking,
4989
5098
  privacy: activePrivacy,
5099
+ valueNamespace: sid,
4990
5100
  },
4991
5101
  }, requestValueEntries, captureScope);
4992
5102
  const paramsValueCapture = await maybeCaptureRequestValueAsync({
@@ -5000,6 +5110,7 @@ function reproMiddleware(cfg) {
5000
5110
  trace: endpointTraceCtx,
5001
5111
  masking,
5002
5112
  privacy: activePrivacy,
5113
+ valueNamespace: sid,
5003
5114
  },
5004
5115
  }, requestValueEntries, captureScope);
5005
5116
  const queryValueCapture = await maybeCaptureRequestValueAsync({
@@ -5013,6 +5124,7 @@ function reproMiddleware(cfg) {
5013
5124
  trace: endpointTraceCtx,
5014
5125
  masking,
5015
5126
  privacy: activePrivacy,
5127
+ valueNamespace: sid,
5016
5128
  },
5017
5129
  }, requestValueEntries, captureScope);
5018
5130
  const headersValueCapture = await maybeCaptureRequestValueAsync({
@@ -5026,6 +5138,7 @@ function reproMiddleware(cfg) {
5026
5138
  trace: endpointTraceCtx,
5027
5139
  masking,
5028
5140
  privacy: activePrivacy,
5141
+ valueNamespace: sid,
5029
5142
  },
5030
5143
  }, requestValueEntries, captureScope);
5031
5144
  const respBodyValueCapture = capturedBody === undefined
@@ -5041,6 +5154,7 @@ function reproMiddleware(cfg) {
5041
5154
  trace: endpointTraceCtx,
5042
5155
  masking,
5043
5156
  privacy: activePrivacy,
5157
+ valueNamespace: sid,
5044
5158
  },
5045
5159
  }, requestValueEntries, captureScope);
5046
5160
  const responseDurationMs = finishedAt !== null && Number.isFinite(finishedAt)
@@ -5208,17 +5322,17 @@ function reproMiddleware(cfg) {
5208
5322
  evt.__reproPending = {
5209
5323
  ...(ev.args !== undefined
5210
5324
  ? {
5211
- argsRaw: normalizeRawTraceArgs(ev.args),
5325
+ argsRaw: snapshotTraceArgsAtObservation(ev.args, captureScope),
5212
5326
  }
5213
5327
  : {}),
5214
5328
  ...(ev.returnValue !== undefined
5215
5329
  ? {
5216
- returnValueRaw: ev.returnValue,
5330
+ returnValueRaw: snapshotTraceValueAtObservation(ev.returnValue, captureScope, '$', 'trace.returnValue'),
5217
5331
  }
5218
5332
  : {}),
5219
5333
  ...(hasErrorPayload
5220
5334
  ? {
5221
- errorRaw: ev.error,
5335
+ errorRaw: snapshotTraceValueAtObservation(ev.error, captureScope, '$', 'trace.error'),
5222
5336
  }
5223
5337
  : {}),
5224
5338
  };
@@ -5250,6 +5364,7 @@ function reproMiddleware(cfg) {
5250
5364
  masking,
5251
5365
  resolvePrivacy,
5252
5366
  captureScope,
5367
+ valueNamespace: sid,
5253
5368
  });
5254
5369
  const baseEvents = balanceTraceEvents(materializedEvents.slice());
5255
5370
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
@@ -5273,9 +5388,10 @@ function reproMiddleware(cfg) {
5273
5388
  // on the wire so a large trace queue cannot starve completion.
5274
5389
  emitRequestCaptureManifest(traceBatches.length);
5275
5390
  try {
5391
+ const emittedValueIds = new Set();
5276
5392
  for (let i = 0; i < traceBatches.length; i++) {
5277
5393
  const batch = traceBatches[i];
5278
- const traceValues = collectBatchTraceValueEntries(batch, i);
5394
+ const traceValues = collectBatchTraceValueEntries(batch, i, emittedValueIds);
5279
5395
  post(captureCfg, sid, {
5280
5396
  entries: [{
5281
5397
  actionId: aid,
@@ -5846,12 +5962,6 @@ function dehydrateComplexValue(value) {
5846
5962
  }
5847
5963
  }
5848
5964
  catch { }
5849
- const ctor = value?.constructor?.name;
5850
- if (ctor && ctor !== 'Object') {
5851
- const plain = safeJson(value);
5852
- if (plain !== undefined)
5853
- return plain;
5854
- }
5855
5965
  return value;
5856
5966
  }
5857
5967
  function emitDbQuery(cfg, sid, aid, payload) {
@@ -5922,6 +6032,7 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
5922
6032
  masking: null,
5923
6033
  privacy,
5924
6034
  db: dbContext,
6035
+ valueNamespace: sid,
5925
6036
  };
5926
6037
  const dbValues = [];
5927
6038
  const pkValueCapture = pkMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
@@ -5929,37 +6040,37 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
5929
6040
  rawValue: payload.pk,
5930
6041
  previewValue: pkPreview,
5931
6042
  capture,
5932
- }, dbValues);
6043
+ }, dbValues, ctx.captureScope);
5933
6044
  const beforeValueCapture = beforeMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5934
6045
  target: 'db.before',
5935
6046
  rawValue: payload.before,
5936
6047
  previewValue: beforePreview,
5937
6048
  capture,
5938
- }, dbValues);
6049
+ }, dbValues, ctx.captureScope);
5939
6050
  const afterValueCapture = afterMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5940
6051
  target: 'db.after',
5941
6052
  rawValue: payload.after,
5942
6053
  previewValue: afterPreview,
5943
6054
  capture,
5944
- }, dbValues);
6055
+ }, dbValues, ctx.captureScope);
5945
6056
  const queryValueCapture = queryMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5946
6057
  target: 'db.query',
5947
6058
  rawValue: querySource,
5948
6059
  previewValue: queryPreview,
5949
6060
  capture,
5950
- }, dbValues);
6061
+ }, dbValues, ctx.captureScope);
5951
6062
  const resultMetaValueCapture = resultMetaMaterialization.skipped ? undefined : await maybeCaptureDbValueFromCandidatesAsync({
5952
6063
  target: 'db.resultMeta',
5953
6064
  rawValues: buildResultMetaFullValueCandidates(payload.resultMetaRaw, resultMetaSource),
5954
6065
  previewValue: resultMetaPreview,
5955
6066
  capture,
5956
- }, dbValues);
6067
+ }, dbValues, ctx.captureScope);
5957
6068
  const errorValueCapture = errorMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5958
6069
  target: 'db.error',
5959
6070
  rawValue: payload.error,
5960
6071
  previewValue: errorPreview,
5961
6072
  capture,
5962
- }, dbValues);
6073
+ }, dbValues, ctx.captureScope);
5963
6074
  const dbEntry = attachSpanContext({
5964
6075
  type: payload.type ?? 'mongo',
5965
6076
  collection: payload.collection,
@@ -6501,6 +6612,7 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
6501
6612
  trace: candidate,
6502
6613
  masking,
6503
6614
  privacy,
6615
+ valueNamespace: getCtx().sid,
6504
6616
  };
6505
6617
  const traceValueMetadata = {
6506
6618
  fn: evt.fn,
@@ -6569,6 +6681,7 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
6569
6681
  function buildPendingKafkaTraceEvent(raw) {
6570
6682
  if (!raw || (raw.type !== 'enter' && raw.type !== 'exit'))
6571
6683
  return null;
6684
+ raw = snapshotRawTraceEventAtObservation(raw, getCtx().captureScope);
6572
6685
  const sourceFileForLibrary = typeof raw.sourceFile === 'string' && raw.sourceFile
6573
6686
  ? String(raw.sourceFile)
6574
6687
  : null;
@@ -6638,6 +6751,7 @@ async function recordKafkaTraceEventAsync(raw, sink, cfg, maskReq) {
6638
6751
  maskReq,
6639
6752
  masking,
6640
6753
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
6754
+ valueNamespace: getCtx().sid,
6641
6755
  });
6642
6756
  sink.push(...materializedEvents);
6643
6757
  }
@@ -6649,10 +6763,11 @@ function buildKafkaTraceEntries(actionId, requestRid, events) {
6649
6763
  ? reorderTraceEvents(baseEvents)
6650
6764
  : sortTraceEventsChronologically(baseEvents);
6651
6765
  const batches = chunkTraceEventsForTransport(orderedEvents, requestRid, actionId);
6766
+ const emittedValueIds = new Set();
6652
6767
  return batches.map((batch, index) => ({
6653
6768
  actionId: actionId ?? null,
6654
6769
  trace: batch,
6655
- traceValues: collectBatchTraceValueEntries(batch, index),
6770
+ traceValues: collectBatchTraceValueEntries(batch, index, emittedValueIds),
6656
6771
  traceBatch: {
6657
6772
  rid: requestRid,
6658
6773
  index,
@@ -6686,6 +6801,7 @@ function scheduleKafkaTraceFlush(cfg, kafkaCtx, actionId, requestRid, rawTraceEv
6686
6801
  masking,
6687
6802
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
6688
6803
  captureScope,
6804
+ valueNamespace: sid,
6689
6805
  });
6690
6806
  entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
6691
6807
  }
@@ -6999,7 +7115,7 @@ function wrapKafkaEachMessageHandler(eachMessage, cfg, consumer) {
6999
7115
  unsubscribe = __TRACER__.tracer.on((event) => {
7000
7116
  if (!event || event.traceId !== traceId)
7001
7117
  return;
7002
- rawTraceEvents.push(event);
7118
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
7003
7119
  });
7004
7120
  }
7005
7121
  catch { }
@@ -7113,7 +7229,7 @@ function wrapKafkaEachBatchHandler(eachBatch, cfg, consumer) {
7113
7229
  unsubscribe = __TRACER__.tracer.on((event) => {
7114
7230
  if (!event || event.traceId !== traceId)
7115
7231
  return;
7116
- rawTraceEvents.push(event);
7232
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
7117
7233
  });
7118
7234
  }
7119
7235
  catch { }
@@ -7831,7 +7947,7 @@ function wrapAmqplibConsumeHandler(onMessage, cfg, queue) {
7831
7947
  unsubscribe = __TRACER__.tracer.on((event) => {
7832
7948
  if (!event || event.traceId !== traceId)
7833
7949
  return;
7834
- rawTraceEvents.push(event);
7950
+ rawTraceEvents.push(snapshotRawTraceEventAtObservation(event, captureScope));
7835
7951
  });
7836
7952
  }
7837
7953
  catch { }
@@ -10090,5 +10206,6 @@ exports.__reproTestHooks = {
10090
10206
  estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
10091
10207
  sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
10092
10208
  sanitizeMaterializedTraceValueForTest: sanitizeMaterializedTraceValue,
10209
+ projectSemanticInfrastructureValueForTest: semantic_serialization_1.projectSemanticInfrastructureValue,
10093
10210
  getEventTraceValueEntriesForTest: getEventTraceValueEntries,
10094
10211
  };
@@ -0,0 +1,9 @@
1
+ export type SemanticProjection = {
2
+ matched: true;
3
+ value: Record<string, unknown>;
4
+ };
5
+ /**
6
+ * Projects known runtime machinery into stable, non-recursive descriptions.
7
+ * Unknown application values deliberately return null and remain fully captured.
8
+ */
9
+ export declare function projectSemanticInfrastructureValue(value: unknown): SemanticProjection | null;