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