@reproapp/node-sdk 0.0.4 → 0.0.6

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.js CHANGED
@@ -736,6 +736,9 @@ function traceEventCandidateFromRecord(event) {
736
736
  library: inferLibraryNameFromFile(sourceFileForLibrary ?? event.file),
737
737
  };
738
738
  }
739
+ function shouldOmitJsonBuiltinTraceValues(event) {
740
+ return event.fn === 'JSON.parse' || event.fn === 'JSON.stringify';
741
+ }
739
742
  function preparePendingTraceEventsForFlush(events) {
740
743
  const prepared = [];
741
744
  const excludedSpanIds = new Set();
@@ -755,6 +758,13 @@ function preparePendingTraceEventsForFlush(events) {
755
758
  }
756
759
  if (event.__reproPending) {
757
760
  event.__reproPending.candidate = candidate;
761
+ if (shouldOmitJsonBuiltinTraceValues(candidate)) {
762
+ delete event.__reproPending.argsRaw;
763
+ delete event.__reproPending.returnValueRaw;
764
+ if (event.__reproPending.errorRaw === undefined) {
765
+ delete event.__reproPending;
766
+ }
767
+ }
758
768
  }
759
769
  delete event.__reproSourceFile;
760
770
  prepared.push(event);
@@ -807,6 +817,12 @@ const SDK_BACKGROUND_BUSY_IDLE_DELAY_MS = (() => {
807
817
  return env;
808
818
  return 1000;
809
819
  })();
820
+ const SDK_BACKGROUND_MAX_DEFER_MS = (() => {
821
+ const env = Number(process.env.REPRO_SDK_BACKGROUND_MAX_DEFER_MS);
822
+ if (Number.isFinite(env) && env >= 0)
823
+ return Math.trunc(env);
824
+ return 2000;
825
+ })();
810
826
  const SDK_BACKGROUND_WORK_TIMEOUT_MS = (() => {
811
827
  const env = Number(process.env.REPRO_SDK_BACKGROUND_WORK_TIMEOUT_MS);
812
828
  if (Number.isFinite(env) && env >= 0)
@@ -844,6 +860,7 @@ const TRACE_MATERIALIZE_WORKER_IDLE_SHUTDOWN_MS = (() => {
844
860
  return 500;
845
861
  })();
846
862
  let activeClientRequestCount = 0;
863
+ let oldestActiveClientRequestAt = null;
847
864
  let lastClientActivityAt = 0;
848
865
  let sdkBackgroundQuietUntil = 0;
849
866
  const sdkBackgroundQueue = [];
@@ -863,6 +880,12 @@ function noteClientActivity(now = Date.now()) {
863
880
  }
864
881
  function getSdkBackgroundDelayMs(now = Date.now()) {
865
882
  if (hasActiveClientRequests()) {
883
+ if (oldestActiveClientRequestAt !== null && SDK_BACKGROUND_MAX_DEFER_MS >= 0) {
884
+ const activeForMs = now - oldestActiveClientRequestAt;
885
+ if (activeForMs >= SDK_BACKGROUND_MAX_DEFER_MS)
886
+ return 0;
887
+ return Math.min(SDK_BACKGROUND_IDLE_DELAY_MS, Math.max(0, SDK_BACKGROUND_MAX_DEFER_MS - activeForMs));
888
+ }
866
889
  return SDK_BACKGROUND_IDLE_DELAY_MS;
867
890
  }
868
891
  const sinceLastActivity = now - lastClientActivityAt;
@@ -1077,12 +1100,21 @@ function shouldOffloadPendingTraceEvents(events) {
1077
1100
  const pending = event.__reproPending;
1078
1101
  if (!pending)
1079
1102
  continue;
1080
- if (pending.argsRaw !== undefined)
1103
+ if (pending.argsRaw !== undefined) {
1104
+ if (traceInlineValueExceedsLimit(pending.argsRaw))
1105
+ return false;
1081
1106
  pendingValueCount += 1;
1082
- if (pending.returnValueRaw !== undefined)
1107
+ }
1108
+ if (pending.returnValueRaw !== undefined) {
1109
+ if (traceInlineValueExceedsLimit(pending.returnValueRaw))
1110
+ return false;
1083
1111
  pendingValueCount += 1;
1084
- if (pending.errorRaw !== undefined)
1112
+ }
1113
+ if (pending.errorRaw !== undefined) {
1114
+ if (traceInlineValueExceedsLimit(pending.errorRaw))
1115
+ return false;
1085
1116
  pendingValueCount += 1;
1117
+ }
1086
1118
  if (pendingValueCount >= TRACE_MATERIALIZE_OFFTHREAD_MIN_VALUES) {
1087
1119
  return true;
1088
1120
  }
@@ -1095,6 +1127,9 @@ function beginClientRequest() {
1095
1127
  sdkBackgroundQuietUntil = Math.max(sdkBackgroundQuietUntil, now + SDK_BACKGROUND_BUSY_IDLE_DELAY_MS);
1096
1128
  }
1097
1129
  noteClientActivity(now);
1130
+ if (activeClientRequestCount === 0) {
1131
+ oldestActiveClientRequestAt = now;
1132
+ }
1098
1133
  activeClientRequestCount += 1;
1099
1134
  if (sdkBackgroundTimer) {
1100
1135
  try {
@@ -1108,6 +1143,7 @@ function endClientRequest() {
1108
1143
  activeClientRequestCount = Math.max(0, activeClientRequestCount - 1);
1109
1144
  noteClientActivity();
1110
1145
  if (!hasActiveClientRequests()) {
1146
+ oldestActiveClientRequestAt = null;
1111
1147
  (0, client_1.kickIngestQueueDrain)();
1112
1148
  if (sdkBackgroundTimer) {
1113
1149
  try {
@@ -1609,26 +1645,17 @@ const TRACE_VALUE_MAX_DEPTH = 3;
1609
1645
  const TRACE_VALUE_MAX_KEYS = 20;
1610
1646
  const TRACE_VALUE_MAX_ITEMS = 20;
1611
1647
  const TRACE_VALUE_MAX_STRING = 2000;
1612
- const TRACE_FULL_VALUE_MAX_DEPTH = 8;
1613
- const TRACE_FULL_VALUE_MAX_KEYS = 200;
1614
- const TRACE_FULL_VALUE_MAX_ITEMS = 200;
1615
- const TRACE_FULL_VALUE_MAX_STRING = 32000;
1616
- const TRACE_FULL_VALUE_FALLBACK_DEPTH = 6;
1617
- const TRACE_FULL_VALUE_FALLBACK_KEYS = 100;
1618
- const TRACE_FULL_VALUE_FALLBACK_ITEMS = 100;
1619
- const TRACE_FULL_VALUE_FALLBACK_STRING = 8000;
1620
- const TRACE_FULL_VALUE_MAX_SERIALIZED_CHARS = 128 * 1024;
1621
- const INLINE_PRIVACY_MAX_SERIALIZED_CHARS = (() => {
1622
- const env = Number(process.env.REPRO_SDK_INLINE_PRIVACY_MAX_SERIALIZED_CHARS);
1648
+ const TRACE_FULL_VALUE_MAX_DEPTH = Number.MAX_SAFE_INTEGER;
1649
+ const TRACE_FULL_VALUE_MAX_KEYS = Number.MAX_SAFE_INTEGER;
1650
+ const TRACE_FULL_VALUE_MAX_ITEMS = Number.MAX_SAFE_INTEGER;
1651
+ const TRACE_FULL_VALUE_MAX_STRING = Number.MAX_SAFE_INTEGER;
1652
+ const TRACE_INLINE_VALUE_MAX_SERIALIZED_CHARS = (() => {
1653
+ const env = Number(process.env.REPRO_SDK_TRACE_INLINE_VALUE_MAX_SERIALIZED_CHARS);
1623
1654
  if (Number.isFinite(env) && env > 0)
1624
1655
  return Math.trunc(env);
1625
1656
  return 8 * 1024;
1626
1657
  })();
1627
- const INLINE_PRIVACY_PREVIEW_MAX_DEPTH = 3;
1628
- const INLINE_PRIVACY_PREVIEW_MAX_KEYS = 12;
1629
- const INLINE_PRIVACY_PREVIEW_MAX_ITEMS = 12;
1630
- const INLINE_PRIVACY_PREVIEW_MAX_STRING = 512;
1631
- const INLINE_PRIVACY_PREVIEW_JSON_PARSE_MAX_CHARS = 128 * 1024;
1658
+ const TRACE_VALUE_SIZE_EXCEEDED = Symbol('trace-value-size-exceeded');
1632
1659
  const TRACE_BATCH_SIZE = 100;
1633
1660
  const TRACE_FLUSH_DELAY_MS = 20;
1634
1661
  // Choose how to order trace events in payloads.
@@ -1954,7 +1981,7 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
1954
1981
  if (existingPath)
1955
1982
  return circularReference(existingPath);
1956
1983
  seen.set(value, valuePath);
1957
- if (depth >= TRACE_VALUE_MAX_DEPTH) {
1984
+ if (!options.disableTruncation && depth >= TRACE_VALUE_MAX_DEPTH) {
1958
1985
  const shallow = safeJson(value);
1959
1986
  if (shallow !== undefined) {
1960
1987
  return shallow;
@@ -1965,9 +1992,10 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
1965
1992
  : { __truncated: `depth>${TRACE_VALUE_MAX_DEPTH}` };
1966
1993
  }
1967
1994
  if (Array.isArray(value)) {
1968
- const out = value.slice(0, TRACE_VALUE_MAX_ITEMS)
1995
+ const sourceItems = options.disableTruncation ? value : value.slice(0, TRACE_VALUE_MAX_ITEMS);
1996
+ const out = sourceItems
1969
1997
  .map((item, index) => sanitizeTraceValue(item, depth + 1, seen, options, childCapturePath(valuePath, index)));
1970
- if (value.length > TRACE_VALUE_MAX_ITEMS) {
1998
+ if (!options.disableTruncation && value.length > TRACE_VALUE_MAX_ITEMS) {
1971
1999
  out.push(`…(${value.length - TRACE_VALUE_MAX_ITEMS} more items)`);
1972
2000
  }
1973
2001
  return out;
@@ -1976,13 +2004,13 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
1976
2004
  const out = {};
1977
2005
  let index = 0;
1978
2006
  for (const [k, v] of value) {
1979
- if (index >= TRACE_VALUE_MAX_ITEMS)
2007
+ if (!options.disableTruncation && index >= TRACE_VALUE_MAX_ITEMS)
1980
2008
  break;
1981
2009
  const key = normalizeCollectionKeyForCapture(k, index);
1982
2010
  out[key] = sanitizeTraceValue(v, depth + 1, seen, options, childCapturePath(valuePath, key));
1983
2011
  index += 1;
1984
2012
  }
1985
- if (value.size > TRACE_VALUE_MAX_ITEMS) {
2013
+ if (!options.disableTruncation && value.size > TRACE_VALUE_MAX_ITEMS) {
1986
2014
  out.__truncatedEntries = value.size - TRACE_VALUE_MAX_ITEMS;
1987
2015
  }
1988
2016
  return out;
@@ -1990,11 +2018,11 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
1990
2018
  if (value instanceof Set) {
1991
2019
  const arr = [];
1992
2020
  for (const item of value) {
1993
- if (arr.length >= TRACE_VALUE_MAX_ITEMS)
2021
+ if (!options.disableTruncation && arr.length >= TRACE_VALUE_MAX_ITEMS)
1994
2022
  break;
1995
2023
  arr.push(sanitizeTraceValue(item, depth + 1, seen, options, childCapturePath(valuePath, arr.length)));
1996
2024
  }
1997
- if (value.size > TRACE_VALUE_MAX_ITEMS) {
2025
+ if (!options.disableTruncation && value.size > TRACE_VALUE_MAX_ITEMS) {
1998
2026
  arr.push(`…(${value.size - TRACE_VALUE_MAX_ITEMS} more items)`);
1999
2027
  }
2000
2028
  return arr;
@@ -2002,7 +2030,8 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
2002
2030
  const ctor = value?.constructor?.name;
2003
2031
  const result = {};
2004
2032
  const keys = Object.keys(value);
2005
- for (const key of keys.slice(0, TRACE_VALUE_MAX_KEYS)) {
2033
+ const visibleKeys = options.disableTruncation ? keys : keys.slice(0, TRACE_VALUE_MAX_KEYS);
2034
+ for (const key of visibleKeys) {
2006
2035
  try {
2007
2036
  result[key] = sanitizeTraceValue(value[key], depth + 1, seen, options, childCapturePath(valuePath, key));
2008
2037
  }
@@ -2010,7 +2039,7 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
2010
2039
  result[key] = `[Cannot serialize: ${err?.message || 'unknown error'}]`;
2011
2040
  }
2012
2041
  }
2013
- if (keys.length > TRACE_VALUE_MAX_KEYS) {
2042
+ if (!options.disableTruncation && keys.length > TRACE_VALUE_MAX_KEYS) {
2014
2043
  result.__truncatedKeys = keys.length - TRACE_VALUE_MAX_KEYS;
2015
2044
  }
2016
2045
  if (ctor && ctor !== 'Object') {
@@ -2024,7 +2053,10 @@ function sanitizeTraceArgs(values) {
2024
2053
  return values.map((v, index) => sanitizeTraceValue(v, 0, new WeakMap(), {}, `$[${index}]`));
2025
2054
  }
2026
2055
  function sanitizeTraceValueForPrivacy(value) {
2027
- return sanitizeTraceValue(value, 0, new WeakMap(), { preserveLongStrings: true });
2056
+ return sanitizeTraceValue(value, 0, new WeakMap(), { preserveLongStrings: true, disableTruncation: true });
2057
+ }
2058
+ function sanitizeMaterializedTraceValue(value) {
2059
+ return sanitizeTraceValue(value, 0, new WeakMap(), { preserveLongStrings: true, disableTruncation: true });
2028
2060
  }
2029
2061
  function sanitizeTraceArgsForPrivacy(values) {
2030
2062
  if (!Array.isArray(values))
@@ -2036,6 +2068,9 @@ function normalizeRawTraceArgs(values) {
2036
2068
  return values;
2037
2069
  return [values];
2038
2070
  }
2071
+ function hasMeaningfulRawTraceError(error) {
2072
+ return error !== undefined && error !== null;
2073
+ }
2039
2074
  async function materializePendingTraceEvents(events, params) {
2040
2075
  for (const evt of events) {
2041
2076
  const pending = evt.__reproPending;
@@ -2043,74 +2078,16 @@ async function materializePendingTraceEvents(events, params) {
2043
2078
  continue;
2044
2079
  delete evt.__reproPending;
2045
2080
  const candidate = pending.candidate ?? traceEventCandidateFromRecord(evt);
2081
+ const omitJsonBuiltinValues = shouldOmitJsonBuiltinTraceValues(candidate);
2046
2082
  const activePrivacy = params.resolvePrivacy();
2047
- const capture = {
2048
- runtimeConfig: params.cfg,
2049
- captureHeaders: params.cfg.captureHeaders,
2050
- maskReq: params.maskReq,
2051
- trace: candidate,
2052
- masking: params.masking,
2053
- privacy: activePrivacy,
2054
- };
2055
- if (pending.argsRaw !== undefined) {
2056
- const argsPreview = sanitizeTraceArgsForPrivacy(pending.argsRaw);
2057
- const argsMaterialization = await materializeInlinePrivacyValueAsync('trace.args', argsPreview, params.cfg, params.maskReq, candidate, params.masking, activePrivacy);
2058
- if (argsMaterialization.skipped) {
2059
- evt.argsMaterialization = argsMaterialization.skipped;
2060
- if (argsMaterialization.value !== undefined) {
2061
- evt.args = sanitizeTraceValue(argsMaterialization.value);
2062
- }
2063
- }
2064
- else {
2065
- evt.args = sanitizeTraceValue(argsMaterialization.value);
2066
- evt.argsValueCapture = await maybeCaptureFullTraceValueAsync({
2067
- target: 'trace.args',
2068
- rawValue: pending.argsRaw,
2069
- previewValue: evt.args,
2070
- event: evt,
2071
- capture,
2072
- });
2073
- }
2083
+ if (!omitJsonBuiltinValues && pending.argsRaw !== undefined) {
2084
+ evt.args = sanitizeMaterializedTraceValue(await materializeTracePrivacyValueAsync('trace.args', sanitizeTraceArgsForPrivacy(pending.argsRaw), params.cfg, params.maskReq, candidate, params.masking, activePrivacy));
2074
2085
  }
2075
- if (pending.returnValueRaw !== undefined) {
2076
- const returnValuePreview = sanitizeTraceValueForPrivacy(pending.returnValueRaw);
2077
- const returnValueMaterialization = await materializeInlinePrivacyValueAsync('trace.returnValue', returnValuePreview, params.cfg, params.maskReq, candidate, params.masking, activePrivacy);
2078
- if (returnValueMaterialization.skipped) {
2079
- evt.returnValueMaterialization = returnValueMaterialization.skipped;
2080
- if (returnValueMaterialization.value !== undefined) {
2081
- evt.returnValue = sanitizeTraceValue(returnValueMaterialization.value);
2082
- }
2083
- }
2084
- else {
2085
- evt.returnValue = sanitizeTraceValue(returnValueMaterialization.value);
2086
- evt.returnValueCapture = await maybeCaptureFullTraceValueAsync({
2087
- target: 'trace.returnValue',
2088
- rawValue: pending.returnValueRaw,
2089
- previewValue: evt.returnValue,
2090
- event: evt,
2091
- capture,
2092
- });
2093
- }
2086
+ if (!omitJsonBuiltinValues && pending.returnValueRaw !== undefined) {
2087
+ evt.returnValue = sanitizeMaterializedTraceValue(await materializeTracePrivacyValueAsync('trace.returnValue', sanitizeTraceValueForPrivacy(pending.returnValueRaw), params.cfg, params.maskReq, candidate, params.masking, activePrivacy));
2094
2088
  }
2095
2089
  if (pending.errorRaw !== undefined) {
2096
- const errorPreview = sanitizeTraceValueForPrivacy(pending.errorRaw);
2097
- const errorMaterialization = await materializeInlinePrivacyValueAsync('trace.error', errorPreview, params.cfg, params.maskReq, candidate, params.masking, activePrivacy);
2098
- if (errorMaterialization.skipped) {
2099
- evt.errorMaterialization = errorMaterialization.skipped;
2100
- if (errorMaterialization.value !== undefined) {
2101
- evt.error = sanitizeTraceValue(errorMaterialization.value);
2102
- }
2103
- }
2104
- else {
2105
- evt.error = sanitizeTraceValue(errorMaterialization.value);
2106
- evt.errorValueCapture = await maybeCaptureFullTraceValueAsync({
2107
- target: 'trace.error',
2108
- rawValue: pending.errorRaw,
2109
- previewValue: evt.error,
2110
- event: evt,
2111
- capture,
2112
- });
2113
- }
2090
+ evt.error = sanitizeMaterializedTraceValue(await materializeTracePrivacyValueAsync('trace.error', sanitizeTraceValueForPrivacy(pending.errorRaw), params.cfg, params.maskReq, candidate, params.masking, activePrivacy));
2114
2091
  }
2115
2092
  }
2116
2093
  return events;
@@ -2120,10 +2097,6 @@ function stripPendingTraceValues(events, reason) {
2120
2097
  if (!evt.__reproPending)
2121
2098
  continue;
2122
2099
  delete evt.__reproPending;
2123
- evt.valueMaterialization = {
2124
- status: 'skipped',
2125
- reason,
2126
- };
2127
2100
  }
2128
2101
  return events;
2129
2102
  }
@@ -2203,7 +2176,7 @@ function sanitizeRequestSnapshot(value) {
2203
2176
  if (value === undefined)
2204
2177
  return undefined;
2205
2178
  try {
2206
- return sanitizeTraceValue(value);
2179
+ return sanitizeTraceValue(value, 0, new WeakMap(), { preserveLongStrings: true, disableTruncation: true });
2207
2180
  }
2208
2181
  catch {
2209
2182
  if (value === null)
@@ -2396,6 +2369,21 @@ function shareRuntimePrivacyState(source, target) {
2396
2369
  function shouldCapturePrivacyRaw(cfg) {
2397
2370
  return cfg.privacy?.captureRaw === true;
2398
2371
  }
2372
+ function normalizeValueForRuntimePrivacy(value) {
2373
+ if (value === undefined || value === null) {
2374
+ return value;
2375
+ }
2376
+ try {
2377
+ return sanitizeTraceValue(value, 0, new WeakMap(), {
2378
+ preserveLongStrings: true,
2379
+ disableTruncation: true,
2380
+ });
2381
+ }
2382
+ catch {
2383
+ const fallback = safeJson(value);
2384
+ return fallback === undefined ? value : fallback;
2385
+ }
2386
+ }
2399
2387
  function resolveRuntimePrivacyStartupMaxWaitMs(cfg) {
2400
2388
  const value = Number(cfg?.startupMaxWaitMs);
2401
2389
  if (!Number.isFinite(value)) {
@@ -2560,7 +2548,8 @@ function applyPrivacyThenMask(target, value, cfg, req, trace, masking, privacy,
2560
2548
  if (shouldCapturePrivacyRaw(cfg)) {
2561
2549
  return value;
2562
2550
  }
2563
- const transformed = (0, privacy_1.applyRuntimePrivacyPolicy)(privacy, target, value, {
2551
+ const privacyInput = normalizeValueForRuntimePrivacy(value);
2552
+ const transformed = (0, privacy_1.applyRuntimePrivacyPolicy)(privacy, target, privacyInput, {
2564
2553
  routeKey: req.key,
2565
2554
  trace: trace
2566
2555
  ? {
@@ -2579,7 +2568,8 @@ async function applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking
2579
2568
  if (shouldCapturePrivacyRaw(cfg)) {
2580
2569
  return value;
2581
2570
  }
2582
- const transformed = await (0, privacy_1.applyRuntimePrivacyPolicyAsync)(privacy, target, value, {
2571
+ const privacyInput = normalizeValueForRuntimePrivacy(value);
2572
+ const transformed = await (0, privacy_1.applyRuntimePrivacyPolicyAsync)(privacy, target, privacyInput, {
2583
2573
  routeKey: req.key,
2584
2574
  trace: trace
2585
2575
  ? {
@@ -2594,174 +2584,43 @@ async function applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking
2594
2584
  });
2595
2585
  return applyMasking(target, transformed, req, trace, masking);
2596
2586
  }
2597
- function shouldBoundInlinePrivacyTarget(target) {
2598
- return (target === 'request.body' ||
2599
- target === 'response.body' ||
2600
- target === 'trace.args' ||
2601
- target === 'trace.returnValue' ||
2602
- target === 'trace.error' ||
2603
- target === 'db.before' ||
2604
- target === 'db.after' ||
2605
- target === 'db.query' ||
2606
- target === 'db.resultMeta' ||
2607
- target === 'db.error');
2608
- }
2609
- function buildOversizedInlinePreview(value, depth = 0, seen = new WeakSet(), preserveScalarValue = true) {
2610
- if (value === null || value === undefined)
2611
- return value;
2612
- const type = typeof value;
2613
- if (type === 'string') {
2614
- if (value.length <= INLINE_PRIVACY_PREVIEW_MAX_STRING) {
2615
- return preserveScalarValue ? value : { __type: 'string', length: value.length };
2616
- }
2617
- const trimmed = value.trim();
2618
- const mayBeJson = trimmed.startsWith('{') || trimmed.startsWith('[');
2619
- if (mayBeJson && value.length <= INLINE_PRIVACY_PREVIEW_JSON_PARSE_MAX_CHARS) {
2620
- try {
2621
- return {
2622
- __type: 'json-string',
2623
- length: value.length,
2624
- parsed: buildOversizedInlinePreview(JSON.parse(value), depth + 1, seen, false),
2625
- };
2626
- }
2627
- catch { }
2628
- }
2629
- return {
2630
- __type: 'string',
2631
- length: value.length,
2632
- truncated: true,
2633
- };
2634
- }
2635
- if (type === 'number' || type === 'boolean')
2636
- return preserveScalarValue ? value : { __type: type };
2637
- if (type === 'bigint')
2638
- return preserveScalarValue ? `${value.toString()}n` : { __type: 'bigint' };
2639
- if (type === 'symbol')
2640
- return preserveScalarValue ? value.toString() : { __type: 'symbol' };
2641
- if (type === 'function')
2642
- return `[Function${value.name ? ` ${value.name}` : ''}]`;
2643
- if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
2644
- return {
2645
- __type: Buffer.isBuffer(value) ? 'Buffer' : 'Uint8Array',
2646
- length: value.length,
2647
- preview: value.length ? Buffer.from(value).slice(0, 32).toString('hex') : '',
2648
- };
2649
- }
2650
- if (value instanceof Date)
2651
- return preserveScalarValue ? value.toISOString() : { __type: 'Date' };
2652
- if (value instanceof RegExp)
2653
- return preserveScalarValue ? value.toString() : { __type: 'RegExp' };
2654
- if (value instanceof Error) {
2655
- return {
2656
- __type: 'Error',
2657
- name: value.name,
2658
- message: preserveScalarValue ? value.message : { __type: 'string', length: value.message.length },
2659
- };
2660
- }
2661
- if (value instanceof WeakMap || value instanceof WeakSet) {
2662
- return { __type: getCtorName(value) || 'WeakCollection', uninspectable: true };
2663
- }
2664
- if (type !== 'object')
2665
- return preserveScalarValue ? String(value) : { __type: type };
2666
- if (seen.has(value))
2667
- return { __type: 'circular-reference' };
2668
- seen.add(value);
2669
- if (Array.isArray(value)) {
2670
- const items = value
2671
- .slice(0, INLINE_PRIVACY_PREVIEW_MAX_ITEMS)
2672
- .map((item) => buildOversizedInlinePreview(item, depth + 1, seen, true));
2673
- if (value.length > INLINE_PRIVACY_PREVIEW_MAX_ITEMS) {
2674
- items.push({ __truncatedItems: value.length - INLINE_PRIVACY_PREVIEW_MAX_ITEMS });
2675
- }
2676
- return items;
2677
- }
2678
- if (value instanceof Map) {
2679
- const out = { __type: 'Map', size: value.size };
2680
- let index = 0;
2681
- for (const [key, item] of value.entries()) {
2682
- if (index >= INLINE_PRIVACY_PREVIEW_MAX_ITEMS)
2683
- break;
2684
- out[normalizeCollectionKeyForCapture(key, index)] = buildOversizedInlinePreview(item, depth + 1, seen, false);
2685
- index += 1;
2686
- }
2687
- if (value.size > INLINE_PRIVACY_PREVIEW_MAX_ITEMS) {
2688
- out.__truncatedEntries = value.size - INLINE_PRIVACY_PREVIEW_MAX_ITEMS;
2689
- }
2690
- return out;
2587
+ function traceInlineValueExceedsLimit(value) {
2588
+ return boundedSerializedTraceValueLength(value, TRACE_INLINE_VALUE_MAX_SERIALIZED_CHARS).exceeded;
2589
+ }
2590
+ function limitInlinePrivacyValue(target, value) {
2591
+ void target;
2592
+ return { value };
2593
+ }
2594
+ function limitRawInlinePrivacyValue(target, value) {
2595
+ const limited = limitInlinePrivacyValue(target, value);
2596
+ return limited.skipped ? limited : null;
2597
+ }
2598
+ function materializeInlinePrivacyValue(target, value, cfg, req, trace, masking, privacy, db) {
2599
+ if (value === undefined || shouldCapturePrivacyRaw(cfg)) {
2600
+ return { value };
2691
2601
  }
2692
- if (value instanceof Set) {
2693
- const items = [];
2694
- let index = 0;
2695
- for (const item of value.values()) {
2696
- if (index >= INLINE_PRIVACY_PREVIEW_MAX_ITEMS)
2697
- break;
2698
- items.push(buildOversizedInlinePreview(item, depth + 1, seen, false));
2699
- index += 1;
2700
- }
2701
- if (value.size > INLINE_PRIVACY_PREVIEW_MAX_ITEMS) {
2702
- items.push({ __truncatedItems: value.size - INLINE_PRIVACY_PREVIEW_MAX_ITEMS });
2703
- }
2704
- return {
2705
- __type: 'Set',
2706
- size: value.size,
2707
- items,
2708
- };
2602
+ try {
2603
+ const valueAfterPrivacy = applyPrivacyThenMask(target, value, cfg, req, trace, masking, privacy, db);
2604
+ return { value: valueAfterPrivacy };
2709
2605
  }
2710
- const keys = Object.keys(value);
2711
- const ctor = getCtorName(value);
2712
- if (depth >= INLINE_PRIVACY_PREVIEW_MAX_DEPTH) {
2606
+ catch {
2713
2607
  return {
2714
- ...(ctor && ctor !== 'Object' ? { __type: ctor } : { __type: 'Object' }),
2715
- __keys: keys.slice(0, INLINE_PRIVACY_PREVIEW_MAX_KEYS),
2716
- ...(keys.length > INLINE_PRIVACY_PREVIEW_MAX_KEYS
2717
- ? { __truncatedKeys: keys.length - INLINE_PRIVACY_PREVIEW_MAX_KEYS }
2718
- : {}),
2608
+ value: undefined,
2609
+ skipped: {
2610
+ status: 'skipped',
2611
+ reason: 'inline_privacy_failed',
2612
+ target,
2613
+ },
2719
2614
  };
2720
2615
  }
2721
- const out = ctor && ctor !== 'Object' ? { __type: ctor } : {};
2722
- for (const key of keys.slice(0, INLINE_PRIVACY_PREVIEW_MAX_KEYS)) {
2723
- try {
2724
- out[key] = buildOversizedInlinePreview(value[key], depth + 1, seen, false);
2725
- }
2726
- catch (err) {
2727
- out[key] = { __type: 'uninspectable', message: err?.message || 'unknown error' };
2728
- }
2729
- }
2730
- if (keys.length > INLINE_PRIVACY_PREVIEW_MAX_KEYS) {
2731
- out.__truncatedKeys = keys.length - INLINE_PRIVACY_PREVIEW_MAX_KEYS;
2732
- }
2733
- return out;
2734
2616
  }
2735
2617
  async function materializeInlinePrivacyValueAsync(target, value, cfg, req, trace, masking, privacy, db) {
2736
2618
  if (value === undefined || shouldCapturePrivacyRaw(cfg)) {
2737
2619
  return { value };
2738
2620
  }
2739
- if (shouldBoundInlinePrivacyTarget(target)) {
2740
- const serialized = stableSerializeTraceValue(value);
2741
- const serializedLength = serialized?.length ?? 0;
2742
- if (serializedLength > INLINE_PRIVACY_MAX_SERIALIZED_CHARS) {
2743
- let preview = buildOversizedInlinePreview(value);
2744
- try {
2745
- preview = await applyPrivacyThenMaskAsync(target, preview, cfg, req, trace, masking, privacy, db);
2746
- }
2747
- catch { }
2748
- return {
2749
- value: preview,
2750
- skipped: {
2751
- status: 'skipped',
2752
- reason: 'inline_value_too_large',
2753
- target,
2754
- serializedLength,
2755
- limit: INLINE_PRIVACY_MAX_SERIALIZED_CHARS,
2756
- preview: 'bounded',
2757
- },
2758
- };
2759
- }
2760
- }
2761
2621
  try {
2762
- return {
2763
- value: await applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking, privacy, db),
2764
- };
2622
+ const valueAfterPrivacy = await applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking, privacy, db);
2623
+ return { value: valueAfterPrivacy };
2765
2624
  }
2766
2625
  catch {
2767
2626
  return {
@@ -2774,6 +2633,18 @@ async function materializeInlinePrivacyValueAsync(target, value, cfg, req, trace
2774
2633
  };
2775
2634
  }
2776
2635
  }
2636
+ function materializeTracePrivacyValue(target, value, cfg, req, trace, masking, privacy) {
2637
+ if (value === undefined || shouldCapturePrivacyRaw(cfg)) {
2638
+ return value;
2639
+ }
2640
+ return applyPrivacyThenMask(target, value, cfg, req, trace, masking, privacy);
2641
+ }
2642
+ async function materializeTracePrivacyValueAsync(target, value, cfg, req, trace, masking, privacy) {
2643
+ if (value === undefined || shouldCapturePrivacyRaw(cfg)) {
2644
+ return value;
2645
+ }
2646
+ return applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking, privacy);
2647
+ }
2777
2648
  function maskWhenRequiresTrace(when) {
2778
2649
  return Boolean(when.fn ||
2779
2650
  when.functionName ||
@@ -3035,6 +2906,130 @@ function stableSerializeTraceValue(value) {
3035
2906
  return null;
3036
2907
  }
3037
2908
  }
2909
+ function boundedSerializedTraceValueLength(value, limit) {
2910
+ let length = 0;
2911
+ const seen = new WeakSet();
2912
+ const add = (amount) => {
2913
+ length += amount;
2914
+ if (length > limit) {
2915
+ throw TRACE_VALUE_SIZE_EXCEEDED;
2916
+ }
2917
+ };
2918
+ const addString = (input) => {
2919
+ if (length + input.length + 2 > limit) {
2920
+ throw TRACE_VALUE_SIZE_EXCEEDED;
2921
+ }
2922
+ add(JSON.stringify(input).length);
2923
+ };
2924
+ const walk = (input) => {
2925
+ if (input === undefined) {
2926
+ addString('__undefined__');
2927
+ return;
2928
+ }
2929
+ if (input === null) {
2930
+ add(4);
2931
+ return;
2932
+ }
2933
+ const inputType = typeof input;
2934
+ if (inputType === 'string') {
2935
+ addString(input);
2936
+ return;
2937
+ }
2938
+ if (inputType === 'number' || inputType === 'boolean') {
2939
+ add(JSON.stringify(input)?.length ?? String(input).length);
2940
+ return;
2941
+ }
2942
+ if (inputType === 'bigint') {
2943
+ addString(`${input.toString()}n`);
2944
+ return;
2945
+ }
2946
+ if (inputType === 'symbol') {
2947
+ addString(input.toString());
2948
+ return;
2949
+ }
2950
+ if (inputType === 'function') {
2951
+ addString('[Function]');
2952
+ return;
2953
+ }
2954
+ if (Array.isArray(input)) {
2955
+ add(1);
2956
+ input.forEach((item, index) => {
2957
+ if (index > 0)
2958
+ add(1);
2959
+ walk(item);
2960
+ });
2961
+ add(1);
2962
+ return;
2963
+ }
2964
+ if (input instanceof Map) {
2965
+ add(1);
2966
+ let index = 0;
2967
+ for (const [key, item] of input.entries()) {
2968
+ if (index > 0)
2969
+ add(1);
2970
+ addString(normalizeCollectionKeyForCapture(key, index));
2971
+ add(1);
2972
+ walk(item);
2973
+ index += 1;
2974
+ }
2975
+ add(1);
2976
+ return;
2977
+ }
2978
+ if (input instanceof Set) {
2979
+ add(1);
2980
+ let index = 0;
2981
+ for (const item of input.values()) {
2982
+ if (index > 0)
2983
+ add(1);
2984
+ walk(item);
2985
+ index += 1;
2986
+ }
2987
+ add(1);
2988
+ return;
2989
+ }
2990
+ if (input instanceof WeakMap || input instanceof WeakSet) {
2991
+ addString(`[${getCtorName(input) || 'WeakCollection'}: uninspectable]`);
2992
+ return;
2993
+ }
2994
+ if (input && typeof input === 'object') {
2995
+ if (seen.has(input)) {
2996
+ addString('[Circular]');
2997
+ return;
2998
+ }
2999
+ seen.add(input);
3000
+ add(1);
3001
+ let index = 0;
3002
+ for (const key of Object.keys(input).sort()) {
3003
+ if (index > 0)
3004
+ add(1);
3005
+ addString(key);
3006
+ add(1);
3007
+ try {
3008
+ walk(input[key]);
3009
+ }
3010
+ catch (err) {
3011
+ if (err === TRACE_VALUE_SIZE_EXCEEDED)
3012
+ throw err;
3013
+ addString(`[Cannot serialize: ${err?.message || 'unknown error'}]`);
3014
+ }
3015
+ index += 1;
3016
+ }
3017
+ add(1);
3018
+ return;
3019
+ }
3020
+ addString(String(input));
3021
+ };
3022
+ try {
3023
+ walk(value);
3024
+ return { exceeded: false, length };
3025
+ }
3026
+ catch (err) {
3027
+ if (err === TRACE_VALUE_SIZE_EXCEEDED) {
3028
+ return { exceeded: true, length };
3029
+ }
3030
+ return { exceeded: true, length: limit + 1 };
3031
+ }
3032
+ }
3038
3033
  function normalizeCollectionKeyForCapture(value, index) {
3039
3034
  const mongoId = coerceMongoId(value);
3040
3035
  if (mongoId !== null)
@@ -3496,12 +3491,6 @@ function createCapturedValueEntry(params) {
3496
3491
  maxItems: TRACE_FULL_VALUE_MAX_ITEMS,
3497
3492
  maxString: TRACE_FULL_VALUE_MAX_STRING,
3498
3493
  };
3499
- const fallbackLimits = {
3500
- maxDepth: TRACE_FULL_VALUE_FALLBACK_DEPTH,
3501
- maxKeys: TRACE_FULL_VALUE_FALLBACK_KEYS,
3502
- maxItems: TRACE_FULL_VALUE_FALLBACK_ITEMS,
3503
- maxString: TRACE_FULL_VALUE_FALLBACK_STRING,
3504
- };
3505
3494
  const buildMaskedCandidate = (limits) => {
3506
3495
  const projected = params.target === 'request.headers'
3507
3496
  ? sanitizeHeaders(params.rawValue, params.capture.captureHeaders)
@@ -3519,14 +3508,6 @@ function createCapturedValueEntry(params) {
3519
3508
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
3520
3509
  return undefined;
3521
3510
  }
3522
- let truncatedForStorage = false;
3523
- if (candidateSerialized.length > TRACE_FULL_VALUE_MAX_SERIALIZED_CHARS) {
3524
- const fallback = buildMaskedCandidate(fallbackLimits);
3525
- if (fallback !== undefined && hasMeaningfulFullValue(fallback)) {
3526
- candidate = fallback;
3527
- truncatedForStorage = true;
3528
- }
3529
- }
3530
3511
  const valueId = (0, crypto_1.randomUUID)();
3531
3512
  const projectedKind = candidate && typeof candidate === 'object'
3532
3513
  ? (candidate.__kind || candidate.__type)
@@ -3535,13 +3516,13 @@ function createCapturedValueEntry(params) {
3535
3516
  id: valueId,
3536
3517
  target: params.target,
3537
3518
  value: candidate,
3538
- truncatedForStorage,
3519
+ truncatedForStorage: false,
3539
3520
  projectedKind: typeof projectedKind === 'string' ? projectedKind : undefined,
3540
3521
  ...(params.metadata ?? {}),
3541
3522
  };
3542
3523
  return {
3543
3524
  captureRef: {
3544
- status: truncatedForStorage ? 'truncated-for-storage' : 'stored',
3525
+ status: 'stored',
3545
3526
  valueId,
3546
3527
  projectedKind: entry.projectedKind,
3547
3528
  },
@@ -3557,12 +3538,6 @@ async function createCapturedValueEntryAsync(params) {
3557
3538
  maxItems: TRACE_FULL_VALUE_MAX_ITEMS,
3558
3539
  maxString: TRACE_FULL_VALUE_MAX_STRING,
3559
3540
  };
3560
- const fallbackLimits = {
3561
- maxDepth: TRACE_FULL_VALUE_FALLBACK_DEPTH,
3562
- maxKeys: TRACE_FULL_VALUE_FALLBACK_KEYS,
3563
- maxItems: TRACE_FULL_VALUE_FALLBACK_ITEMS,
3564
- maxString: TRACE_FULL_VALUE_FALLBACK_STRING,
3565
- };
3566
3541
  const buildMaskedCandidate = async (limits) => {
3567
3542
  const projected = params.target === 'request.headers'
3568
3543
  ? sanitizeHeaders(params.rawValue, params.capture.captureHeaders)
@@ -3580,14 +3555,6 @@ async function createCapturedValueEntryAsync(params) {
3580
3555
  if (!candidateSerialized || candidateSerialized === previewSerialized) {
3581
3556
  return undefined;
3582
3557
  }
3583
- let truncatedForStorage = false;
3584
- if (candidateSerialized.length > TRACE_FULL_VALUE_MAX_SERIALIZED_CHARS) {
3585
- const fallback = await buildMaskedCandidate(fallbackLimits);
3586
- if (fallback !== undefined && hasMeaningfulFullValue(fallback)) {
3587
- candidate = fallback;
3588
- truncatedForStorage = true;
3589
- }
3590
- }
3591
3558
  const valueId = (0, crypto_1.randomUUID)();
3592
3559
  const projectedKind = candidate && typeof candidate === 'object'
3593
3560
  ? (candidate.__kind || candidate.__type)
@@ -3596,83 +3563,19 @@ async function createCapturedValueEntryAsync(params) {
3596
3563
  id: valueId,
3597
3564
  target: params.target,
3598
3565
  value: candidate,
3599
- truncatedForStorage,
3566
+ truncatedForStorage: false,
3600
3567
  projectedKind: typeof projectedKind === 'string' ? projectedKind : undefined,
3601
3568
  ...(params.metadata ?? {}),
3602
3569
  };
3603
3570
  return {
3604
3571
  captureRef: {
3605
- status: truncatedForStorage ? 'truncated-for-storage' : 'stored',
3572
+ status: 'stored',
3606
3573
  valueId,
3607
3574
  projectedKind: entry.projectedKind,
3608
3575
  },
3609
3576
  entry,
3610
3577
  };
3611
3578
  }
3612
- function maybeCaptureFullTraceValue(params) {
3613
- const result = createCapturedValueEntry({
3614
- target: params.target,
3615
- rawValue: params.rawValue,
3616
- previewValue: params.previewValue,
3617
- capture: params.capture,
3618
- metadata: {
3619
- fn: params.event.fn,
3620
- file: params.event.file,
3621
- line: params.event.line ?? null,
3622
- spanId: params.event.spanId ?? null,
3623
- parentSpanId: params.event.parentSpanId ?? null,
3624
- },
3625
- });
3626
- if (!result)
3627
- return undefined;
3628
- if (result.entry) {
3629
- const existing = params.event[TRACE_FULL_VALUE_ENTRY_SYMBOL];
3630
- if (Array.isArray(existing)) {
3631
- existing.push(result.entry);
3632
- }
3633
- else {
3634
- Object.defineProperty(params.event, TRACE_FULL_VALUE_ENTRY_SYMBOL, {
3635
- value: [result.entry],
3636
- enumerable: false,
3637
- configurable: true,
3638
- writable: true,
3639
- });
3640
- }
3641
- }
3642
- return result.captureRef;
3643
- }
3644
- async function maybeCaptureFullTraceValueAsync(params) {
3645
- const result = await createCapturedValueEntryAsync({
3646
- target: params.target,
3647
- rawValue: params.rawValue,
3648
- previewValue: params.previewValue,
3649
- capture: params.capture,
3650
- metadata: {
3651
- fn: params.event.fn,
3652
- file: params.event.file,
3653
- line: params.event.line ?? null,
3654
- spanId: params.event.spanId ?? null,
3655
- parentSpanId: params.event.parentSpanId ?? null,
3656
- },
3657
- });
3658
- if (!result)
3659
- return undefined;
3660
- if (result.entry) {
3661
- const existing = params.event[TRACE_FULL_VALUE_ENTRY_SYMBOL];
3662
- if (Array.isArray(existing)) {
3663
- existing.push(result.entry);
3664
- }
3665
- else {
3666
- Object.defineProperty(params.event, TRACE_FULL_VALUE_ENTRY_SYMBOL, {
3667
- value: [result.entry],
3668
- enumerable: false,
3669
- configurable: true,
3670
- writable: true,
3671
- });
3672
- }
3673
- }
3674
- return result.captureRef;
3675
- }
3676
3579
  function maybeCaptureRequestValue(params, sink) {
3677
3580
  const result = createCapturedValueEntry(params);
3678
3581
  if (!result)
@@ -3933,7 +3836,8 @@ function reproMiddleware(cfg) {
3933
3836
  if (finished) {
3934
3837
  scheduleIdleFlush();
3935
3838
  }
3936
- if (ev.args !== undefined || ev.returnValue !== undefined || ev.error !== undefined) {
3839
+ const hasErrorPayload = hasMeaningfulRawTraceError(ev.error);
3840
+ if (ev.args !== undefined || ev.returnValue !== undefined || hasErrorPayload) {
3937
3841
  evt.__reproPending = {
3938
3842
  ...(ev.args !== undefined
3939
3843
  ? {
@@ -3945,7 +3849,7 @@ function reproMiddleware(cfg) {
3945
3849
  returnValueRaw: ev.returnValue,
3946
3850
  }
3947
3851
  : {}),
3948
- ...(ev.error !== undefined
3852
+ ...(hasErrorPayload
3949
3853
  ? {
3950
3854
  errorRaw: ev.error,
3951
3855
  }
@@ -4010,12 +3914,17 @@ function reproMiddleware(cfg) {
4010
3914
  };
4011
3915
  })();
4012
3916
  const activePrivacy = resolvePrivacy();
4013
- const requestBodyMaterialization = await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(req.body), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
3917
+ const requestBodyRaw = req.body;
3918
+ const requestBodyMaterialization = limitRawInlinePrivacyValue('request.body', requestBodyRaw)
3919
+ ?? await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(requestBodyRaw), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4014
3920
  const requestBody = requestBodyMaterialization.value;
4015
3921
  const requestParams = await applyPrivacyThenMaskAsync('request.params', sanitizeRequestSnapshot(req.params), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4016
3922
  const requestQuery = await applyPrivacyThenMaskAsync('request.query', sanitizeRequestSnapshot(req.query), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4017
3923
  const maskedHeaders = await applyPrivacyThenMaskAsync('request.headers', requestHeaders, cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4018
- const responseBodyMaterialization = await materializeInlinePrivacyValueAsync('response.body', capturedBody === undefined ? undefined : sanitizeRequestSnapshot(capturedBody), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
3924
+ const responseBodyMaterialization = capturedBody === undefined
3925
+ ? { value: undefined }
3926
+ : limitRawInlinePrivacyValue('response.body', capturedBody)
3927
+ ?? await materializeInlinePrivacyValueAsync('response.body', sanitizeRequestSnapshot(capturedBody), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4019
3928
  const responseBody = responseBodyMaterialization.value;
4020
3929
  const requestValueEntries = [];
4021
3930
  const bodyValueCapture = requestBodyMaterialization.skipped
@@ -4567,7 +4476,11 @@ function sanitizeResultForMeta(value, options = {}) {
4567
4476
  if (typeof value === 'function')
4568
4477
  return undefined;
4569
4478
  try {
4570
- return sanitizeTraceValue(value, 0, new WeakMap(), options);
4479
+ return sanitizeTraceValue(value, 0, new WeakMap(), {
4480
+ preserveLongStrings: true,
4481
+ disableTruncation: true,
4482
+ ...options,
4483
+ });
4571
4484
  }
4572
4485
  catch {
4573
4486
  const fallback = safeJson(value);
@@ -4653,7 +4566,7 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
4653
4566
  const afterPreview = afterMaterialization.value;
4654
4567
  const resultMetaMaterialization = await materializeInlinePrivacyValueAsync('db.resultMeta', resultMetaSource ?? undefined, cfg, maskReq, null, null, privacy, dbContext);
4655
4568
  const resultMetaPreviewRaw = resultMetaMaterialization.value;
4656
- const resultMetaPreview = sanitizeTraceValue(resultMetaPreviewRaw);
4569
+ const resultMetaPreview = sanitizeMaterializedTraceValue(resultMetaPreviewRaw);
4657
4570
  const errorMaterialization = await materializeInlinePrivacyValueAsync('db.error', payload.error ?? undefined, cfg, maskReq, null, null, privacy, dbContext);
4658
4571
  const errorPreview = errorMaterialization.value;
4659
4572
  const capture = {
@@ -5049,23 +4962,6 @@ function normalizeKafkaHeadersForPayload(headers) {
5049
4962
  }
5050
4963
  return out;
5051
4964
  }
5052
- function previewKafkaValue(value, maxLength = 120) {
5053
- if (value === null || value === undefined)
5054
- return null;
5055
- let text;
5056
- if (typeof value === 'string') {
5057
- text = value;
5058
- }
5059
- else if (Buffer.isBuffer(value)) {
5060
- text = value.toString('utf8');
5061
- }
5062
- else {
5063
- return null;
5064
- }
5065
- if (text.length <= maxLength)
5066
- return text;
5067
- return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
5068
- }
5069
4965
  function kafkaValueSize(value) {
5070
4966
  if (value === null || value === undefined)
5071
4967
  return null;
@@ -5073,8 +4969,93 @@ function kafkaValueSize(value) {
5073
4969
  return Buffer.byteLength(value, 'utf8');
5074
4970
  if (Buffer.isBuffer(value))
5075
4971
  return value.length;
4972
+ if (value instanceof Uint8Array)
4973
+ return value.byteLength;
5076
4974
  return null;
5077
4975
  }
4976
+ function decodeKafkaValue(value) {
4977
+ if (value === null || value === undefined)
4978
+ return null;
4979
+ if (typeof value === 'string')
4980
+ return value;
4981
+ if (Buffer.isBuffer(value))
4982
+ return value.toString('utf8');
4983
+ if (value instanceof Uint8Array)
4984
+ return Buffer.from(value).toString('utf8');
4985
+ if (typeof value === 'number' || typeof value === 'boolean')
4986
+ return value;
4987
+ return sanitizeResultForMeta(value);
4988
+ }
4989
+ function parseKafkaJsonString(value) {
4990
+ const trimmed = value.trim();
4991
+ if (!trimmed || !'{['.includes(trimmed[0])) {
4992
+ return { parsed: false, value };
4993
+ }
4994
+ try {
4995
+ return { parsed: true, value: JSON.parse(value) };
4996
+ }
4997
+ catch {
4998
+ return { parsed: false, value };
4999
+ }
5000
+ }
5001
+ function decodeKafkaMessageValue(value) {
5002
+ const decoded = decodeKafkaValue(value);
5003
+ if (typeof decoded !== 'string') {
5004
+ return {
5005
+ value: decoded,
5006
+ valueEncoding: decoded === null ? null : typeof decoded,
5007
+ };
5008
+ }
5009
+ const parsed = parseKafkaJsonString(decoded);
5010
+ if (parsed.parsed) {
5011
+ return {
5012
+ value: parsed.value,
5013
+ rawValue: decoded,
5014
+ valueEncoding: 'json',
5015
+ };
5016
+ }
5017
+ return {
5018
+ value: decoded,
5019
+ valueEncoding: 'utf8',
5020
+ };
5021
+ }
5022
+ function buildKafkaMessagePayload(message) {
5023
+ const msgObj = message && typeof message === 'object' ? message : { value: message };
5024
+ const decoded = decodeKafkaMessageValue(msgObj.value);
5025
+ const payload = {
5026
+ key: decodeKafkaValue(msgObj.key),
5027
+ value: decoded.value,
5028
+ valueEncoding: decoded.valueEncoding,
5029
+ valueBytes: kafkaValueSize(msgObj.value),
5030
+ headers: normalizeKafkaHeadersForPayload(msgObj.headers),
5031
+ };
5032
+ if (decoded.rawValue !== undefined) {
5033
+ payload.rawValue = decoded.rawValue;
5034
+ }
5035
+ if (msgObj.partition !== undefined) {
5036
+ payload.partition = msgObj.partition;
5037
+ }
5038
+ if (msgObj.timestamp !== undefined) {
5039
+ payload.timestamp = msgObj.timestamp;
5040
+ }
5041
+ if (msgObj.offset !== undefined) {
5042
+ payload.offset = msgObj.offset;
5043
+ }
5044
+ return payload;
5045
+ }
5046
+ function cloneKafkaTelemetryValue(value) {
5047
+ if (value === undefined || value === null)
5048
+ return value;
5049
+ try {
5050
+ return JSON.parse(JSON.stringify(value));
5051
+ }
5052
+ catch {
5053
+ return value;
5054
+ }
5055
+ }
5056
+ async function materializeKafkaRequestBody(cfg, maskReq, body) {
5057
+ return materializeInlinePrivacyValueAsync('request.body', sanitizeTraceValueForPrivacy(body), cfg, maskReq, null, normalizeMaskingConfig(cfg.masking), getRuntimePrivacyState(cfg).policy ?? null);
5058
+ }
5078
5059
  function summarizeKafkaError(error) {
5079
5060
  const message = typeof error?.message === 'string'
5080
5061
  ? error.message
@@ -5150,6 +5131,7 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
5150
5131
  };
5151
5132
  if (shouldDropTraceEvent(candidate))
5152
5133
  return;
5134
+ const omitJsonBuiltinValues = shouldOmitJsonBuiltinTraceValues(candidate);
5153
5135
  const evt = {
5154
5136
  t: alignTimestamp(typeof raw.t === 'number' ? raw.t : Date.now()),
5155
5137
  type: raw.type,
@@ -5163,14 +5145,14 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
5163
5145
  };
5164
5146
  const privacy = getRuntimePrivacyState(cfg).policy ?? null;
5165
5147
  const masking = normalizeMaskingConfig(cfg.masking);
5166
- if (raw.args !== undefined) {
5167
- evt.args = sanitizeTraceValue(applyPrivacyThenMask('trace.args', sanitizeTraceArgsForPrivacy(raw.args), cfg, maskReq, candidate, masking, privacy));
5148
+ if (!omitJsonBuiltinValues && raw.args !== undefined) {
5149
+ evt.args = sanitizeMaterializedTraceValue(materializeTracePrivacyValue('trace.args', sanitizeTraceArgsForPrivacy(raw.args), cfg, maskReq, candidate, masking, privacy));
5168
5150
  }
5169
- if (raw.returnValue !== undefined) {
5170
- evt.returnValue = sanitizeTraceValue(applyPrivacyThenMask('trace.returnValue', sanitizeTraceValueForPrivacy(raw.returnValue), cfg, maskReq, candidate, masking, privacy));
5151
+ if (!omitJsonBuiltinValues && raw.returnValue !== undefined) {
5152
+ evt.returnValue = sanitizeMaterializedTraceValue(materializeTracePrivacyValue('trace.returnValue', sanitizeTraceValueForPrivacy(raw.returnValue), cfg, maskReq, candidate, masking, privacy));
5171
5153
  }
5172
- if (raw.error !== undefined) {
5173
- evt.error = sanitizeTraceValue(applyPrivacyThenMask('trace.error', sanitizeTraceValueForPrivacy(raw.error), cfg, maskReq, candidate, masking, privacy));
5154
+ if (hasMeaningfulRawTraceError(raw.error)) {
5155
+ evt.error = sanitizeMaterializedTraceValue(materializeTracePrivacyValue('trace.error', sanitizeTraceValueForPrivacy(raw.error), cfg, maskReq, candidate, masking, privacy));
5174
5156
  }
5175
5157
  if (raw.threw !== undefined)
5176
5158
  evt.threw = raw.threw === true;
@@ -5197,6 +5179,7 @@ function buildPendingKafkaTraceEvent(raw) {
5197
5179
  };
5198
5180
  if (shouldDropTraceEvent(candidate))
5199
5181
  return null;
5182
+ const omitJsonBuiltinValues = shouldOmitJsonBuiltinTraceValues(candidate);
5200
5183
  const evt = {
5201
5184
  t: alignTimestamp(typeof raw.t === 'number' ? raw.t : Date.now()),
5202
5185
  type: raw.type,
@@ -5211,19 +5194,20 @@ function buildPendingKafkaTraceEvent(raw) {
5211
5194
  if (typeof raw.sourceFile === 'string' && raw.sourceFile) {
5212
5195
  evt.__reproSourceFile = String(raw.sourceFile);
5213
5196
  }
5214
- if (raw.args !== undefined || raw.returnValue !== undefined || raw.error !== undefined) {
5197
+ const hasErrorPayload = hasMeaningfulRawTraceError(raw.error);
5198
+ if ((!omitJsonBuiltinValues && (raw.args !== undefined || raw.returnValue !== undefined)) || hasErrorPayload) {
5215
5199
  evt.__reproPending = {
5216
- ...(raw.args !== undefined
5200
+ ...(!omitJsonBuiltinValues && raw.args !== undefined
5217
5201
  ? {
5218
5202
  argsRaw: normalizeRawTraceArgs(raw.args),
5219
5203
  }
5220
5204
  : {}),
5221
- ...(raw.returnValue !== undefined
5205
+ ...(!omitJsonBuiltinValues && raw.returnValue !== undefined
5222
5206
  ? {
5223
5207
  returnValueRaw: raw.returnValue,
5224
5208
  }
5225
5209
  : {}),
5226
- ...(raw.error !== undefined
5210
+ ...(hasErrorPayload
5227
5211
  ? {
5228
5212
  errorRaw: raw.error,
5229
5213
  }
@@ -5351,14 +5335,26 @@ function patchKafkaProducerInstance(producer, cfg) {
5351
5335
  finally {
5352
5336
  if (!sid)
5353
5337
  return;
5354
- const previewKeys = propagatedMessages
5355
- .slice(0, 10)
5356
- .map((message) => previewKafkaValue(message?.key))
5357
- .filter((value) => value !== null);
5358
5338
  const totalValueBytes = propagatedMessages.reduce((sum, message) => {
5359
5339
  const size = kafkaValueSize(message?.value);
5360
5340
  return sum + (size ?? 0);
5361
5341
  }, 0);
5342
+ const requestKey = `KAFKA_PUBLISH ${topic}`;
5343
+ const maskReq = {
5344
+ method: 'KAFKA_PUBLISH',
5345
+ path: `kafka://${topic}`,
5346
+ key: requestKey,
5347
+ };
5348
+ const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, {
5349
+ transport: 'kafka',
5350
+ topic,
5351
+ messageCount: propagatedMessages.length,
5352
+ totalValueBytes,
5353
+ messages: propagatedMessages.map((message) => buildKafkaMessagePayload(message)),
5354
+ traceId: span?.traceId ?? null,
5355
+ spanId: span?.spanId ?? null,
5356
+ parentSpanId: span?.parentSpanId ?? null,
5357
+ });
5362
5358
  const requestPayload = {
5363
5359
  rid: publishRid,
5364
5360
  method: 'KAFKA_PUBLISH',
@@ -5366,22 +5362,14 @@ function patchKafkaProducerInstance(producer, cfg) {
5366
5362
  path: `kafka://${topic}`,
5367
5363
  status: threw ? 500 : 200,
5368
5364
  durMs: Date.now() - t0,
5369
- key: `KAFKA_PUBLISH ${topic}`,
5365
+ key: requestKey,
5370
5366
  headers: {
5371
5367
  transport: 'kafka',
5372
5368
  topic,
5373
5369
  messageCount: propagatedMessages.length,
5374
5370
  },
5375
- body: {
5376
- transport: 'kafka',
5377
- topic,
5378
- messageCount: propagatedMessages.length,
5379
- totalValueBytes,
5380
- keyPreview: previewKeys,
5381
- traceId: span?.traceId ?? null,
5382
- spanId: span?.spanId ?? null,
5383
- parentSpanId: span?.parentSpanId ?? null,
5384
- },
5371
+ body: bodyMaterialization.value,
5372
+ bodyMaterialization: bodyMaterialization.skipped,
5385
5373
  respBody: threw
5386
5374
  ? { error: summarizeKafkaError(error) }
5387
5375
  : summarizeKafkaProducerResult(result),
@@ -5466,29 +5454,51 @@ function patchKafkaProducerInstance(producer, cfg) {
5466
5454
  const count = Array.isArray(entry.messages) ? entry.messages.length : 0;
5467
5455
  return sum + count;
5468
5456
  }, 0);
5457
+ const totalValueBytes = patchedTopicMessages.reduce((sum, entry) => {
5458
+ const messages = Array.isArray(entry.messages) ? entry.messages : [];
5459
+ return sum + messages.reduce((messageSum, message) => {
5460
+ const size = kafkaValueSize(message?.value);
5461
+ return messageSum + (size ?? 0);
5462
+ }, 0);
5463
+ }, 0);
5464
+ const requestKey = uniqueTopics.length === 1
5465
+ ? `KAFKA_PUBLISH_BATCH ${uniqueTopics[0]}`
5466
+ : 'KAFKA_PUBLISH_BATCH';
5467
+ const requestPath = uniqueTopics.length === 1 ? `kafka://${uniqueTopics[0]}` : 'kafka://batch';
5468
+ const maskReq = {
5469
+ method: 'KAFKA_PUBLISH_BATCH',
5470
+ path: requestPath,
5471
+ key: requestKey,
5472
+ };
5473
+ const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, {
5474
+ transport: 'kafka',
5475
+ topics: uniqueTopics,
5476
+ topicMessages: patchedTopicMessages.map((entry) => ({
5477
+ topic: sanitizeKafkaTopic(entry.topic),
5478
+ messages: (Array.isArray(entry.messages) ? entry.messages : [])
5479
+ .map((message) => buildKafkaMessagePayload(message)),
5480
+ })),
5481
+ messageCount,
5482
+ totalValueBytes,
5483
+ traceId: span?.traceId ?? null,
5484
+ spanId: span?.spanId ?? null,
5485
+ parentSpanId: span?.parentSpanId ?? null,
5486
+ });
5469
5487
  const requestPayload = {
5470
5488
  rid: publishRid,
5471
5489
  method: 'KAFKA_PUBLISH_BATCH',
5472
- url: uniqueTopics.length === 1 ? `kafka://${uniqueTopics[0]}` : 'kafka://batch',
5473
- path: uniqueTopics.length === 1 ? `kafka://${uniqueTopics[0]}` : 'kafka://batch',
5490
+ url: requestPath,
5491
+ path: requestPath,
5474
5492
  status: threw ? 500 : 200,
5475
5493
  durMs: Date.now() - t0,
5476
- key: uniqueTopics.length === 1
5477
- ? `KAFKA_PUBLISH_BATCH ${uniqueTopics[0]}`
5478
- : 'KAFKA_PUBLISH_BATCH',
5494
+ key: requestKey,
5479
5495
  headers: {
5480
5496
  transport: 'kafka',
5481
5497
  topicCount: uniqueTopics.length,
5482
5498
  messageCount,
5483
5499
  },
5484
- body: {
5485
- transport: 'kafka',
5486
- topics: uniqueTopics,
5487
- messageCount,
5488
- traceId: span?.traceId ?? null,
5489
- spanId: span?.spanId ?? null,
5490
- parentSpanId: span?.parentSpanId ?? null,
5491
- },
5500
+ body: bodyMaterialization.value,
5501
+ bodyMaterialization: bodyMaterialization.skipped,
5492
5502
  respBody: threw
5493
5503
  ? { error: summarizeKafkaError(error) }
5494
5504
  : summarizeKafkaProducerResult(result),
@@ -5574,6 +5584,24 @@ function wrapKafkaEachMessageHandler(eachMessage, cfg, consumer) {
5574
5584
  finally {
5575
5585
  if (!sid)
5576
5586
  return;
5587
+ const messagePayload = buildKafkaMessagePayload(message);
5588
+ const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, {
5589
+ transport: 'kafka',
5590
+ topic,
5591
+ partition,
5592
+ offset: message?.offset ?? null,
5593
+ timestamp: message?.timestamp ?? null,
5594
+ groupId: consumer?.__repro_group_id ?? null,
5595
+ parentRequestRid: runtime.requestRid,
5596
+ parentTraceId: runtime.traceId,
5597
+ parentSpanId: runtime.parentSpanId,
5598
+ messageKey: messagePayload.key,
5599
+ value: cloneKafkaTelemetryValue(messagePayload.value),
5600
+ rawValue: messagePayload.rawValue,
5601
+ valueEncoding: messagePayload.valueEncoding,
5602
+ valueBytes: messagePayload.valueBytes,
5603
+ message: messagePayload,
5604
+ });
5577
5605
  const requestPayload = {
5578
5606
  rid: consumeRid,
5579
5607
  method: 'KAFKA_CONSUME',
@@ -5583,19 +5611,8 @@ function wrapKafkaEachMessageHandler(eachMessage, cfg, consumer) {
5583
5611
  durMs: Date.now() - t0,
5584
5612
  key: `KAFKA_CONSUME ${topic}`,
5585
5613
  headers: normalizeKafkaHeadersForPayload(message?.headers),
5586
- body: {
5587
- transport: 'kafka',
5588
- topic,
5589
- partition,
5590
- offset: message?.offset ?? null,
5591
- timestamp: message?.timestamp ?? null,
5592
- groupId: consumer?.__repro_group_id ?? null,
5593
- parentRequestRid: runtime.requestRid,
5594
- parentTraceId: runtime.traceId,
5595
- parentSpanId: runtime.parentSpanId,
5596
- messageKey: previewKafkaValue(message?.key),
5597
- valueBytes: kafkaValueSize(message?.value),
5598
- },
5614
+ body: bodyMaterialization.value,
5615
+ bodyMaterialization: bodyMaterialization.skipped,
5599
5616
  respBody: threw ? { error: summarizeKafkaError(error) } : undefined,
5600
5617
  };
5601
5618
  post(cfg, sid, {
@@ -5680,6 +5697,20 @@ function wrapKafkaEachBatchHandler(eachBatch, cfg, consumer) {
5680
5697
  return;
5681
5698
  const firstOffset = messages.length ? messages[0]?.offset ?? null : null;
5682
5699
  const lastOffset = messages.length ? messages[messages.length - 1]?.offset ?? null : null;
5700
+ const firstMessagePayload = firstMessage ? buildKafkaMessagePayload(firstMessage) : null;
5701
+ const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, {
5702
+ transport: 'kafka',
5703
+ topic,
5704
+ groupId: consumer?.__repro_group_id ?? null,
5705
+ messageCount: messages.length,
5706
+ firstOffset,
5707
+ lastOffset,
5708
+ parentRequestRid: runtime.requestRid,
5709
+ parentTraceId: runtime.traceId,
5710
+ parentSpanId: runtime.parentSpanId,
5711
+ firstMessageKey: firstMessagePayload?.key ?? null,
5712
+ messages: messages.map((message) => buildKafkaMessagePayload(message)),
5713
+ });
5683
5714
  const requestPayload = {
5684
5715
  rid: consumeRid,
5685
5716
  method: 'KAFKA_CONSUME_BATCH',
@@ -5689,18 +5720,8 @@ function wrapKafkaEachBatchHandler(eachBatch, cfg, consumer) {
5689
5720
  durMs: Date.now() - t0,
5690
5721
  key: `KAFKA_CONSUME_BATCH ${topic}`,
5691
5722
  headers: firstMessage ? normalizeKafkaHeadersForPayload(firstMessage.headers) : {},
5692
- body: {
5693
- transport: 'kafka',
5694
- topic,
5695
- groupId: consumer?.__repro_group_id ?? null,
5696
- messageCount: messages.length,
5697
- firstOffset,
5698
- lastOffset,
5699
- parentRequestRid: runtime.requestRid,
5700
- parentTraceId: runtime.traceId,
5701
- parentSpanId: runtime.parentSpanId,
5702
- firstMessageKey: firstMessage ? previewKafkaValue(firstMessage.key) : null,
5703
- },
5723
+ body: bodyMaterialization.value,
5724
+ bodyMaterialization: bodyMaterialization.skipped,
5704
5725
  respBody: threw ? { error: summarizeKafkaError(error) } : undefined,
5705
5726
  };
5706
5727
  post(cfg, sid, {
@@ -6531,5 +6552,7 @@ exports.__reproTestHooks = {
6531
6552
  getRuntimePrivacyState(cfg).policy = policy;
6532
6553
  },
6533
6554
  recordKafkaTraceEventAsyncForTest: recordKafkaTraceEventAsync,
6555
+ materializeInlinePrivacyValueAsyncForTest: materializeInlinePrivacyValueAsync,
6556
+ patchKafkaProducerInstanceForTest: patchKafkaProducerInstance,
6534
6557
  wrapKafkaEachMessageHandlerForTest: wrapKafkaEachMessageHandler,
6535
6558
  };