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