@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 +7 -1
- package/dist/index.js +173 -56
- package/dist/semantic-serialization.d.ts +9 -0
- package/dist/semantic-serialization.js +171 -0
- package/package.json +3 -3
- package/src/index.ts +296 -56
- package/src/semantic-serialization.ts +182 -0
- package/test/content-addressed-mongodb.testcontainers.test.js +367 -0
- package/test/content-addressed-trace-values.test.js +264 -0
- package/test/fixtures/semantic-serialization-trace-controller.js +85 -0
- package/test/fixtures/semantic-serialization-trace-server.js +19 -0
- package/test/kafka-runtime-privacy-policy.test.js +26 -2
- package/test/policy-session-completion.testcontainers.test.js +18 -1
- package/test/semantic-serialization-trace.test.js +152 -0
- package/test/semantic-serialization.test.js +229 -0
- package/test/session-storage-budget.test.js +298 -0
- package/test/trace-batch-size.test.js +33 -0
- package/test/trace-observation-snapshots.test.js +403 -0
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
|
-
|
|
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
|
|
4342
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = (
|
|
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 = (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
:
|
|
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:
|
|
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;
|