@reproapp/node-sdk 0.0.38 → 0.0.39

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
@@ -305,6 +305,9 @@ const KAFKA_HEADER_SESSION_ID = 'x-bug-session-id';
305
305
  const KAFKA_HEADER_ACTION_ID = 'x-bug-action-id';
306
306
  const DEFAULT_INGEST_BASE = 'http://localhost:8080';
307
307
  const DEFAULT_INGEST_PATH = '/v1/ingest/events';
308
+ const DEFAULT_LIVE_SESSION_DURATION_MS = 3 * 60000;
309
+ const DEFAULT_LIVE_SEGMENT_IDLE_MS = 15000;
310
+ const DEFAULT_LIVE_SEGMENT_DURATION_MS = DEFAULT_LIVE_SESSION_DURATION_MS;
308
311
  function escapeRx(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
309
312
  let __TRACER__ = null;
310
313
  let __TRACER_READY = false;
@@ -1681,6 +1684,299 @@ function toIngestConfig(cfg) {
1681
1684
  schemaVersion: cfg.schemaVersion,
1682
1685
  };
1683
1686
  }
1687
+ function normalizeLiveModeConfig(cfg) {
1688
+ const liveMode = cfg.liveMode;
1689
+ if (liveMode === true) {
1690
+ return { enabled: true };
1691
+ }
1692
+ if (!liveMode || typeof liveMode !== 'object') {
1693
+ return null;
1694
+ }
1695
+ if (liveMode.enabled === false) {
1696
+ return null;
1697
+ }
1698
+ return liveMode;
1699
+ }
1700
+ let backendLiveTimingOverrideForTest = null;
1701
+ function positiveInternalDuration(value, fallback) {
1702
+ const numeric = Number(value);
1703
+ if (!Number.isFinite(numeric) || numeric <= 0)
1704
+ return fallback;
1705
+ return Math.max(1, Math.trunc(numeric));
1706
+ }
1707
+ function getBackendLiveTiming() {
1708
+ return {
1709
+ sessionMaxDurationMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.sessionMaxDurationMs, DEFAULT_LIVE_SESSION_DURATION_MS),
1710
+ segmentMaxDurationMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.segmentMaxDurationMs, DEFAULT_LIVE_SEGMENT_DURATION_MS),
1711
+ segmentIdleMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.segmentIdleMs, DEFAULT_LIVE_SEGMENT_IDLE_MS),
1712
+ };
1713
+ }
1714
+ function normalizeLiveActorId(value) {
1715
+ if (value === undefined || value === null)
1716
+ return null;
1717
+ const normalized = String(value).trim();
1718
+ return normalized.length ? normalized : null;
1719
+ }
1720
+ function normalizeLiveActorLabels(value) {
1721
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1722
+ return undefined;
1723
+ const labels = {};
1724
+ for (const [key, rawValue] of Object.entries(value)) {
1725
+ const normalizedKey = key.trim();
1726
+ if (!normalizedKey || rawValue === undefined || rawValue === null)
1727
+ continue;
1728
+ const normalizedValue = String(rawValue).trim();
1729
+ if (normalizedValue)
1730
+ labels[normalizedKey] = normalizedValue;
1731
+ }
1732
+ return Object.keys(labels).length ? labels : undefined;
1733
+ }
1734
+ function newSessionId(base = Date.now()) {
1735
+ return `S_${Math.max(0, Math.trunc(base))}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
1736
+ }
1737
+ function newActionId(base = Date.now()) {
1738
+ return `A_${Math.max(0, Math.trunc(base))}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
1739
+ }
1740
+ function resolveLiveApiBase(cfg, liveMode) {
1741
+ const env = typeof process !== 'undefined' ? process?.env : undefined;
1742
+ const raw = liveMode?.apiBase ||
1743
+ cfg.apiBase ||
1744
+ env?.REPRO_API_BASE_URL ||
1745
+ env?.REPRO_API_BASE ||
1746
+ cfg.privacy?.apiBase ||
1747
+ null;
1748
+ if (typeof raw !== 'string' || !raw.trim())
1749
+ return null;
1750
+ return raw.trim().replace(/\/+$/, '');
1751
+ }
1752
+ function liveApiHeaders(cfg) {
1753
+ const headers = {
1754
+ 'Content-Type': 'application/json',
1755
+ 'X-App-Id': cfg.appId,
1756
+ };
1757
+ if (cfg.appSecret) {
1758
+ headers['X-App-Secret'] = cfg.appSecret;
1759
+ }
1760
+ if (cfg.appName) {
1761
+ headers['X-App-Name'] = cfg.appName;
1762
+ }
1763
+ return headers;
1764
+ }
1765
+ async function postLiveApi(cfg, liveMode, path, body) {
1766
+ await postLiveApiJson(cfg, liveMode, path, body);
1767
+ }
1768
+ async function postLiveApiJson(cfg, liveMode, path, body) {
1769
+ const apiBase = resolveLiveApiBase(cfg, liveMode);
1770
+ const fetchImpl = globalThis.fetch;
1771
+ if (!apiBase || !cfg.appId || !cfg.appSecret || typeof fetchImpl !== 'function') {
1772
+ return null;
1773
+ }
1774
+ try {
1775
+ const res = await fetchImpl(`${apiBase}${path}`, {
1776
+ method: 'POST',
1777
+ headers: liveApiHeaders(cfg),
1778
+ body: JSON.stringify(body),
1779
+ });
1780
+ if (!res?.ok) {
1781
+ return null;
1782
+ }
1783
+ try {
1784
+ return await res.json();
1785
+ }
1786
+ catch {
1787
+ return null;
1788
+ }
1789
+ }
1790
+ catch {
1791
+ // never break user code
1792
+ return null;
1793
+ }
1794
+ }
1795
+ function enqueueLiveSessionStart(cfg, liveMode, payload) {
1796
+ scheduleSdkBackgroundWork(() => postLiveApi(cfg, liveMode, '/v1/live-sessions', payload), { priority: 'high' });
1797
+ }
1798
+ function enqueueLiveSessionFinish(cfg, liveMode, sessionId) {
1799
+ scheduleSdkBackgroundWork(() => postLiveApi(cfg, liveMode, `/v1/live-sessions/${encodeURIComponent(sessionId)}/finish`, { notes: '' }), { priority: 'high' });
1800
+ }
1801
+ function emitBackendLiveSessionManifest(cfg, session, options) {
1802
+ post(cfg, session.sessionId, {
1803
+ entries: [{
1804
+ sessionCaptureManifest: {
1805
+ mode: 'backend-only',
1806
+ captureMode: 'backend-only',
1807
+ sessionKind: options.sessionKind,
1808
+ parentSessionId: options.parentSessionId ?? null,
1809
+ previousSessionId: options.previousSessionId ?? null,
1810
+ actorId: options.actorId,
1811
+ actorType: options.actorType,
1812
+ actorLabels: options.actorLabels,
1813
+ expected: {
1814
+ frontendActionDoc: 0,
1815
+ rrwebChunk: 0,
1816
+ frontendSessionEnd: 0,
1817
+ },
1818
+ closedAt: options.closedAt,
1819
+ },
1820
+ t: options.closedAt,
1821
+ }],
1822
+ }, { priority: 'high' });
1823
+ }
1824
+ function closeBackendLiveParent(cfg, liveMode, state, parent, closedAt) {
1825
+ if (parent.closed) {
1826
+ return;
1827
+ }
1828
+ parent.closed = true;
1829
+ if (parent.closeTimer) {
1830
+ try {
1831
+ clearTimeout(parent.closeTimer);
1832
+ }
1833
+ catch { }
1834
+ parent.closeTimer = null;
1835
+ }
1836
+ state.previousParentSessionId = parent.sessionId;
1837
+ if (state.parent === parent) {
1838
+ state.parent = null;
1839
+ }
1840
+ emitBackendLiveSessionManifest(cfg, parent, {
1841
+ sessionKind: 'parent',
1842
+ actorId: parent.actorId,
1843
+ actorType: parent.actorType,
1844
+ actorLabels: parent.actorLabels,
1845
+ previousSessionId: parent.previousSessionId,
1846
+ closedAt,
1847
+ });
1848
+ enqueueLiveSessionFinish(cfg, liveMode, parent.sessionId);
1849
+ }
1850
+ function scheduleBackendLiveParentClose(cfg, liveMode, state, parent) {
1851
+ if (parent.closed) {
1852
+ return;
1853
+ }
1854
+ if (parent.closeTimer) {
1855
+ try {
1856
+ clearTimeout(parent.closeTimer);
1857
+ }
1858
+ catch { }
1859
+ parent.closeTimer = null;
1860
+ }
1861
+ const now = Date.now();
1862
+ const activeChild = state.activeChild;
1863
+ const activeChildBelongsToParent = activeChild?.parentSessionId === parent.sessionId;
1864
+ const closeAt = activeChildBelongsToParent && now >= parent.expiresAt
1865
+ ? Math.max(parent.expiresAt, activeChild.closeDeadline, now + 1)
1866
+ : parent.expiresAt;
1867
+ parent.closeTimer = setTimeout(() => {
1868
+ parent.closeTimer = null;
1869
+ if (parent.closed || state.parent !== parent) {
1870
+ return;
1871
+ }
1872
+ const currentChild = state.activeChild;
1873
+ if (currentChild?.parentSessionId === parent.sessionId) {
1874
+ if (currentChild.requestsInFlight <= 0) {
1875
+ scheduleBackendLiveChildClose(cfg, liveMode, state, currentChild);
1876
+ }
1877
+ scheduleBackendLiveParentClose(cfg, liveMode, state, parent);
1878
+ return;
1879
+ }
1880
+ closeBackendLiveParent(cfg, liveMode, state, parent, Date.now());
1881
+ }, Math.max(0, closeAt - now));
1882
+ try {
1883
+ parent.closeTimer.unref?.();
1884
+ }
1885
+ catch { }
1886
+ }
1887
+ function scheduleBackendLiveChildClose(cfg, liveMode, state, child) {
1888
+ if (child.closeTimer) {
1889
+ try {
1890
+ clearTimeout(child.closeTimer);
1891
+ }
1892
+ catch { }
1893
+ child.closeTimer = null;
1894
+ }
1895
+ const delay = Math.max(0, child.closeDeadline - Date.now());
1896
+ child.closeTimer = setTimeout(() => {
1897
+ child.closeTimer = null;
1898
+ if (child.requestsInFlight > 0 || Date.now() < child.closeDeadline) {
1899
+ scheduleBackendLiveChildClose(cfg, liveMode, state, child);
1900
+ return;
1901
+ }
1902
+ if (state.activeChild === child) {
1903
+ state.activeChild = null;
1904
+ }
1905
+ state.previousChildSessionId = child.sessionId;
1906
+ const closedAt = Date.now();
1907
+ emitBackendLiveSessionManifest(cfg, child, {
1908
+ sessionKind: 'child',
1909
+ actorId: child.actorId,
1910
+ actorType: child.actorType,
1911
+ actorLabels: child.actorLabels,
1912
+ parentSessionId: child.parentSessionId,
1913
+ previousSessionId: child.previousSessionId,
1914
+ closedAt,
1915
+ });
1916
+ enqueueLiveSessionFinish(cfg, liveMode, child.sessionId);
1917
+ }, delay);
1918
+ try {
1919
+ child.closeTimer.unref?.();
1920
+ }
1921
+ catch { }
1922
+ }
1923
+ function releaseBackendLiveRequest(cfg, liveMode, state, child, finishedAt) {
1924
+ child.requestsInFlight = Math.max(0, child.requestsInFlight - 1);
1925
+ const idleMs = getBackendLiveTiming().segmentIdleMs;
1926
+ child.closeDeadline = Math.max(child.closeDeadline, finishedAt + idleMs);
1927
+ if (child.requestsInFlight <= 0) {
1928
+ scheduleBackendLiveChildClose(cfg, liveMode, state, child);
1929
+ }
1930
+ }
1931
+ async function resolveBackendLiveContext(cfg, req, requestStart) {
1932
+ const liveMode = normalizeLiveModeConfig(cfg);
1933
+ if (!liveMode?.backendOnly) {
1934
+ return null;
1935
+ }
1936
+ const method = String(req.method || 'GET').toUpperCase();
1937
+ if (method === 'OPTIONS' && !hasReproRequestCorrelation(req)) {
1938
+ return null;
1939
+ }
1940
+ const identity = await resolveLiveActorIdentity(cfg, req);
1941
+ if (!identity) {
1942
+ return null;
1943
+ }
1944
+ const { actorId, actorType, actorLabels } = identity;
1945
+ const claim = await postLiveApiJson(cfg, liveMode, '/v1/live-sessions/claim', {
1946
+ clientTime: requestStart,
1947
+ captureMode: 'backend-only',
1948
+ actorId,
1949
+ actorIds: [actorId],
1950
+ actorType,
1951
+ actorLabels,
1952
+ });
1953
+ const sid = normalizeLiveActorId(claim?.childSessionId) ??
1954
+ normalizeLiveActorId(claim?.sessionId);
1955
+ if (!sid) {
1956
+ return null;
1957
+ }
1958
+ const aid = normalizeLiveActorId(claim?.actionId) ?? newActionId(requestStart);
1959
+ const parentSessionId = normalizeLiveActorId(claim?.parentSessionId) ?? '';
1960
+ const liveChild = {
1961
+ sessionId: sid,
1962
+ parentSessionId,
1963
+ previousSessionId: null,
1964
+ actorId,
1965
+ actorType,
1966
+ actorLabels,
1967
+ startedAt: requestStart,
1968
+ expiresAt: requestStart,
1969
+ closeDeadline: requestStart,
1970
+ requestsInFlight: 0,
1971
+ closeTimer: null,
1972
+ };
1973
+ return {
1974
+ sid: liveChild.sessionId,
1975
+ aid,
1976
+ requestStart,
1977
+ child: liveChild,
1978
+ };
1979
+ }
1684
1980
  function resolveIngestUrl(cfg) {
1685
1981
  const envBase = typeof process !== 'undefined' ? process?.env?.REPRO_INGEST_BASE : undefined;
1686
1982
  const ingestBase = String(envBase || cfg.ingestBase || DEFAULT_INGEST_BASE).replace(/\/+$/, '');
@@ -1727,6 +2023,19 @@ function readRequestQueryNumber(req, name) {
1727
2023
  const parsed = Number(raw);
1728
2024
  return Number.isFinite(parsed) ? parsed : null;
1729
2025
  }
2026
+ function hasReproRequestCorrelation(req) {
2027
+ const headers = req.headers || {};
2028
+ if (hasHttpHeader(headers, KAFKA_HEADER_SESSION_ID) ||
2029
+ hasHttpHeader(headers, KAFKA_HEADER_ACTION_ID) ||
2030
+ hasHttpHeader(headers, KAFKA_HEADER_REQUEST_ID) ||
2031
+ hasHttpHeader(headers, KAFKA_HEADER_TRACE_ID) ||
2032
+ hasHttpHeader(headers, KAFKA_HEADER_SPAN_ID) ||
2033
+ hasHttpHeader(headers, KAFKA_HEADER_PARENT_SPAN_ID)) {
2034
+ return true;
2035
+ }
2036
+ return Boolean(readRequestQueryParam(req, REPRO_QUERY_SESSION_PARAM) ||
2037
+ readRequestQueryParam(req, REPRO_QUERY_ACTION_PARAM));
2038
+ }
1730
2039
  function stripReproQueryFromUrl(rawUrl) {
1731
2040
  if (!rawUrl || !rawUrl.includes('?')) {
1732
2041
  return rawUrl;
@@ -4131,6 +4440,72 @@ async function maybeCaptureDbValueFromCandidatesAsync(params, sink) {
4131
4440
  }
4132
4441
  return skippedRef;
4133
4442
  }
4443
+ function buildLiveActorArgs(cfg, req) {
4444
+ const rawUrl = String(req.originalUrl || req.url || '/');
4445
+ const path = rawUrl.split('?')[0] || '/';
4446
+ return {
4447
+ req,
4448
+ headers: req.headers,
4449
+ method: String(req.method || 'GET').toUpperCase(),
4450
+ url: rawUrl,
4451
+ path,
4452
+ ip: req.ip ?? req.socket?.remoteAddress ?? null,
4453
+ appId: cfg.appId,
4454
+ appName: cfg.appName,
4455
+ serviceName: cfg.serviceName,
4456
+ };
4457
+ }
4458
+ async function resolveLiveActorIdentity(cfg, req) {
4459
+ const liveMode = normalizeLiveModeConfig(cfg);
4460
+ if (!liveMode) {
4461
+ return null;
4462
+ }
4463
+ const args = buildLiveActorArgs(cfg, req);
4464
+ let rawActorId;
4465
+ try {
4466
+ rawActorId = liveMode.defineActorId
4467
+ ? await liveMode.defineActorId(args)
4468
+ : cfg.actorId;
4469
+ }
4470
+ catch {
4471
+ rawActorId = null;
4472
+ }
4473
+ const actorId = normalizeLiveActorId(rawActorId);
4474
+ if (!actorId) {
4475
+ return null;
4476
+ }
4477
+ const actorType = normalizeLiveActorId(liveMode.actorType) ?? cfg.actorType ?? 'user';
4478
+ const decision = {
4479
+ ...args,
4480
+ actorId,
4481
+ actorType,
4482
+ };
4483
+ if (liveMode.shouldCapture) {
4484
+ let shouldCapture = false;
4485
+ try {
4486
+ shouldCapture = (await liveMode.shouldCapture(decision)) === true;
4487
+ }
4488
+ catch {
4489
+ shouldCapture = false;
4490
+ }
4491
+ if (!shouldCapture) {
4492
+ return null;
4493
+ }
4494
+ }
4495
+ let rawLabels;
4496
+ try {
4497
+ rawLabels =
4498
+ typeof liveMode.actorLabels === 'function'
4499
+ ? await liveMode.actorLabels(decision)
4500
+ : liveMode.actorLabels;
4501
+ }
4502
+ catch {
4503
+ rawLabels = undefined;
4504
+ }
4505
+ const actorLabels = normalizeLiveActorLabels(rawLabels) ?? normalizeLiveActorLabels(cfg.actorLabels);
4506
+ return { actorId, actorType, actorLabels };
4507
+ }
4508
+ const backendLiveActors = new Map();
4134
4509
  function reproMiddleware(cfg) {
4135
4510
  const masking = normalizeMaskingConfig(cfg.masking);
4136
4511
  patchMongoDb(cfg);
@@ -4165,18 +4540,62 @@ function reproMiddleware(cfg) {
4165
4540
  clientRequestEnded = true;
4166
4541
  endClientRequest();
4167
4542
  };
4168
- const sid = req.headers['x-bug-session-id'] || querySid || '';
4169
- const aid = req.headers['x-bug-action-id'] || queryAid || '';
4543
+ const requestStartRaw = Date.now();
4544
+ const headerTs = readHeaderNumber(req.headers[REQUEST_START_HEADER]) ?? queryTs;
4545
+ const requestEpochMs = headerTs !== null ? headerTs : requestStartRaw;
4546
+ let backendLiveState = null;
4547
+ let backendLiveChild = null;
4548
+ const headerSid = req.headers['x-bug-session-id'] || '';
4549
+ const headerAid = req.headers['x-bug-action-id'] || '';
4550
+ const hasIncomingReproSession = Boolean(headerSid || querySid || headerAid || queryAid);
4551
+ const backendLiveContext = !hasIncomingReproSession
4552
+ ? await resolveBackendLiveContext(cfg, req, requestEpochMs)
4553
+ : null;
4554
+ const taggedActorIdentity = hasIncomingReproSession
4555
+ ? await resolveLiveActorIdentity(cfg, req)
4556
+ : null;
4557
+ if (backendLiveContext) {
4558
+ backendLiveState = backendLiveActors.get(backendLiveContext.child.actorId) ?? null;
4559
+ backendLiveChild = backendLiveContext.child;
4560
+ }
4561
+ const sid = headerSid || querySid || backendLiveContext?.sid || '';
4562
+ const aid = headerAid || queryAid || backendLiveContext?.aid || '';
4170
4563
  if (!sid || !aid) {
4171
4564
  res.on('finish', endTrackedClientRequest);
4172
4565
  res.on('close', endTrackedClientRequest);
4173
4566
  return next(); // only capture tagged requests
4174
4567
  }
4175
- const resolvePrivacy = () => getRuntimePrivacyState(cfg).policy ?? null;
4176
- const requestStartRaw = Date.now();
4177
- const headerTs = readHeaderNumber(req.headers[REQUEST_START_HEADER]) ?? queryTs;
4568
+ const captureCfg = backendLiveChild
4569
+ ? {
4570
+ ...cfg,
4571
+ actorId: backendLiveChild.actorId,
4572
+ actorType: backendLiveChild.actorType,
4573
+ actorLabels: backendLiveChild.actorLabels,
4574
+ }
4575
+ : taggedActorIdentity
4576
+ ? {
4577
+ ...cfg,
4578
+ actorId: taggedActorIdentity.actorId,
4579
+ actorType: taggedActorIdentity.actorType,
4580
+ actorLabels: taggedActorIdentity.actorLabels,
4581
+ }
4582
+ : cfg;
4583
+ if (taggedActorIdentity) {
4584
+ post(captureCfg, sid, {
4585
+ entries: [{
4586
+ actionId: aid,
4587
+ sessionActor: {
4588
+ actorId: taggedActorIdentity.actorId,
4589
+ actorType: taggedActorIdentity.actorType,
4590
+ actorLabels: taggedActorIdentity.actorLabels,
4591
+ source: 'node-sdk',
4592
+ },
4593
+ t: requestEpochMs,
4594
+ }],
4595
+ }, { priority: 'high' });
4596
+ }
4597
+ const resolvePrivacy = () => getRuntimePrivacyState(captureCfg).policy ?? null;
4178
4598
  const clockSkewMs = headerTs !== null ? headerTs - requestStartRaw : 0;
4179
- const requestEpochMs = headerTs !== null ? headerTs : requestStartRaw + clockSkewMs;
4180
4599
  const rid = nextRequestId(requestEpochMs);
4181
4600
  const captureScope = createRequestCaptureScope(rid, 'http');
4182
4601
  const t0 = requestStartRaw;
@@ -4380,15 +4799,15 @@ function reproMiddleware(cfg) {
4380
4799
  const activePrivacy = resolvePrivacy();
4381
4800
  const requestBodyRaw = req.body;
4382
4801
  const requestBodyMaterialization = limitRawInlinePrivacyValue('request.body', requestBodyRaw)
4383
- ?? await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(requestBodyRaw), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4802
+ ?? await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(requestBodyRaw), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4384
4803
  const requestBody = requestBodyMaterialization.value;
4385
- const requestParams = await applyPrivacyThenMaskAsync('request.params', sanitizeRequestSnapshot(req.params), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4386
- const requestQuery = await applyPrivacyThenMaskAsync('request.query', sanitizeRequestSnapshot(req.query), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4387
- const maskedHeaders = await applyPrivacyThenMaskAsync('request.headers', requestHeaders, cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4804
+ const requestParams = await applyPrivacyThenMaskAsync('request.params', sanitizeRequestSnapshot(req.params), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4805
+ const requestQuery = await applyPrivacyThenMaskAsync('request.query', sanitizeRequestSnapshot(req.query), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4806
+ const maskedHeaders = await applyPrivacyThenMaskAsync('request.headers', requestHeaders, captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4388
4807
  const responseBodyMaterialization = capturedBody === undefined
4389
4808
  ? { value: undefined }
4390
4809
  : limitRawInlinePrivacyValue('response.body', capturedBody)
4391
- ?? await materializeInlinePrivacyValueAsync('response.body', sanitizeRequestSnapshot(capturedBody), cfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4810
+ ?? await materializeInlinePrivacyValueAsync('response.body', sanitizeRequestSnapshot(capturedBody), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
4392
4811
  const responseBody = responseBodyMaterialization.value;
4393
4812
  const requestValueEntries = [];
4394
4813
  const bodyValueCapture = requestBodyMaterialization.skipped
@@ -4398,7 +4817,7 @@ function reproMiddleware(cfg) {
4398
4817
  rawValue: req.body,
4399
4818
  previewValue: requestBody,
4400
4819
  capture: {
4401
- runtimeConfig: cfg,
4820
+ runtimeConfig: captureCfg,
4402
4821
  captureHeaders: cfg.captureHeaders,
4403
4822
  maskReq,
4404
4823
  trace: endpointTraceCtx,
@@ -4411,7 +4830,7 @@ function reproMiddleware(cfg) {
4411
4830
  rawValue: req.params,
4412
4831
  previewValue: requestParams,
4413
4832
  capture: {
4414
- runtimeConfig: cfg,
4833
+ runtimeConfig: captureCfg,
4415
4834
  captureHeaders: cfg.captureHeaders,
4416
4835
  maskReq,
4417
4836
  trace: endpointTraceCtx,
@@ -4424,7 +4843,7 @@ function reproMiddleware(cfg) {
4424
4843
  rawValue: req.query,
4425
4844
  previewValue: requestQuery,
4426
4845
  capture: {
4427
- runtimeConfig: cfg,
4846
+ runtimeConfig: captureCfg,
4428
4847
  captureHeaders: cfg.captureHeaders,
4429
4848
  maskReq,
4430
4849
  trace: endpointTraceCtx,
@@ -4437,7 +4856,7 @@ function reproMiddleware(cfg) {
4437
4856
  rawValue: req.headers,
4438
4857
  previewValue: maskedHeaders,
4439
4858
  capture: {
4440
- runtimeConfig: cfg,
4859
+ runtimeConfig: captureCfg,
4441
4860
  captureHeaders: cfg.captureHeaders,
4442
4861
  maskReq,
4443
4862
  trace: endpointTraceCtx,
@@ -4452,7 +4871,7 @@ function reproMiddleware(cfg) {
4452
4871
  rawValue: capturedBody,
4453
4872
  previewValue: responseBody,
4454
4873
  capture: {
4455
- runtimeConfig: cfg,
4874
+ runtimeConfig: captureCfg,
4456
4875
  captureHeaders: cfg.captureHeaders,
4457
4876
  maskReq,
4458
4877
  trace: endpointTraceCtx,
@@ -4513,7 +4932,7 @@ function reproMiddleware(cfg) {
4513
4932
  }
4514
4933
  const { chosenEndpoint, hasTraceEvents } = chooseRequestEndpoint();
4515
4934
  const { requestPayload, requestValueEntries } = await buildRequestCapturePayloadAsync(chosenEndpoint, hasTraceEvents);
4516
- post(cfg, sid, {
4935
+ post(captureCfg, sid, {
4517
4936
  entries: [{
4518
4937
  actionId: aid,
4519
4938
  request: requestPayload,
@@ -4531,7 +4950,7 @@ function reproMiddleware(cfg) {
4531
4950
  if (requestCaptureManifestScheduled)
4532
4951
  return;
4533
4952
  requestCaptureManifestScheduled = true;
4534
- post(cfg, sid, {
4953
+ post(captureCfg, sid, {
4535
4954
  entries: [{
4536
4955
  actionId: aid,
4537
4956
  requestRid: rid,
@@ -4634,7 +5053,7 @@ function reproMiddleware(cfg) {
4634
5053
  try {
4635
5054
  const pendingEvents = preparePendingTraceEventsForFlush(events.slice());
4636
5055
  const materializedEvents = await materializePendingTraceEventsAsync(pendingEvents, {
4637
- cfg,
5056
+ cfg: captureCfg,
4638
5057
  maskReq,
4639
5058
  masking,
4640
5059
  resolvePrivacy,
@@ -4662,7 +5081,7 @@ function reproMiddleware(cfg) {
4662
5081
  for (let i = 0; i < traceBatches.length; i++) {
4663
5082
  const batch = traceBatches[i];
4664
5083
  const traceValues = collectBatchTraceValueEntries(batch, i);
4665
- post(cfg, sid, {
5084
+ post(captureCfg, sid, {
4666
5085
  entries: [{
4667
5086
  actionId: aid,
4668
5087
  trace: batch,
@@ -4684,6 +5103,9 @@ function reproMiddleware(cfg) {
4684
5103
  }
4685
5104
  sessionDrainWait = waitForSessionDrain(sid);
4686
5105
  endSessionRequest(sid);
5106
+ if (backendLiveState && backendLiveChild) {
5107
+ releaseBackendLiveRequest(cfg, normalizeLiveModeConfig(cfg), backendLiveState, backendLiveChild, finishedAtAligned ?? alignedNow());
5108
+ }
4687
5109
  endTrackedClientRequest();
4688
5110
  if (__TRACER_READY) {
4689
5111
  bumpIdle();
@@ -8801,9 +9223,11 @@ async function initRepro(cfg) {
8801
9223
  const ingestCfg = toIngestConfig(cfg);
8802
9224
  reproAutoMiddlewareConfig = {
8803
9225
  ...ingestCfg,
9226
+ apiBase: cfg.apiBase,
8804
9227
  captureHeaders: cfg.captureHeaders,
8805
9228
  masking: cfg.masking,
8806
9229
  privacy: cfg.privacy,
9230
+ liveMode: cfg.liveMode,
8807
9231
  };
8808
9232
  const integrationCfg = {
8809
9233
  ...reproAutoMiddlewareConfig,
@@ -8855,6 +9279,9 @@ exports.__reproTestHooks = {
8855
9279
  lastClientActivityAt = 0;
8856
9280
  sdkBackgroundQuietUntil = 0;
8857
9281
  },
9282
+ setBackendLiveTimingForTest(override) {
9283
+ backendLiveTimingOverrideForTest = override;
9284
+ },
8858
9285
  runWithRequestContextForTest(ctx, fn) {
8859
9286
  return als.run({
8860
9287
  ...ctx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.38",
3
+ "version": "0.0.39",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
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/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/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/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.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-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",
15
+ "test": "npm run build && 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/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.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-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",
16
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"
17
17
  },
18
18
  "peerDependencies": {