@reproapp/node-sdk 0.0.47 → 0.0.48

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,8 +12,8 @@
12
12
  "build": "tsc -p tsconfig.json",
13
13
  "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
14
14
  "prepublishOnly": "npm run build",
15
- "test": "npm run build && node test/trace-source-exclude.test.js && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/backend-live-mode.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/sdk-background-timeout-isolation.test.js && node test/runtime-service-registration.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-high-priority-fairness.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-batching.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-service-scope.test.js && node test/privacy-policy-index.test.js && node test/policy-capture-attestation.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js && node test/postgresql-integration.test.js",
16
- "test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js && node test/mongodb-mongoose-native-suppression.testcontainers.test.js && node test/policy-session-completion.testcontainers.test.js && node test/postgresql-integrations.testcontainers.test.js"
15
+ "test": "npm run build && node test/semantic-serialization.test.js && node test/semantic-serialization-trace.test.js && node test/content-addressed-trace-values.test.js && node test/session-storage-budget.test.js && node test/trace-source-exclude.test.js && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/backend-live-mode.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/sdk-background-timeout-isolation.test.js && node test/runtime-service-registration.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-high-priority-fairness.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-batching.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-service-scope.test.js && node test/privacy-policy-index.test.js && node test/policy-capture-attestation.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js && node test/postgresql-integration.test.js",
16
+ "test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js && node test/mongodb-mongoose-native-suppression.testcontainers.test.js && node test/content-addressed-mongodb.testcontainers.test.js && node test/policy-session-completion.testcontainers.test.js && node test/postgresql-integrations.testcontainers.test.js"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "express": "^5.1.0",
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 { 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,7 @@ type RequestCaptureScopeState = {
1224
1227
  privacyMode: 'policy' | 'fallback' | 'raw';
1225
1228
  privacyPolicyFingerprint?: string;
1226
1229
  privacyStatementCount: number;
1230
+ emittedValueDefinitionIds: Set<string>;
1227
1231
  };
1228
1232
 
1229
1233
  function createRequestCaptureScope(
@@ -1251,6 +1255,7 @@ function createRequestCaptureScope(
1251
1255
  privacyMode,
1252
1256
  privacyPolicyFingerprint: privacy?.fingerprint,
1253
1257
  privacyStatementCount: privacy?.statements.length ?? 0,
1258
+ emittedValueDefinitionIds: new Set(),
1254
1259
  };
1255
1260
  }
1256
1261
 
@@ -2828,7 +2833,11 @@ const TRACE_MAX_EVENTS_PER_REQUEST = (() => {
2828
2833
  const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
2829
2834
  const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
2830
2835
  if (Number.isFinite(env) && env > 0) return Math.trunc(env);
2831
- return Math.ceil(TRACE_MAX_EVENTS_PER_REQUEST / TRACE_BATCH_SIZE);
2836
+ // Byte-based splitting can legitimately produce one batch per event when
2837
+ // every event owns a distinct large value. The event limit remains the
2838
+ // hard upper bound; deriving this from the count-only batch size silently
2839
+ // dropped otherwise valid events after ten batches.
2840
+ return TRACE_MAX_EVENTS_PER_REQUEST;
2832
2841
  })();
2833
2842
  const TRACE_FLUSH_DELAY_MS = 20;
2834
2843
  // Choose how to order trace events in payloads.
@@ -3180,6 +3189,17 @@ function sanitizeTraceValue(
3180
3189
  const mongoPlaceholder = describeMongoPlaceholder(value);
3181
3190
  if (mongoPlaceholder) return mongoPlaceholder;
3182
3191
 
3192
+ const semanticProjection = projectSemanticInfrastructureValue(value);
3193
+ if (semanticProjection) {
3194
+ return sanitizeTraceValue(
3195
+ semanticProjection.value,
3196
+ depth,
3197
+ seen,
3198
+ options,
3199
+ valuePath,
3200
+ );
3201
+ }
3202
+
3183
3203
  if (isThenable(value)) {
3184
3204
  return { __type: 'Promise', state: 'pending' };
3185
3205
  }
@@ -3403,6 +3423,7 @@ async function materializePendingTraceEvents(
3403
3423
  masking: NormalizedMaskingConfig | null;
3404
3424
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3405
3425
  captureScope?: RequestCaptureScopeState | null;
3426
+ valueNamespace?: string;
3406
3427
  },
3407
3428
  ): Promise<TraceEventRecord[]> {
3408
3429
  for (const evt of events) {
@@ -3420,6 +3441,7 @@ async function materializePendingTraceEvents(
3420
3441
  trace: candidate,
3421
3442
  masking: params.masking,
3422
3443
  privacy: activePrivacy,
3444
+ valueNamespace: params.valueNamespace,
3423
3445
  };
3424
3446
  const traceValueMetadata = {
3425
3447
  fn: evt.fn,
@@ -3556,6 +3578,7 @@ async function materializePendingTraceEventsAsync(
3556
3578
  masking: NormalizedMaskingConfig | null;
3557
3579
  resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
3558
3580
  captureScope?: RequestCaptureScopeState | null;
3581
+ valueNamespace?: string;
3559
3582
  },
3560
3583
  ): Promise<TraceEventRecord[]> {
3561
3584
  if (!events.some((evt) => evt.__reproPending)) {
@@ -3583,6 +3606,7 @@ async function materializePendingTraceEventsAsync(
3583
3606
  return await materializePendingTraceEventsOffThread(events, {
3584
3607
  events,
3585
3608
  cfg: {
3609
+ tenantId: params.cfg.tenantId,
3586
3610
  appId: params.cfg.appId,
3587
3611
  appSecret: params.cfg.appSecret,
3588
3612
  appName: params.cfg.appName,
@@ -3592,6 +3616,7 @@ async function materializePendingTraceEventsAsync(
3592
3616
  masking: params.masking,
3593
3617
  privacy,
3594
3618
  fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
3619
+ valueNamespace: params.valueNamespace,
3595
3620
  });
3596
3621
  } catch (err) {
3597
3622
  try {
@@ -3627,6 +3652,7 @@ export async function __materializePendingTraceEventsForWorker(
3627
3652
  maskReq: payload.maskReq,
3628
3653
  masking: payload.masking,
3629
3654
  resolvePrivacy: () => payload.privacy,
3655
+ valueNamespace: payload.valueNamespace,
3630
3656
  });
3631
3657
  return materialized.map((event) => {
3632
3658
  const entries = getEventTraceValueEntries(event);
@@ -4435,6 +4461,7 @@ type FullValueCaptureContext = {
4435
4461
  masking: NormalizedMaskingConfig | null;
4436
4462
  privacy: NormalizedRuntimePrivacyPolicy | null;
4437
4463
  db?: RuntimePrivacyDbContext | null;
4464
+ valueNamespace?: string;
4438
4465
  };
4439
4466
 
4440
4467
  const TRACE_FULL_VALUE_ENTRY_SYMBOL = Symbol('repro.traceFullValueEntries');
@@ -4840,6 +4867,18 @@ function cloneFullValueCandidate(
4840
4867
  const mongoPlaceholder = describeMongoPlaceholder(value);
4841
4868
  if (mongoPlaceholder) return undefined;
4842
4869
 
4870
+ const semanticProjection = projectSemanticInfrastructureValue(value);
4871
+ if (semanticProjection) {
4872
+ return cloneFullValueCandidate(
4873
+ semanticProjection.value,
4874
+ ctx,
4875
+ limits,
4876
+ depth,
4877
+ seen,
4878
+ valuePath,
4879
+ );
4880
+ }
4881
+
4843
4882
  if (isThenable(value)) return undefined;
4844
4883
  if (shouldSkipFullValueObject(value)) return undefined;
4845
4884
 
@@ -5067,6 +5106,18 @@ async function cloneFullValueCandidateAsync(
5067
5106
  const mongoPlaceholder = describeMongoPlaceholder(value);
5068
5107
  if (mongoPlaceholder) return undefined;
5069
5108
 
5109
+ const semanticProjection = projectSemanticInfrastructureValue(value);
5110
+ if (semanticProjection) {
5111
+ return cloneFullValueCandidateAsync(
5112
+ semanticProjection.value,
5113
+ ctx,
5114
+ limits,
5115
+ depth,
5116
+ seen,
5117
+ valuePath,
5118
+ );
5119
+ }
5120
+
5070
5121
  if (isThenable(value)) return undefined;
5071
5122
  if (shouldSkipFullValueObject(value)) return undefined;
5072
5123
 
@@ -5213,12 +5264,18 @@ function getEventTraceValueEntries(event: TraceEventRecord | PendingTraceEventRe
5213
5264
  return undefined;
5214
5265
  }
5215
5266
 
5216
- function collectBatchTraceValueEntries(batch: TraceEventRecord[], batchIndex: number): TraceValueBatchEntry[] {
5267
+ function collectBatchTraceValueEntries(
5268
+ batch: TraceEventRecord[],
5269
+ batchIndex: number,
5270
+ emittedValueIds: Set<string> = new Set(),
5271
+ ): TraceValueBatchEntry[] {
5217
5272
  const collected: TraceValueBatchEntry[] = [];
5218
5273
  batch.forEach((event, eventIndex) => {
5219
5274
  const entries = getEventTraceValueEntries(event);
5220
5275
  if (!Array.isArray(entries) || !entries.length) return;
5221
5276
  entries.forEach((entry) => {
5277
+ if (emittedValueIds.has(entry.id)) return;
5278
+ emittedValueIds.add(entry.id);
5222
5279
  collected.push({
5223
5280
  ...entry,
5224
5281
  batchIndex,
@@ -5237,13 +5294,44 @@ function serializedByteLength(value: any): number {
5237
5294
  }
5238
5295
  }
5239
5296
 
5297
+ function contentAddressedValueId(
5298
+ target: TraceValueBatchEntry['target'],
5299
+ candidateSerialized: string,
5300
+ capture: FullValueCaptureContext,
5301
+ ): string {
5302
+ const config = capture.runtimeConfig;
5303
+ const signingKey = String(
5304
+ config.appSecret ||
5305
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
5306
+ );
5307
+ const namespace = String(
5308
+ capture.valueNamespace ||
5309
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
5310
+ );
5311
+ const digest = createHmac('sha256', signingKey)
5312
+ .update('repro-captured-value-v1\0')
5313
+ .update(namespace)
5314
+ .update('\0')
5315
+ .update(target)
5316
+ .update('\0')
5317
+ .update(candidateSerialized)
5318
+ .digest('hex')
5319
+ .slice(0, 40);
5320
+ return `rv1_${digest}`;
5321
+ }
5322
+
5240
5323
  function estimateTraceBatchSerializedBytes(
5241
5324
  batch: TraceEventRecord[],
5242
5325
  requestRid: string,
5243
5326
  actionId: string | null | undefined,
5244
5327
  batchIndex: number,
5328
+ previouslyEmittedValueIds: ReadonlySet<string> = new Set(),
5245
5329
  ): number {
5246
- const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
5330
+ const traceValues = collectBatchTraceValueEntries(
5331
+ batch,
5332
+ batchIndex,
5333
+ new Set(previouslyEmittedValueIds),
5334
+ );
5247
5335
  return serializedByteLength({
5248
5336
  actionId: actionId ?? null,
5249
5337
  trace: batch,
@@ -5267,15 +5355,26 @@ function chunkTraceEventsForTransport(
5267
5355
  const boundedEvents = boundTraceEventsForTransport(events);
5268
5356
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
5269
5357
  const sizedBatches: TraceEventRecord[][] = [];
5358
+ const emittedValueIds = new Set<string>();
5270
5359
  let current: TraceEventRecord[] = [];
5271
5360
 
5361
+ const commitCurrent = (): void => {
5362
+ if (!current.length) return;
5363
+ collectBatchTraceValueEntries(
5364
+ current,
5365
+ sizedBatches.length,
5366
+ emittedValueIds,
5367
+ );
5368
+ sizedBatches.push(current);
5369
+ current = [];
5370
+ };
5371
+
5272
5372
  for (const event of countBatches.flat()) {
5273
5373
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5274
5374
  break;
5275
5375
  }
5276
5376
  if (current.length >= TRACE_BATCH_SIZE) {
5277
- sizedBatches.push(current);
5278
- current = [];
5377
+ commitCurrent();
5279
5378
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5280
5379
  break;
5281
5380
  }
@@ -5287,15 +5386,15 @@ function chunkTraceEventsForTransport(
5287
5386
  requestRid,
5288
5387
  actionId,
5289
5388
  batchIndex,
5389
+ emittedValueIds,
5290
5390
  );
5291
5391
 
5292
5392
  if (
5293
5393
  current.length > 0 &&
5294
5394
  estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES
5295
5395
  ) {
5296
- sizedBatches.push(current);
5396
+ commitCurrent();
5297
5397
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
5298
- current = [];
5299
5398
  break;
5300
5399
  }
5301
5400
  current = [event];
@@ -5309,7 +5408,7 @@ function chunkTraceEventsForTransport(
5309
5408
  current.length > 0 &&
5310
5409
  sizedBatches.length < TRACE_MAX_BATCHES_PER_REQUEST
5311
5410
  ) {
5312
- sizedBatches.push(current);
5411
+ commitCurrent();
5313
5412
  }
5314
5413
 
5315
5414
  return sizedBatches;
@@ -5381,7 +5480,11 @@ function createCapturedValueEntry(
5381
5480
  return undefined;
5382
5481
  }
5383
5482
 
5384
- const valueId = randomUUID();
5483
+ const valueId = contentAddressedValueId(
5484
+ params.target,
5485
+ candidateSerialized,
5486
+ params.capture,
5487
+ );
5385
5488
  const projectedKind =
5386
5489
  candidate && typeof candidate === 'object'
5387
5490
  ? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
@@ -5463,7 +5566,11 @@ async function createCapturedValueEntryAsync(
5463
5566
  return undefined;
5464
5567
  }
5465
5568
 
5466
- const valueId = randomUUID();
5569
+ const valueId = contentAddressedValueId(
5570
+ params.target,
5571
+ candidateSerialized,
5572
+ params.capture,
5573
+ );
5467
5574
  const projectedKind =
5468
5575
  candidate && typeof candidate === 'object'
5469
5576
  ? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
@@ -5486,6 +5593,18 @@ async function createCapturedValueEntryAsync(
5486
5593
  };
5487
5594
  }
5488
5595
 
5596
+ function appendCapturedValueDefinition(
5597
+ sink: TraceValueBatchEntry[],
5598
+ entry: TraceValueBatchEntry | undefined,
5599
+ captureScope?: RequestCaptureScopeState | null,
5600
+ ): void {
5601
+ if (!entry) return;
5602
+ const emitted = captureScope?.emittedValueDefinitionIds;
5603
+ if (emitted?.has(entry.id)) return;
5604
+ emitted?.add(entry.id);
5605
+ sink.push(entry);
5606
+ }
5607
+
5489
5608
  function maybeCaptureRequestValue(
5490
5609
  params: {
5491
5610
  target: 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body';
@@ -5497,9 +5616,7 @@ function maybeCaptureRequestValue(
5497
5616
  ): TraceValueCaptureRef | undefined {
5498
5617
  const result = createCapturedValueEntry(params);
5499
5618
  if (!result) return undefined;
5500
- if (result.entry) {
5501
- sink.push(result.entry);
5502
- }
5619
+ appendCapturedValueDefinition(sink, result.entry);
5503
5620
  return result.captureRef;
5504
5621
  }
5505
5622
 
@@ -5526,9 +5643,7 @@ async function maybeCaptureRequestValueAsync(
5526
5643
  return { status: 'failed-policy' };
5527
5644
  }
5528
5645
  if (!result) return undefined;
5529
- if (result.entry) {
5530
- sink.push(result.entry);
5531
- }
5646
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5532
5647
  return result.captureRef;
5533
5648
  }
5534
5649
 
@@ -5578,9 +5693,7 @@ function maybeCaptureDbValue(
5578
5693
  ): TraceValueCaptureRef | undefined {
5579
5694
  const result = createCapturedValueEntry(params);
5580
5695
  if (!result) return undefined;
5581
- if (result.entry) {
5582
- sink.push(result.entry);
5583
- }
5696
+ appendCapturedValueDefinition(sink, result.entry);
5584
5697
  return result.captureRef;
5585
5698
  }
5586
5699
 
@@ -5607,9 +5720,7 @@ async function maybeCaptureDbValueAsync(
5607
5720
  return { status: 'failed-policy' };
5608
5721
  }
5609
5722
  if (!result) return undefined;
5610
- if (result.entry) {
5611
- sink.push(result.entry);
5612
- }
5723
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5613
5724
  return result.captureRef;
5614
5725
  }
5615
5726
 
@@ -5664,7 +5775,7 @@ async function maybeCaptureDbValueFromCandidatesAsync(
5664
5775
  }
5665
5776
  if (!result) continue;
5666
5777
  if (result.entry) {
5667
- sink.push(result.entry);
5778
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
5668
5779
  return result.captureRef;
5669
5780
  }
5670
5781
  if (!skippedRef) {
@@ -6240,6 +6351,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6240
6351
  trace: endpointTraceCtx,
6241
6352
  masking,
6242
6353
  privacy: activePrivacy,
6354
+ valueNamespace: sid,
6243
6355
  },
6244
6356
  }, requestValueEntries, captureScope);
6245
6357
  const paramsValueCapture = await maybeCaptureRequestValueAsync({
@@ -6253,6 +6365,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6253
6365
  trace: endpointTraceCtx,
6254
6366
  masking,
6255
6367
  privacy: activePrivacy,
6368
+ valueNamespace: sid,
6256
6369
  },
6257
6370
  }, requestValueEntries, captureScope);
6258
6371
  const queryValueCapture = await maybeCaptureRequestValueAsync({
@@ -6266,6 +6379,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6266
6379
  trace: endpointTraceCtx,
6267
6380
  masking,
6268
6381
  privacy: activePrivacy,
6382
+ valueNamespace: sid,
6269
6383
  },
6270
6384
  }, requestValueEntries, captureScope);
6271
6385
  const headersValueCapture = await maybeCaptureRequestValueAsync({
@@ -6279,6 +6393,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6279
6393
  trace: endpointTraceCtx,
6280
6394
  masking,
6281
6395
  privacy: activePrivacy,
6396
+ valueNamespace: sid,
6282
6397
  },
6283
6398
  }, requestValueEntries, captureScope);
6284
6399
  const respBodyValueCapture = capturedBody === undefined
@@ -6294,6 +6409,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6294
6409
  trace: endpointTraceCtx,
6295
6410
  masking,
6296
6411
  privacy: activePrivacy,
6412
+ valueNamespace: sid,
6297
6413
  },
6298
6414
  }, requestValueEntries, captureScope);
6299
6415
 
@@ -6302,7 +6418,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6302
6418
  ? Math.max(0, finishedAt - t0)
6303
6419
  : Date.now() - t0;
6304
6420
  const requestPayload: Record<string, any> = {
6305
- rid,
6421
+ rid,
6306
6422
  method: req.method,
6307
6423
  url,
6308
6424
  path,
@@ -6516,6 +6632,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6516
6632
  masking,
6517
6633
  resolvePrivacy,
6518
6634
  captureScope,
6635
+ valueNamespace: sid,
6519
6636
  });
6520
6637
  const baseEvents = balanceTraceEvents(materializedEvents.slice());
6521
6638
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
@@ -6550,9 +6667,14 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
6550
6667
  emitRequestCaptureManifest(traceBatches.length);
6551
6668
 
6552
6669
  try {
6670
+ const emittedValueIds = new Set<string>();
6553
6671
  for (let i = 0; i < traceBatches.length; i++) {
6554
6672
  const batch = traceBatches[i];
6555
- const traceValues = collectBatchTraceValueEntries(batch, i);
6673
+ const traceValues = collectBatchTraceValueEntries(
6674
+ batch,
6675
+ i,
6676
+ emittedValueIds,
6677
+ );
6556
6678
 
6557
6679
  post(captureCfg, sid, {
6558
6680
  entries: [{
@@ -7162,12 +7284,6 @@ function dehydrateComplexValue(value: any) {
7162
7284
  }
7163
7285
  } catch {}
7164
7286
 
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
7287
  return value;
7172
7288
  }
7173
7289
 
@@ -7242,6 +7358,7 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
7242
7358
  masking: null,
7243
7359
  privacy,
7244
7360
  db: dbContext,
7361
+ valueNamespace: sid,
7245
7362
  };
7246
7363
  const dbValues: TraceValueBatchEntry[] = [];
7247
7364
  const pkValueCapture = pkMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
@@ -7249,37 +7366,37 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
7249
7366
  rawValue: payload.pk,
7250
7367
  previewValue: pkPreview,
7251
7368
  capture,
7252
- }, dbValues);
7369
+ }, dbValues, ctx.captureScope);
7253
7370
  const beforeValueCapture = beforeMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7254
7371
  target: 'db.before',
7255
7372
  rawValue: payload.before,
7256
7373
  previewValue: beforePreview,
7257
7374
  capture,
7258
- }, dbValues);
7375
+ }, dbValues, ctx.captureScope);
7259
7376
  const afterValueCapture = afterMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7260
7377
  target: 'db.after',
7261
7378
  rawValue: payload.after,
7262
7379
  previewValue: afterPreview,
7263
7380
  capture,
7264
- }, dbValues);
7381
+ }, dbValues, ctx.captureScope);
7265
7382
  const queryValueCapture = queryMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7266
7383
  target: 'db.query',
7267
7384
  rawValue: querySource,
7268
7385
  previewValue: queryPreview,
7269
7386
  capture,
7270
- }, dbValues);
7387
+ }, dbValues, ctx.captureScope);
7271
7388
  const resultMetaValueCapture = resultMetaMaterialization.skipped ? undefined : await maybeCaptureDbValueFromCandidatesAsync({
7272
7389
  target: 'db.resultMeta',
7273
7390
  rawValues: buildResultMetaFullValueCandidates(payload.resultMetaRaw, resultMetaSource),
7274
7391
  previewValue: resultMetaPreview,
7275
7392
  capture,
7276
- }, dbValues);
7393
+ }, dbValues, ctx.captureScope);
7277
7394
  const errorValueCapture = errorMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
7278
7395
  target: 'db.error',
7279
7396
  rawValue: payload.error,
7280
7397
  previewValue: errorPreview,
7281
7398
  capture,
7282
- }, dbValues);
7399
+ }, dbValues, ctx.captureScope);
7283
7400
  const dbEntry = attachSpanContext({
7284
7401
  type: payload.type ?? 'mongo',
7285
7402
  collection: payload.collection,
@@ -7884,6 +8001,7 @@ function recordKafkaTraceEvent(
7884
8001
  trace: candidate,
7885
8002
  masking,
7886
8003
  privacy,
8004
+ valueNamespace: getCtx().sid,
7887
8005
  };
7888
8006
  const traceValueMetadata = {
7889
8007
  fn: evt.fn,
@@ -8052,6 +8170,7 @@ async function recordKafkaTraceEventAsync(
8052
8170
  maskReq,
8053
8171
  masking,
8054
8172
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8173
+ valueNamespace: getCtx().sid,
8055
8174
  });
8056
8175
  sink.push(...materializedEvents);
8057
8176
  }
@@ -8072,10 +8191,11 @@ function buildKafkaTraceEntries(
8072
8191
  requestRid,
8073
8192
  actionId,
8074
8193
  );
8194
+ const emittedValueIds = new Set<string>();
8075
8195
  return batches.map((batch, index) => ({
8076
8196
  actionId: actionId ?? null,
8077
8197
  trace: batch,
8078
- traceValues: collectBatchTraceValueEntries(batch, index),
8198
+ traceValues: collectBatchTraceValueEntries(batch, index, emittedValueIds),
8079
8199
  traceBatch: {
8080
8200
  rid: requestRid,
8081
8201
  index,
@@ -8117,6 +8237,7 @@ function scheduleKafkaTraceFlush(
8117
8237
  masking,
8118
8238
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
8119
8239
  captureScope,
8240
+ valueNamespace: sid,
8120
8241
  });
8121
8242
  entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
8122
8243
  }
@@ -11773,5 +11894,6 @@ export const __reproTestHooks = {
11773
11894
  estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
11774
11895
  sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
11775
11896
  sanitizeMaterializedTraceValueForTest: sanitizeMaterializedTraceValue,
11897
+ projectSemanticInfrastructureValueForTest: projectSemanticInfrastructureValue,
11776
11898
  getEventTraceValueEntriesForTest: getEventTraceValueEntries,
11777
11899
  };