@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/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,7 @@ type RequestCaptureScopeState = {
295
298
  privacyMode: 'policy' | 'fallback' | 'raw';
296
299
  privacyPolicyFingerprint?: string;
297
300
  privacyStatementCount: number;
301
+ emittedValueDefinitionIds: Set<string>;
298
302
  };
299
303
  type BackendLiveTimingConfig = {
300
304
  sessionMaxDurationMs: number;
@@ -334,7 +338,7 @@ type InlinePrivacyMaterializationResult = {
334
338
  };
335
339
  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
340
  declare function getEventTraceValueEntries(event: TraceEventRecord | PendingTraceEventRecord): TraceValueBatchEntry[] | undefined;
337
- declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number): number;
341
+ declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number, previouslyEmittedValueIds?: ReadonlySet<string>): number;
338
342
  declare function chunkTraceEventsForTransport(events: TraceEventRecord[], requestRid: string, actionId: string | null | undefined): TraceEventRecord[][];
339
343
  export type ReproMiddlewareConfig = IngestClientConfig & {
340
344
  /** API base used for backend-owned live session documents. Defaults to REPRO_API_BASE_URL/REPRO_API_BASE/privacy.apiBase. */
@@ -491,6 +495,7 @@ export declare const __reproTestHooks: {
491
495
  estimateTraceBatchSerializedBytesForTest: typeof estimateTraceBatchSerializedBytes;
492
496
  sanitizeTraceValueForPrivacyForTest: typeof sanitizeTraceValueForPrivacy;
493
497
  sanitizeMaterializedTraceValueForTest: typeof sanitizeMaterializedTraceValue;
498
+ projectSemanticInfrastructureValueForTest: typeof projectSemanticInfrastructureValue;
494
499
  getEventTraceValueEntriesForTest: typeof getEventTraceValueEntries;
495
500
  };
496
501
  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,7 @@ function createRequestCaptureScope(requestRid, transport, cfg) {
837
838
  privacyMode,
838
839
  privacyPolicyFingerprint: privacy?.fingerprint,
839
840
  privacyStatementCount: privacy?.statements.length ?? 0,
841
+ emittedValueDefinitionIds: new Set(),
840
842
  };
841
843
  }
842
844
  function recordRequestCaptureFailure(scope, stage, target, reason) {
@@ -2275,7 +2277,11 @@ const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
2275
2277
  const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
2276
2278
  if (Number.isFinite(env) && env > 0)
2277
2279
  return Math.trunc(env);
2278
- return Math.ceil(TRACE_MAX_EVENTS_PER_REQUEST / TRACE_BATCH_SIZE);
2280
+ // Byte-based splitting can legitimately produce one batch per event when
2281
+ // every event owns a distinct large value. The event limit remains the
2282
+ // hard upper bound; deriving this from the count-only batch size silently
2283
+ // dropped otherwise valid events after ten batches.
2284
+ return TRACE_MAX_EVENTS_PER_REQUEST;
2279
2285
  })();
2280
2286
  const TRACE_FLUSH_DELAY_MS = 20;
2281
2287
  // Choose how to order trace events in payloads.
@@ -2603,6 +2609,10 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
2603
2609
  const mongoPlaceholder = describeMongoPlaceholder(value);
2604
2610
  if (mongoPlaceholder)
2605
2611
  return mongoPlaceholder;
2612
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
2613
+ if (semanticProjection) {
2614
+ return sanitizeTraceValue(semanticProjection.value, depth, seen, options, valuePath);
2615
+ }
2606
2616
  if (isThenable(value)) {
2607
2617
  return { __type: 'Promise', state: 'pending' };
2608
2618
  }
@@ -2824,6 +2834,7 @@ async function materializePendingTraceEvents(events, params) {
2824
2834
  trace: candidate,
2825
2835
  masking: params.masking,
2826
2836
  privacy: activePrivacy,
2837
+ valueNamespace: params.valueNamespace,
2827
2838
  };
2828
2839
  const traceValueMetadata = {
2829
2840
  fn: evt.fn,
@@ -2946,6 +2957,7 @@ async function materializePendingTraceEventsAsync(events, params) {
2946
2957
  return await materializePendingTraceEventsOffThread(events, {
2947
2958
  events,
2948
2959
  cfg: {
2960
+ tenantId: params.cfg.tenantId,
2949
2961
  appId: params.cfg.appId,
2950
2962
  appSecret: params.cfg.appSecret,
2951
2963
  appName: params.cfg.appName,
@@ -2955,6 +2967,7 @@ async function materializePendingTraceEventsAsync(events, params) {
2955
2967
  masking: params.masking,
2956
2968
  privacy,
2957
2969
  fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
2970
+ valueNamespace: params.valueNamespace,
2958
2971
  });
2959
2972
  }
2960
2973
  catch (err) {
@@ -2981,6 +2994,7 @@ async function __materializePendingTraceEventsForWorker(payload) {
2981
2994
  maskReq: payload.maskReq,
2982
2995
  masking: payload.masking,
2983
2996
  resolvePrivacy: () => payload.privacy,
2997
+ valueNamespace: payload.valueNamespace,
2984
2998
  });
2985
2999
  return materialized.map((event) => {
2986
3000
  const entries = getEventTraceValueEntries(event);
@@ -3982,6 +3996,10 @@ function cloneFullValueCandidate(value, ctx, limits, depth = 0, seen = new WeakM
3982
3996
  const mongoPlaceholder = describeMongoPlaceholder(value);
3983
3997
  if (mongoPlaceholder)
3984
3998
  return undefined;
3999
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
4000
+ if (semanticProjection) {
4001
+ return cloneFullValueCandidate(semanticProjection.value, ctx, limits, depth, seen, valuePath);
4002
+ }
3985
4003
  if (isThenable(value))
3986
4004
  return undefined;
3987
4005
  if (shouldSkipFullValueObject(value))
@@ -4166,6 +4184,10 @@ async function cloneFullValueCandidateAsync(value, ctx, limits, depth = 0, seen
4166
4184
  const mongoPlaceholder = describeMongoPlaceholder(value);
4167
4185
  if (mongoPlaceholder)
4168
4186
  return undefined;
4187
+ const semanticProjection = (0, semantic_serialization_1.projectSemanticInfrastructureValue)(value);
4188
+ if (semanticProjection) {
4189
+ return cloneFullValueCandidateAsync(semanticProjection.value, ctx, limits, depth, seen, valuePath);
4190
+ }
4169
4191
  if (isThenable(value))
4170
4192
  return undefined;
4171
4193
  if (shouldSkipFullValueObject(value))
@@ -4314,13 +4336,16 @@ function getEventTraceValueEntries(event) {
4314
4336
  }
4315
4337
  return undefined;
4316
4338
  }
4317
- function collectBatchTraceValueEntries(batch, batchIndex) {
4339
+ function collectBatchTraceValueEntries(batch, batchIndex, emittedValueIds = new Set()) {
4318
4340
  const collected = [];
4319
4341
  batch.forEach((event, eventIndex) => {
4320
4342
  const entries = getEventTraceValueEntries(event);
4321
4343
  if (!Array.isArray(entries) || !entries.length)
4322
4344
  return;
4323
4345
  entries.forEach((entry) => {
4346
+ if (emittedValueIds.has(entry.id))
4347
+ return;
4348
+ emittedValueIds.add(entry.id);
4324
4349
  collected.push({
4325
4350
  ...entry,
4326
4351
  batchIndex,
@@ -4338,8 +4363,25 @@ function serializedByteLength(value) {
4338
4363
  return Number.MAX_SAFE_INTEGER;
4339
4364
  }
4340
4365
  }
4341
- function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchIndex) {
4342
- const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
4366
+ function contentAddressedValueId(target, candidateSerialized, capture) {
4367
+ const config = capture.runtimeConfig;
4368
+ const signingKey = String(config.appSecret ||
4369
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`);
4370
+ const namespace = String(capture.valueNamespace ||
4371
+ `${config.tenantId || 'tenant'}:${config.appId || 'app'}`);
4372
+ const digest = (0, crypto_1.createHmac)('sha256', signingKey)
4373
+ .update('repro-captured-value-v1\0')
4374
+ .update(namespace)
4375
+ .update('\0')
4376
+ .update(target)
4377
+ .update('\0')
4378
+ .update(candidateSerialized)
4379
+ .digest('hex')
4380
+ .slice(0, 40);
4381
+ return `rv1_${digest}`;
4382
+ }
4383
+ function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchIndex, previouslyEmittedValueIds = new Set()) {
4384
+ const traceValues = collectBatchTraceValueEntries(batch, batchIndex, new Set(previouslyEmittedValueIds));
4343
4385
  return serializedByteLength({
4344
4386
  actionId: actionId ?? null,
4345
4387
  trace: batch,
@@ -4358,26 +4400,32 @@ function chunkTraceEventsForTransport(events, requestRid, actionId) {
4358
4400
  const boundedEvents = boundTraceEventsForTransport(events);
4359
4401
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
4360
4402
  const sizedBatches = [];
4403
+ const emittedValueIds = new Set();
4361
4404
  let current = [];
4405
+ const commitCurrent = () => {
4406
+ if (!current.length)
4407
+ return;
4408
+ collectBatchTraceValueEntries(current, sizedBatches.length, emittedValueIds);
4409
+ sizedBatches.push(current);
4410
+ current = [];
4411
+ };
4362
4412
  for (const event of countBatches.flat()) {
4363
4413
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4364
4414
  break;
4365
4415
  }
4366
4416
  if (current.length >= TRACE_BATCH_SIZE) {
4367
- sizedBatches.push(current);
4368
- current = [];
4417
+ commitCurrent();
4369
4418
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4370
4419
  break;
4371
4420
  }
4372
4421
  }
4373
4422
  const candidate = current.concat(event);
4374
4423
  const batchIndex = sizedBatches.length;
4375
- const estimatedBytes = estimateTraceBatchSerializedBytes(candidate, requestRid, actionId, batchIndex);
4424
+ const estimatedBytes = estimateTraceBatchSerializedBytes(candidate, requestRid, actionId, batchIndex, emittedValueIds);
4376
4425
  if (current.length > 0 &&
4377
4426
  estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES) {
4378
- sizedBatches.push(current);
4427
+ commitCurrent();
4379
4428
  if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
4380
- current = [];
4381
4429
  break;
4382
4430
  }
4383
4431
  current = [event];
@@ -4387,7 +4435,7 @@ function chunkTraceEventsForTransport(events, requestRid, actionId) {
4387
4435
  }
4388
4436
  if (current.length > 0 &&
4389
4437
  sizedBatches.length < TRACE_MAX_BATCHES_PER_REQUEST) {
4390
- sizedBatches.push(current);
4438
+ commitCurrent();
4391
4439
  }
4392
4440
  return sizedBatches;
4393
4441
  }
@@ -4424,7 +4472,7 @@ function createCapturedValueEntry(params) {
4424
4472
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
4425
4473
  return undefined;
4426
4474
  }
4427
- const valueId = (0, crypto_1.randomUUID)();
4475
+ const valueId = contentAddressedValueId(params.target, candidateSerialized, params.capture);
4428
4476
  const projectedKind = candidate && typeof candidate === 'object'
4429
4477
  ? (candidate.__kind || candidate.__type)
4430
4478
  : undefined;
@@ -4471,7 +4519,7 @@ async function createCapturedValueEntryAsync(params) {
4471
4519
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
4472
4520
  return undefined;
4473
4521
  }
4474
- const valueId = (0, crypto_1.randomUUID)();
4522
+ const valueId = contentAddressedValueId(params.target, candidateSerialized, params.capture);
4475
4523
  const projectedKind = candidate && typeof candidate === 'object'
4476
4524
  ? (candidate.__kind || candidate.__type)
4477
4525
  : undefined;
@@ -4492,13 +4540,20 @@ async function createCapturedValueEntryAsync(params) {
4492
4540
  entry,
4493
4541
  };
4494
4542
  }
4543
+ function appendCapturedValueDefinition(sink, entry, captureScope) {
4544
+ if (!entry)
4545
+ return;
4546
+ const emitted = captureScope?.emittedValueDefinitionIds;
4547
+ if (emitted?.has(entry.id))
4548
+ return;
4549
+ emitted?.add(entry.id);
4550
+ sink.push(entry);
4551
+ }
4495
4552
  function maybeCaptureRequestValue(params, sink) {
4496
4553
  const result = createCapturedValueEntry(params);
4497
4554
  if (!result)
4498
4555
  return undefined;
4499
- if (result.entry) {
4500
- sink.push(result.entry);
4501
- }
4556
+ appendCapturedValueDefinition(sink, result.entry);
4502
4557
  return result.captureRef;
4503
4558
  }
4504
4559
  async function maybeCaptureRequestValueAsync(params, sink, captureScope) {
@@ -4512,9 +4567,7 @@ async function maybeCaptureRequestValueAsync(params, sink, captureScope) {
4512
4567
  }
4513
4568
  if (!result)
4514
4569
  return undefined;
4515
- if (result.entry) {
4516
- sink.push(result.entry);
4517
- }
4570
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4518
4571
  return result.captureRef;
4519
4572
  }
4520
4573
  function maybeCaptureTraceValue(params) {
@@ -4533,9 +4586,7 @@ function maybeCaptureDbValue(params, sink) {
4533
4586
  const result = createCapturedValueEntry(params);
4534
4587
  if (!result)
4535
4588
  return undefined;
4536
- if (result.entry) {
4537
- sink.push(result.entry);
4538
- }
4589
+ appendCapturedValueDefinition(sink, result.entry);
4539
4590
  return result.captureRef;
4540
4591
  }
4541
4592
  async function maybeCaptureDbValueAsync(params, sink, captureScope) {
@@ -4549,9 +4600,7 @@ async function maybeCaptureDbValueAsync(params, sink, captureScope) {
4549
4600
  }
4550
4601
  if (!result)
4551
4602
  return undefined;
4552
- if (result.entry) {
4553
- sink.push(result.entry);
4554
- }
4603
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4555
4604
  return result.captureRef;
4556
4605
  }
4557
4606
  function pushUniqueCaptureCandidate(candidates, value) {
@@ -4591,7 +4640,7 @@ async function maybeCaptureDbValueFromCandidatesAsync(params, sink, captureScope
4591
4640
  if (!result)
4592
4641
  continue;
4593
4642
  if (result.entry) {
4594
- sink.push(result.entry);
4643
+ appendCapturedValueDefinition(sink, result.entry, captureScope);
4595
4644
  return result.captureRef;
4596
4645
  }
4597
4646
  if (!skippedRef) {
@@ -4987,6 +5036,7 @@ function reproMiddleware(cfg) {
4987
5036
  trace: endpointTraceCtx,
4988
5037
  masking,
4989
5038
  privacy: activePrivacy,
5039
+ valueNamespace: sid,
4990
5040
  },
4991
5041
  }, requestValueEntries, captureScope);
4992
5042
  const paramsValueCapture = await maybeCaptureRequestValueAsync({
@@ -5000,6 +5050,7 @@ function reproMiddleware(cfg) {
5000
5050
  trace: endpointTraceCtx,
5001
5051
  masking,
5002
5052
  privacy: activePrivacy,
5053
+ valueNamespace: sid,
5003
5054
  },
5004
5055
  }, requestValueEntries, captureScope);
5005
5056
  const queryValueCapture = await maybeCaptureRequestValueAsync({
@@ -5013,6 +5064,7 @@ function reproMiddleware(cfg) {
5013
5064
  trace: endpointTraceCtx,
5014
5065
  masking,
5015
5066
  privacy: activePrivacy,
5067
+ valueNamespace: sid,
5016
5068
  },
5017
5069
  }, requestValueEntries, captureScope);
5018
5070
  const headersValueCapture = await maybeCaptureRequestValueAsync({
@@ -5026,6 +5078,7 @@ function reproMiddleware(cfg) {
5026
5078
  trace: endpointTraceCtx,
5027
5079
  masking,
5028
5080
  privacy: activePrivacy,
5081
+ valueNamespace: sid,
5029
5082
  },
5030
5083
  }, requestValueEntries, captureScope);
5031
5084
  const respBodyValueCapture = capturedBody === undefined
@@ -5041,6 +5094,7 @@ function reproMiddleware(cfg) {
5041
5094
  trace: endpointTraceCtx,
5042
5095
  masking,
5043
5096
  privacy: activePrivacy,
5097
+ valueNamespace: sid,
5044
5098
  },
5045
5099
  }, requestValueEntries, captureScope);
5046
5100
  const responseDurationMs = finishedAt !== null && Number.isFinite(finishedAt)
@@ -5250,6 +5304,7 @@ function reproMiddleware(cfg) {
5250
5304
  masking,
5251
5305
  resolvePrivacy,
5252
5306
  captureScope,
5307
+ valueNamespace: sid,
5253
5308
  });
5254
5309
  const baseEvents = balanceTraceEvents(materializedEvents.slice());
5255
5310
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
@@ -5273,9 +5328,10 @@ function reproMiddleware(cfg) {
5273
5328
  // on the wire so a large trace queue cannot starve completion.
5274
5329
  emitRequestCaptureManifest(traceBatches.length);
5275
5330
  try {
5331
+ const emittedValueIds = new Set();
5276
5332
  for (let i = 0; i < traceBatches.length; i++) {
5277
5333
  const batch = traceBatches[i];
5278
- const traceValues = collectBatchTraceValueEntries(batch, i);
5334
+ const traceValues = collectBatchTraceValueEntries(batch, i, emittedValueIds);
5279
5335
  post(captureCfg, sid, {
5280
5336
  entries: [{
5281
5337
  actionId: aid,
@@ -5846,12 +5902,6 @@ function dehydrateComplexValue(value) {
5846
5902
  }
5847
5903
  }
5848
5904
  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
5905
  return value;
5856
5906
  }
5857
5907
  function emitDbQuery(cfg, sid, aid, payload) {
@@ -5922,6 +5972,7 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
5922
5972
  masking: null,
5923
5973
  privacy,
5924
5974
  db: dbContext,
5975
+ valueNamespace: sid,
5925
5976
  };
5926
5977
  const dbValues = [];
5927
5978
  const pkValueCapture = pkMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
@@ -5929,37 +5980,37 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
5929
5980
  rawValue: payload.pk,
5930
5981
  previewValue: pkPreview,
5931
5982
  capture,
5932
- }, dbValues);
5983
+ }, dbValues, ctx.captureScope);
5933
5984
  const beforeValueCapture = beforeMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5934
5985
  target: 'db.before',
5935
5986
  rawValue: payload.before,
5936
5987
  previewValue: beforePreview,
5937
5988
  capture,
5938
- }, dbValues);
5989
+ }, dbValues, ctx.captureScope);
5939
5990
  const afterValueCapture = afterMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5940
5991
  target: 'db.after',
5941
5992
  rawValue: payload.after,
5942
5993
  previewValue: afterPreview,
5943
5994
  capture,
5944
- }, dbValues);
5995
+ }, dbValues, ctx.captureScope);
5945
5996
  const queryValueCapture = queryMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5946
5997
  target: 'db.query',
5947
5998
  rawValue: querySource,
5948
5999
  previewValue: queryPreview,
5949
6000
  capture,
5950
- }, dbValues);
6001
+ }, dbValues, ctx.captureScope);
5951
6002
  const resultMetaValueCapture = resultMetaMaterialization.skipped ? undefined : await maybeCaptureDbValueFromCandidatesAsync({
5952
6003
  target: 'db.resultMeta',
5953
6004
  rawValues: buildResultMetaFullValueCandidates(payload.resultMetaRaw, resultMetaSource),
5954
6005
  previewValue: resultMetaPreview,
5955
6006
  capture,
5956
- }, dbValues);
6007
+ }, dbValues, ctx.captureScope);
5957
6008
  const errorValueCapture = errorMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
5958
6009
  target: 'db.error',
5959
6010
  rawValue: payload.error,
5960
6011
  previewValue: errorPreview,
5961
6012
  capture,
5962
- }, dbValues);
6013
+ }, dbValues, ctx.captureScope);
5963
6014
  const dbEntry = attachSpanContext({
5964
6015
  type: payload.type ?? 'mongo',
5965
6016
  collection: payload.collection,
@@ -6501,6 +6552,7 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
6501
6552
  trace: candidate,
6502
6553
  masking,
6503
6554
  privacy,
6555
+ valueNamespace: getCtx().sid,
6504
6556
  };
6505
6557
  const traceValueMetadata = {
6506
6558
  fn: evt.fn,
@@ -6638,6 +6690,7 @@ async function recordKafkaTraceEventAsync(raw, sink, cfg, maskReq) {
6638
6690
  maskReq,
6639
6691
  masking,
6640
6692
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
6693
+ valueNamespace: getCtx().sid,
6641
6694
  });
6642
6695
  sink.push(...materializedEvents);
6643
6696
  }
@@ -6649,10 +6702,11 @@ function buildKafkaTraceEntries(actionId, requestRid, events) {
6649
6702
  ? reorderTraceEvents(baseEvents)
6650
6703
  : sortTraceEventsChronologically(baseEvents);
6651
6704
  const batches = chunkTraceEventsForTransport(orderedEvents, requestRid, actionId);
6705
+ const emittedValueIds = new Set();
6652
6706
  return batches.map((batch, index) => ({
6653
6707
  actionId: actionId ?? null,
6654
6708
  trace: batch,
6655
- traceValues: collectBatchTraceValueEntries(batch, index),
6709
+ traceValues: collectBatchTraceValueEntries(batch, index, emittedValueIds),
6656
6710
  traceBatch: {
6657
6711
  rid: requestRid,
6658
6712
  index,
@@ -6686,6 +6740,7 @@ function scheduleKafkaTraceFlush(cfg, kafkaCtx, actionId, requestRid, rawTraceEv
6686
6740
  masking,
6687
6741
  resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
6688
6742
  captureScope,
6743
+ valueNamespace: sid,
6689
6744
  });
6690
6745
  entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
6691
6746
  }
@@ -10090,5 +10145,6 @@ exports.__reproTestHooks = {
10090
10145
  estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
10091
10146
  sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
10092
10147
  sanitizeMaterializedTraceValueForTest: sanitizeMaterializedTraceValue,
10148
+ projectSemanticInfrastructureValueForTest: semantic_serialization_1.projectSemanticInfrastructureValue,
10093
10149
  getEventTraceValueEntriesForTest: getEventTraceValueEntries,
10094
10150
  };
@@ -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;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectSemanticInfrastructureValue = void 0;
4
+ function constructorName(value) {
5
+ if (!value || typeof value !== 'object')
6
+ return '';
7
+ try {
8
+ const prototype = Object.getPrototypeOf(value);
9
+ const constructor = prototype
10
+ ? Object.getOwnPropertyDescriptor(prototype, 'constructor')?.value
11
+ : undefined;
12
+ const name = constructor?.name;
13
+ return typeof name === 'string' ? name : '';
14
+ }
15
+ catch {
16
+ return '';
17
+ }
18
+ }
19
+ function ownDataDescriptors(value) {
20
+ const out = {};
21
+ let descriptors;
22
+ try {
23
+ descriptors = Object.getOwnPropertyDescriptors(value);
24
+ }
25
+ catch {
26
+ return out;
27
+ }
28
+ for (const [key, descriptor] of Object.entries(descriptors)) {
29
+ if (Object.prototype.hasOwnProperty.call(descriptor, 'value')) {
30
+ out[key] = descriptor;
31
+ }
32
+ }
33
+ return out;
34
+ }
35
+ function method(value, key) {
36
+ try {
37
+ let current = value;
38
+ while (current) {
39
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
40
+ if (descriptor) {
41
+ return Object.prototype.hasOwnProperty.call(descriptor, 'value') &&
42
+ typeof descriptor.value === 'function';
43
+ }
44
+ current = Object.getPrototypeOf(current);
45
+ }
46
+ return false;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ function primitiveStateValue(value) {
53
+ if (value === null)
54
+ return null;
55
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
56
+ return value;
57
+ }
58
+ if (typeof value === 'bigint')
59
+ return value.toString();
60
+ if (value instanceof Date)
61
+ return value.toISOString();
62
+ return undefined;
63
+ }
64
+ function semanticValue(category, className, details = {}) {
65
+ return {
66
+ matched: true,
67
+ value: {
68
+ __kind: 'repro-semantic',
69
+ category,
70
+ ...(className ? { className } : {}),
71
+ ...details,
72
+ },
73
+ };
74
+ }
75
+ function projectConfigurationService(value, className) {
76
+ if (!/(^|Api|App|Runtime|Environment)ConfigService$/i.test(className))
77
+ return null;
78
+ if (!method(value, 'get') && !method(value, 'getOrThrow'))
79
+ return null;
80
+ return semanticValue('configuration-service', className);
81
+ }
82
+ function projectLogger(value, className) {
83
+ if (!/(Logger|LoggerService)$/i.test(className))
84
+ return null;
85
+ const descriptors = ownDataDescriptors(value);
86
+ const context = primitiveStateValue(descriptors.context?.value);
87
+ return semanticValue('logger', className, context !== undefined ? { context } : {});
88
+ }
89
+ function projectNestInfrastructure(value, className) {
90
+ if (!/(ModuleRef|ModulesContainer|InstanceWrapper|NestContainer|Injector|DiscoveryService|ExecutionContextHost|ArgumentsHost|HttpAdapterHost)$/i.test(className)) {
91
+ return null;
92
+ }
93
+ return semanticValue('nestjs-infrastructure', className);
94
+ }
95
+ function projectRedis(value, className) {
96
+ const looksLikeRedis = /Redis|Commander|Cluster/i.test(className) && (method(value, 'get') || method(value, 'set') || method(value, 'pipeline') || method(value, 'multi'));
97
+ if (!looksLikeRedis)
98
+ return null;
99
+ const descriptors = ownDataDescriptors(value);
100
+ const status = primitiveStateValue(descriptors.status?.value);
101
+ return semanticValue('redis-client', className, status !== undefined ? { status } : {});
102
+ }
103
+ function projectKafka(value, className) {
104
+ if (!/Kafka/i.test(className))
105
+ return null;
106
+ if (method(value, 'send') && method(value, 'connect') && method(value, 'disconnect')) {
107
+ return semanticValue('kafka-producer', className || 'KafkaProducer');
108
+ }
109
+ if (method(value, 'subscribe') && method(value, 'run') && method(value, 'connect')) {
110
+ return semanticValue('kafka-consumer', className || 'KafkaConsumer');
111
+ }
112
+ if (method(value, 'listTopics') && method(value, 'connect') && method(value, 'disconnect')) {
113
+ return semanticValue('kafka-admin', className || 'KafkaAdmin');
114
+ }
115
+ return null;
116
+ }
117
+ function projectAmqp(value, className) {
118
+ if (!/(Amqp|Channel|ChannelModel|ConfirmChannel)/i.test(className))
119
+ return null;
120
+ if (method(value, 'publish') && method(value, 'sendToQueue') && method(value, 'ack')) {
121
+ return semanticValue('amqp-channel', className || 'AmqpChannel');
122
+ }
123
+ if (method(value, 'createChannel') && method(value, 'close')) {
124
+ return semanticValue('amqp-connection', className || 'AmqpConnection');
125
+ }
126
+ return null;
127
+ }
128
+ function projectPostgres(value, className) {
129
+ if (/^(Pool|BoundPool)$/i.test(className) && method(value, 'query') && method(value, 'connect')) {
130
+ return semanticValue('postgres-pool', className);
131
+ }
132
+ if (/^(Client|PoolClient)$/i.test(className) && method(value, 'query')) {
133
+ return semanticValue('postgres-client', className);
134
+ }
135
+ return null;
136
+ }
137
+ function projectTypeOrm(value, className) {
138
+ if (/^DataSource$/i.test(className) && method(value, 'createQueryRunner')) {
139
+ return semanticValue('typeorm-infrastructure', className);
140
+ }
141
+ if (/^EntityManager$/i.test(className) && method(value, 'getRepository') && method(value, 'query')) {
142
+ return semanticValue('typeorm-infrastructure', className);
143
+ }
144
+ if (/^QueryRunner$/i.test(className) && method(value, 'query') && method(value, 'release')) {
145
+ return semanticValue('typeorm-infrastructure', className);
146
+ }
147
+ if (/^Repository$/i.test(className) && method(value, 'find') && method(value, 'save')) {
148
+ return semanticValue('typeorm-infrastructure', className);
149
+ }
150
+ return null;
151
+ }
152
+ /**
153
+ * Projects known runtime machinery into stable, non-recursive descriptions.
154
+ * Unknown application values deliberately return null and remain fully captured.
155
+ */
156
+ function projectSemanticInfrastructureValue(value) {
157
+ if (!value || typeof value !== 'object' || Array.isArray(value))
158
+ return null;
159
+ const className = constructorName(value);
160
+ if (!className || className === 'Object')
161
+ return null;
162
+ return (projectConfigurationService(value, className) ||
163
+ projectLogger(value, className) ||
164
+ projectNestInfrastructure(value, className) ||
165
+ projectRedis(value, className) ||
166
+ projectKafka(value, className) ||
167
+ projectAmqp(value, className) ||
168
+ projectPostgres(value, className) ||
169
+ projectTypeOrm(value, className));
170
+ }
171
+ exports.projectSemanticInfrastructureValue = projectSemanticInfrastructureValue;