@reproapp/node-sdk 0.0.38 → 0.0.41
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/README.md +228 -310
- package/dist/index.d.ts +55 -0
- package/dist/index.js +1038 -21
- package/dist/ingest/client.js +2 -1
- package/dist/ingest/mapper.js +2 -0
- package/dist/ingest/types.d.ts +1 -0
- package/package.json +10 -4
- package/src/index.ts +1320 -20
- package/src/ingest/client.ts +1 -0
- package/src/ingest/mapper.ts +1 -0
- package/src/ingest/types.ts +1 -0
- package/test/backend-live-mode.test.js +421 -0
- package/test/postgresql-integration.test.js +302 -0
- package/test/postgresql-integrations.testcontainers.test.js +261 -0
- package/test/request-manifest-priority.test.js +6 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.__reproTestHooks = exports.initRepro = exports.patchSendgridMail = exports.patchMongoDb = exports.patchRedis = exports.patchAmqplib = exports.patchKafkaJs = exports.reproMongoosePlugin = exports.reproMiddleware = exports.__materializePendingTraceEventsForWorker = exports.isReproTracingEnabled = exports.initReproTracing = exports.disableReproTraceLogs = exports.enableReproTraceLogs = exports.setReproTraceLogsEnabled = exports.setDisabledTraceFiles = exports.setDisabledFunctionTypes = exports.setDisabledFunctionTraces = void 0;
|
|
3
|
+
exports.__reproTestHooks = exports.initRepro = exports.patchSendgridMail = exports.patchMongoDb = exports.patchPostgreSql = exports.patchRedis = exports.patchAmqplib = exports.patchKafkaJs = exports.reproMongoosePlugin = exports.reproMiddleware = exports.__materializePendingTraceEventsForWorker = exports.isReproTracingEnabled = exports.initReproTracing = exports.disableReproTraceLogs = exports.enableReproTraceLogs = exports.setReproTraceLogsEnabled = exports.setDisabledTraceFiles = exports.setDisabledFunctionTypes = exports.setDisabledFunctionTraces = void 0;
|
|
4
4
|
const mongoose = require("mongoose");
|
|
5
5
|
const async_hooks_1 = require("async_hooks");
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
@@ -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;
|
|
@@ -819,6 +822,27 @@ function buildRequestCaptureManifestPayload(scope, traceBatchTotal, closedAtMs)
|
|
|
819
822
|
closedAt,
|
|
820
823
|
};
|
|
821
824
|
}
|
|
825
|
+
function emitRequestCaptureStart(cfg, sessionId, actionId, scope, openedAtMs) {
|
|
826
|
+
if (!sessionId) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const openedAt = typeof openedAtMs === 'number' && Number.isFinite(openedAtMs)
|
|
830
|
+
? openedAtMs
|
|
831
|
+
: alignedNow();
|
|
832
|
+
post(cfg, sessionId, {
|
|
833
|
+
entries: [{
|
|
834
|
+
actionId: actionId ?? null,
|
|
835
|
+
requestRid: scope.requestRid,
|
|
836
|
+
requestCaptureStart: {
|
|
837
|
+
scopeId: `req:${scope.requestRid}`,
|
|
838
|
+
requestRid: scope.requestRid,
|
|
839
|
+
transport: scope.transport,
|
|
840
|
+
openedAt,
|
|
841
|
+
},
|
|
842
|
+
t: openedAt,
|
|
843
|
+
}],
|
|
844
|
+
}, { priority: 'high' });
|
|
845
|
+
}
|
|
822
846
|
const als = new async_hooks_1.AsyncLocalStorage();
|
|
823
847
|
const getCtx = () => als.getStore() || {};
|
|
824
848
|
function normalizeNativeMongoSuppressionOp(op) {
|
|
@@ -1681,6 +1705,299 @@ function toIngestConfig(cfg) {
|
|
|
1681
1705
|
schemaVersion: cfg.schemaVersion,
|
|
1682
1706
|
};
|
|
1683
1707
|
}
|
|
1708
|
+
function normalizeLiveModeConfig(cfg) {
|
|
1709
|
+
const liveMode = cfg.liveMode;
|
|
1710
|
+
if (liveMode === true) {
|
|
1711
|
+
return { enabled: true };
|
|
1712
|
+
}
|
|
1713
|
+
if (!liveMode || typeof liveMode !== 'object') {
|
|
1714
|
+
return null;
|
|
1715
|
+
}
|
|
1716
|
+
if (liveMode.enabled === false) {
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
return liveMode;
|
|
1720
|
+
}
|
|
1721
|
+
let backendLiveTimingOverrideForTest = null;
|
|
1722
|
+
function positiveInternalDuration(value, fallback) {
|
|
1723
|
+
const numeric = Number(value);
|
|
1724
|
+
if (!Number.isFinite(numeric) || numeric <= 0)
|
|
1725
|
+
return fallback;
|
|
1726
|
+
return Math.max(1, Math.trunc(numeric));
|
|
1727
|
+
}
|
|
1728
|
+
function getBackendLiveTiming() {
|
|
1729
|
+
return {
|
|
1730
|
+
sessionMaxDurationMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.sessionMaxDurationMs, DEFAULT_LIVE_SESSION_DURATION_MS),
|
|
1731
|
+
segmentMaxDurationMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.segmentMaxDurationMs, DEFAULT_LIVE_SEGMENT_DURATION_MS),
|
|
1732
|
+
segmentIdleMs: positiveInternalDuration(backendLiveTimingOverrideForTest?.segmentIdleMs, DEFAULT_LIVE_SEGMENT_IDLE_MS),
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
function normalizeLiveActorId(value) {
|
|
1736
|
+
if (value === undefined || value === null)
|
|
1737
|
+
return null;
|
|
1738
|
+
const normalized = String(value).trim();
|
|
1739
|
+
return normalized.length ? normalized : null;
|
|
1740
|
+
}
|
|
1741
|
+
function normalizeLiveActorLabels(value) {
|
|
1742
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
1743
|
+
return undefined;
|
|
1744
|
+
const labels = {};
|
|
1745
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
1746
|
+
const normalizedKey = key.trim();
|
|
1747
|
+
if (!normalizedKey || rawValue === undefined || rawValue === null)
|
|
1748
|
+
continue;
|
|
1749
|
+
const normalizedValue = String(rawValue).trim();
|
|
1750
|
+
if (normalizedValue)
|
|
1751
|
+
labels[normalizedKey] = normalizedValue;
|
|
1752
|
+
}
|
|
1753
|
+
return Object.keys(labels).length ? labels : undefined;
|
|
1754
|
+
}
|
|
1755
|
+
function newSessionId(base = Date.now()) {
|
|
1756
|
+
return `S_${Math.max(0, Math.trunc(base))}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
|
|
1757
|
+
}
|
|
1758
|
+
function newActionId(base = Date.now()) {
|
|
1759
|
+
return `A_${Math.max(0, Math.trunc(base))}_${(0, crypto_1.randomUUID)().slice(0, 8)}`;
|
|
1760
|
+
}
|
|
1761
|
+
function resolveLiveApiBase(cfg, liveMode) {
|
|
1762
|
+
const env = typeof process !== 'undefined' ? process?.env : undefined;
|
|
1763
|
+
const raw = liveMode?.apiBase ||
|
|
1764
|
+
cfg.apiBase ||
|
|
1765
|
+
env?.REPRO_API_BASE_URL ||
|
|
1766
|
+
env?.REPRO_API_BASE ||
|
|
1767
|
+
cfg.privacy?.apiBase ||
|
|
1768
|
+
null;
|
|
1769
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
1770
|
+
return null;
|
|
1771
|
+
return raw.trim().replace(/\/+$/, '');
|
|
1772
|
+
}
|
|
1773
|
+
function liveApiHeaders(cfg) {
|
|
1774
|
+
const headers = {
|
|
1775
|
+
'Content-Type': 'application/json',
|
|
1776
|
+
'X-App-Id': cfg.appId,
|
|
1777
|
+
};
|
|
1778
|
+
if (cfg.appSecret) {
|
|
1779
|
+
headers['X-App-Secret'] = cfg.appSecret;
|
|
1780
|
+
}
|
|
1781
|
+
if (cfg.appName) {
|
|
1782
|
+
headers['X-App-Name'] = cfg.appName;
|
|
1783
|
+
}
|
|
1784
|
+
return headers;
|
|
1785
|
+
}
|
|
1786
|
+
async function postLiveApi(cfg, liveMode, path, body) {
|
|
1787
|
+
await postLiveApiJson(cfg, liveMode, path, body);
|
|
1788
|
+
}
|
|
1789
|
+
async function postLiveApiJson(cfg, liveMode, path, body) {
|
|
1790
|
+
const apiBase = resolveLiveApiBase(cfg, liveMode);
|
|
1791
|
+
const fetchImpl = globalThis.fetch;
|
|
1792
|
+
if (!apiBase || !cfg.appId || !cfg.appSecret || typeof fetchImpl !== 'function') {
|
|
1793
|
+
return null;
|
|
1794
|
+
}
|
|
1795
|
+
try {
|
|
1796
|
+
const res = await fetchImpl(`${apiBase}${path}`, {
|
|
1797
|
+
method: 'POST',
|
|
1798
|
+
headers: liveApiHeaders(cfg),
|
|
1799
|
+
body: JSON.stringify(body),
|
|
1800
|
+
});
|
|
1801
|
+
if (!res?.ok) {
|
|
1802
|
+
return null;
|
|
1803
|
+
}
|
|
1804
|
+
try {
|
|
1805
|
+
return await res.json();
|
|
1806
|
+
}
|
|
1807
|
+
catch {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
catch {
|
|
1812
|
+
// never break user code
|
|
1813
|
+
return null;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
function enqueueLiveSessionStart(cfg, liveMode, payload) {
|
|
1817
|
+
scheduleSdkBackgroundWork(() => postLiveApi(cfg, liveMode, '/v1/live-sessions', payload), { priority: 'high' });
|
|
1818
|
+
}
|
|
1819
|
+
function enqueueLiveSessionFinish(cfg, liveMode, sessionId) {
|
|
1820
|
+
scheduleSdkBackgroundWork(() => postLiveApi(cfg, liveMode, `/v1/live-sessions/${encodeURIComponent(sessionId)}/finish`, { notes: '' }), { priority: 'high' });
|
|
1821
|
+
}
|
|
1822
|
+
function emitBackendLiveSessionManifest(cfg, session, options) {
|
|
1823
|
+
post(cfg, session.sessionId, {
|
|
1824
|
+
entries: [{
|
|
1825
|
+
sessionCaptureManifest: {
|
|
1826
|
+
mode: 'backend-only',
|
|
1827
|
+
captureMode: 'backend-only',
|
|
1828
|
+
sessionKind: options.sessionKind,
|
|
1829
|
+
parentSessionId: options.parentSessionId ?? null,
|
|
1830
|
+
previousSessionId: options.previousSessionId ?? null,
|
|
1831
|
+
actorId: options.actorId,
|
|
1832
|
+
actorType: options.actorType,
|
|
1833
|
+
actorLabels: options.actorLabels,
|
|
1834
|
+
expected: {
|
|
1835
|
+
frontendActionDoc: 0,
|
|
1836
|
+
rrwebChunk: 0,
|
|
1837
|
+
frontendSessionEnd: 0,
|
|
1838
|
+
},
|
|
1839
|
+
closedAt: options.closedAt,
|
|
1840
|
+
},
|
|
1841
|
+
t: options.closedAt,
|
|
1842
|
+
}],
|
|
1843
|
+
}, { priority: 'high' });
|
|
1844
|
+
}
|
|
1845
|
+
function closeBackendLiveParent(cfg, liveMode, state, parent, closedAt) {
|
|
1846
|
+
if (parent.closed) {
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
parent.closed = true;
|
|
1850
|
+
if (parent.closeTimer) {
|
|
1851
|
+
try {
|
|
1852
|
+
clearTimeout(parent.closeTimer);
|
|
1853
|
+
}
|
|
1854
|
+
catch { }
|
|
1855
|
+
parent.closeTimer = null;
|
|
1856
|
+
}
|
|
1857
|
+
state.previousParentSessionId = parent.sessionId;
|
|
1858
|
+
if (state.parent === parent) {
|
|
1859
|
+
state.parent = null;
|
|
1860
|
+
}
|
|
1861
|
+
emitBackendLiveSessionManifest(cfg, parent, {
|
|
1862
|
+
sessionKind: 'parent',
|
|
1863
|
+
actorId: parent.actorId,
|
|
1864
|
+
actorType: parent.actorType,
|
|
1865
|
+
actorLabels: parent.actorLabels,
|
|
1866
|
+
previousSessionId: parent.previousSessionId,
|
|
1867
|
+
closedAt,
|
|
1868
|
+
});
|
|
1869
|
+
enqueueLiveSessionFinish(cfg, liveMode, parent.sessionId);
|
|
1870
|
+
}
|
|
1871
|
+
function scheduleBackendLiveParentClose(cfg, liveMode, state, parent) {
|
|
1872
|
+
if (parent.closed) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
if (parent.closeTimer) {
|
|
1876
|
+
try {
|
|
1877
|
+
clearTimeout(parent.closeTimer);
|
|
1878
|
+
}
|
|
1879
|
+
catch { }
|
|
1880
|
+
parent.closeTimer = null;
|
|
1881
|
+
}
|
|
1882
|
+
const now = Date.now();
|
|
1883
|
+
const activeChild = state.activeChild;
|
|
1884
|
+
const activeChildBelongsToParent = activeChild?.parentSessionId === parent.sessionId;
|
|
1885
|
+
const closeAt = activeChildBelongsToParent && now >= parent.expiresAt
|
|
1886
|
+
? Math.max(parent.expiresAt, activeChild.closeDeadline, now + 1)
|
|
1887
|
+
: parent.expiresAt;
|
|
1888
|
+
parent.closeTimer = setTimeout(() => {
|
|
1889
|
+
parent.closeTimer = null;
|
|
1890
|
+
if (parent.closed || state.parent !== parent) {
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
const currentChild = state.activeChild;
|
|
1894
|
+
if (currentChild?.parentSessionId === parent.sessionId) {
|
|
1895
|
+
if (currentChild.requestsInFlight <= 0) {
|
|
1896
|
+
scheduleBackendLiveChildClose(cfg, liveMode, state, currentChild);
|
|
1897
|
+
}
|
|
1898
|
+
scheduleBackendLiveParentClose(cfg, liveMode, state, parent);
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
closeBackendLiveParent(cfg, liveMode, state, parent, Date.now());
|
|
1902
|
+
}, Math.max(0, closeAt - now));
|
|
1903
|
+
try {
|
|
1904
|
+
parent.closeTimer.unref?.();
|
|
1905
|
+
}
|
|
1906
|
+
catch { }
|
|
1907
|
+
}
|
|
1908
|
+
function scheduleBackendLiveChildClose(cfg, liveMode, state, child) {
|
|
1909
|
+
if (child.closeTimer) {
|
|
1910
|
+
try {
|
|
1911
|
+
clearTimeout(child.closeTimer);
|
|
1912
|
+
}
|
|
1913
|
+
catch { }
|
|
1914
|
+
child.closeTimer = null;
|
|
1915
|
+
}
|
|
1916
|
+
const delay = Math.max(0, child.closeDeadline - Date.now());
|
|
1917
|
+
child.closeTimer = setTimeout(() => {
|
|
1918
|
+
child.closeTimer = null;
|
|
1919
|
+
if (child.requestsInFlight > 0 || Date.now() < child.closeDeadline) {
|
|
1920
|
+
scheduleBackendLiveChildClose(cfg, liveMode, state, child);
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
if (state.activeChild === child) {
|
|
1924
|
+
state.activeChild = null;
|
|
1925
|
+
}
|
|
1926
|
+
state.previousChildSessionId = child.sessionId;
|
|
1927
|
+
const closedAt = Date.now();
|
|
1928
|
+
emitBackendLiveSessionManifest(cfg, child, {
|
|
1929
|
+
sessionKind: 'child',
|
|
1930
|
+
actorId: child.actorId,
|
|
1931
|
+
actorType: child.actorType,
|
|
1932
|
+
actorLabels: child.actorLabels,
|
|
1933
|
+
parentSessionId: child.parentSessionId,
|
|
1934
|
+
previousSessionId: child.previousSessionId,
|
|
1935
|
+
closedAt,
|
|
1936
|
+
});
|
|
1937
|
+
enqueueLiveSessionFinish(cfg, liveMode, child.sessionId);
|
|
1938
|
+
}, delay);
|
|
1939
|
+
try {
|
|
1940
|
+
child.closeTimer.unref?.();
|
|
1941
|
+
}
|
|
1942
|
+
catch { }
|
|
1943
|
+
}
|
|
1944
|
+
function releaseBackendLiveRequest(cfg, liveMode, state, child, finishedAt) {
|
|
1945
|
+
child.requestsInFlight = Math.max(0, child.requestsInFlight - 1);
|
|
1946
|
+
const idleMs = getBackendLiveTiming().segmentIdleMs;
|
|
1947
|
+
child.closeDeadline = Math.max(child.closeDeadline, finishedAt + idleMs);
|
|
1948
|
+
if (child.requestsInFlight <= 0) {
|
|
1949
|
+
scheduleBackendLiveChildClose(cfg, liveMode, state, child);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
async function resolveBackendLiveContext(cfg, req, requestStart) {
|
|
1953
|
+
const liveMode = normalizeLiveModeConfig(cfg);
|
|
1954
|
+
if (!liveMode?.backendOnly) {
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
const method = String(req.method || 'GET').toUpperCase();
|
|
1958
|
+
if (method === 'OPTIONS' && !hasReproRequestCorrelation(req)) {
|
|
1959
|
+
return null;
|
|
1960
|
+
}
|
|
1961
|
+
const identity = await resolveLiveActorIdentity(cfg, req);
|
|
1962
|
+
if (!identity) {
|
|
1963
|
+
return null;
|
|
1964
|
+
}
|
|
1965
|
+
const { actorId, actorType, actorLabels } = identity;
|
|
1966
|
+
const claim = await postLiveApiJson(cfg, liveMode, '/v1/live-sessions/claim', {
|
|
1967
|
+
clientTime: requestStart,
|
|
1968
|
+
captureMode: 'backend-only',
|
|
1969
|
+
actorId,
|
|
1970
|
+
actorIds: [actorId],
|
|
1971
|
+
actorType,
|
|
1972
|
+
actorLabels,
|
|
1973
|
+
});
|
|
1974
|
+
const sid = normalizeLiveActorId(claim?.childSessionId) ??
|
|
1975
|
+
normalizeLiveActorId(claim?.sessionId);
|
|
1976
|
+
if (!sid) {
|
|
1977
|
+
return null;
|
|
1978
|
+
}
|
|
1979
|
+
const aid = normalizeLiveActorId(claim?.actionId) ?? newActionId(requestStart);
|
|
1980
|
+
const parentSessionId = normalizeLiveActorId(claim?.parentSessionId) ?? '';
|
|
1981
|
+
const liveChild = {
|
|
1982
|
+
sessionId: sid,
|
|
1983
|
+
parentSessionId,
|
|
1984
|
+
previousSessionId: null,
|
|
1985
|
+
actorId,
|
|
1986
|
+
actorType,
|
|
1987
|
+
actorLabels,
|
|
1988
|
+
startedAt: requestStart,
|
|
1989
|
+
expiresAt: requestStart,
|
|
1990
|
+
closeDeadline: requestStart,
|
|
1991
|
+
requestsInFlight: 0,
|
|
1992
|
+
closeTimer: null,
|
|
1993
|
+
};
|
|
1994
|
+
return {
|
|
1995
|
+
sid: liveChild.sessionId,
|
|
1996
|
+
aid,
|
|
1997
|
+
requestStart,
|
|
1998
|
+
child: liveChild,
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
1684
2001
|
function resolveIngestUrl(cfg) {
|
|
1685
2002
|
const envBase = typeof process !== 'undefined' ? process?.env?.REPRO_INGEST_BASE : undefined;
|
|
1686
2003
|
const ingestBase = String(envBase || cfg.ingestBase || DEFAULT_INGEST_BASE).replace(/\/+$/, '');
|
|
@@ -1727,6 +2044,19 @@ function readRequestQueryNumber(req, name) {
|
|
|
1727
2044
|
const parsed = Number(raw);
|
|
1728
2045
|
return Number.isFinite(parsed) ? parsed : null;
|
|
1729
2046
|
}
|
|
2047
|
+
function hasReproRequestCorrelation(req) {
|
|
2048
|
+
const headers = req.headers || {};
|
|
2049
|
+
if (hasHttpHeader(headers, KAFKA_HEADER_SESSION_ID) ||
|
|
2050
|
+
hasHttpHeader(headers, KAFKA_HEADER_ACTION_ID) ||
|
|
2051
|
+
hasHttpHeader(headers, KAFKA_HEADER_REQUEST_ID) ||
|
|
2052
|
+
hasHttpHeader(headers, KAFKA_HEADER_TRACE_ID) ||
|
|
2053
|
+
hasHttpHeader(headers, KAFKA_HEADER_SPAN_ID) ||
|
|
2054
|
+
hasHttpHeader(headers, KAFKA_HEADER_PARENT_SPAN_ID)) {
|
|
2055
|
+
return true;
|
|
2056
|
+
}
|
|
2057
|
+
return Boolean(readRequestQueryParam(req, REPRO_QUERY_SESSION_PARAM) ||
|
|
2058
|
+
readRequestQueryParam(req, REPRO_QUERY_ACTION_PARAM));
|
|
2059
|
+
}
|
|
1730
2060
|
function stripReproQueryFromUrl(rawUrl) {
|
|
1731
2061
|
if (!rawUrl || !rawUrl.includes('?')) {
|
|
1732
2062
|
return rawUrl;
|
|
@@ -4131,6 +4461,72 @@ async function maybeCaptureDbValueFromCandidatesAsync(params, sink) {
|
|
|
4131
4461
|
}
|
|
4132
4462
|
return skippedRef;
|
|
4133
4463
|
}
|
|
4464
|
+
function buildLiveActorArgs(cfg, req) {
|
|
4465
|
+
const rawUrl = String(req.originalUrl || req.url || '/');
|
|
4466
|
+
const path = rawUrl.split('?')[0] || '/';
|
|
4467
|
+
return {
|
|
4468
|
+
req,
|
|
4469
|
+
headers: req.headers,
|
|
4470
|
+
method: String(req.method || 'GET').toUpperCase(),
|
|
4471
|
+
url: rawUrl,
|
|
4472
|
+
path,
|
|
4473
|
+
ip: req.ip ?? req.socket?.remoteAddress ?? null,
|
|
4474
|
+
appId: cfg.appId,
|
|
4475
|
+
appName: cfg.appName,
|
|
4476
|
+
serviceName: cfg.serviceName,
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4479
|
+
async function resolveLiveActorIdentity(cfg, req) {
|
|
4480
|
+
const liveMode = normalizeLiveModeConfig(cfg);
|
|
4481
|
+
if (!liveMode) {
|
|
4482
|
+
return null;
|
|
4483
|
+
}
|
|
4484
|
+
const args = buildLiveActorArgs(cfg, req);
|
|
4485
|
+
let rawActorId;
|
|
4486
|
+
try {
|
|
4487
|
+
rawActorId = liveMode.defineActorId
|
|
4488
|
+
? await liveMode.defineActorId(args)
|
|
4489
|
+
: cfg.actorId;
|
|
4490
|
+
}
|
|
4491
|
+
catch {
|
|
4492
|
+
rawActorId = null;
|
|
4493
|
+
}
|
|
4494
|
+
const actorId = normalizeLiveActorId(rawActorId);
|
|
4495
|
+
if (!actorId) {
|
|
4496
|
+
return null;
|
|
4497
|
+
}
|
|
4498
|
+
const actorType = normalizeLiveActorId(liveMode.actorType) ?? cfg.actorType ?? 'user';
|
|
4499
|
+
const decision = {
|
|
4500
|
+
...args,
|
|
4501
|
+
actorId,
|
|
4502
|
+
actorType,
|
|
4503
|
+
};
|
|
4504
|
+
if (liveMode.shouldCapture) {
|
|
4505
|
+
let shouldCapture = false;
|
|
4506
|
+
try {
|
|
4507
|
+
shouldCapture = (await liveMode.shouldCapture(decision)) === true;
|
|
4508
|
+
}
|
|
4509
|
+
catch {
|
|
4510
|
+
shouldCapture = false;
|
|
4511
|
+
}
|
|
4512
|
+
if (!shouldCapture) {
|
|
4513
|
+
return null;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
let rawLabels;
|
|
4517
|
+
try {
|
|
4518
|
+
rawLabels =
|
|
4519
|
+
typeof liveMode.actorLabels === 'function'
|
|
4520
|
+
? await liveMode.actorLabels(decision)
|
|
4521
|
+
: liveMode.actorLabels;
|
|
4522
|
+
}
|
|
4523
|
+
catch {
|
|
4524
|
+
rawLabels = undefined;
|
|
4525
|
+
}
|
|
4526
|
+
const actorLabels = normalizeLiveActorLabels(rawLabels) ?? normalizeLiveActorLabels(cfg.actorLabels);
|
|
4527
|
+
return { actorId, actorType, actorLabels };
|
|
4528
|
+
}
|
|
4529
|
+
const backendLiveActors = new Map();
|
|
4134
4530
|
function reproMiddleware(cfg) {
|
|
4135
4531
|
const masking = normalizeMaskingConfig(cfg.masking);
|
|
4136
4532
|
patchMongoDb(cfg);
|
|
@@ -4165,20 +4561,65 @@ function reproMiddleware(cfg) {
|
|
|
4165
4561
|
clientRequestEnded = true;
|
|
4166
4562
|
endClientRequest();
|
|
4167
4563
|
};
|
|
4168
|
-
const
|
|
4169
|
-
const
|
|
4564
|
+
const requestStartRaw = Date.now();
|
|
4565
|
+
const headerTs = readHeaderNumber(req.headers[REQUEST_START_HEADER]) ?? queryTs;
|
|
4566
|
+
const requestEpochMs = headerTs !== null ? headerTs : requestStartRaw;
|
|
4567
|
+
let backendLiveState = null;
|
|
4568
|
+
let backendLiveChild = null;
|
|
4569
|
+
const headerSid = req.headers['x-bug-session-id'] || '';
|
|
4570
|
+
const headerAid = req.headers['x-bug-action-id'] || '';
|
|
4571
|
+
const hasIncomingReproSession = Boolean(headerSid || querySid || headerAid || queryAid);
|
|
4572
|
+
const backendLiveContext = !hasIncomingReproSession
|
|
4573
|
+
? await resolveBackendLiveContext(cfg, req, requestEpochMs)
|
|
4574
|
+
: null;
|
|
4575
|
+
const taggedActorIdentity = hasIncomingReproSession
|
|
4576
|
+
? await resolveLiveActorIdentity(cfg, req)
|
|
4577
|
+
: null;
|
|
4578
|
+
if (backendLiveContext) {
|
|
4579
|
+
backendLiveState = backendLiveActors.get(backendLiveContext.child.actorId) ?? null;
|
|
4580
|
+
backendLiveChild = backendLiveContext.child;
|
|
4581
|
+
}
|
|
4582
|
+
const sid = headerSid || querySid || backendLiveContext?.sid || '';
|
|
4583
|
+
const aid = headerAid || queryAid || backendLiveContext?.aid || '';
|
|
4170
4584
|
if (!sid || !aid) {
|
|
4171
4585
|
res.on('finish', endTrackedClientRequest);
|
|
4172
4586
|
res.on('close', endTrackedClientRequest);
|
|
4173
4587
|
return next(); // only capture tagged requests
|
|
4174
4588
|
}
|
|
4175
|
-
const
|
|
4176
|
-
|
|
4177
|
-
|
|
4589
|
+
const captureCfg = backendLiveChild
|
|
4590
|
+
? {
|
|
4591
|
+
...cfg,
|
|
4592
|
+
actorId: backendLiveChild.actorId,
|
|
4593
|
+
actorType: backendLiveChild.actorType,
|
|
4594
|
+
actorLabels: backendLiveChild.actorLabels,
|
|
4595
|
+
}
|
|
4596
|
+
: taggedActorIdentity
|
|
4597
|
+
? {
|
|
4598
|
+
...cfg,
|
|
4599
|
+
actorId: taggedActorIdentity.actorId,
|
|
4600
|
+
actorType: taggedActorIdentity.actorType,
|
|
4601
|
+
actorLabels: taggedActorIdentity.actorLabels,
|
|
4602
|
+
}
|
|
4603
|
+
: cfg;
|
|
4604
|
+
if (taggedActorIdentity) {
|
|
4605
|
+
post(captureCfg, sid, {
|
|
4606
|
+
entries: [{
|
|
4607
|
+
actionId: aid,
|
|
4608
|
+
sessionActor: {
|
|
4609
|
+
actorId: taggedActorIdentity.actorId,
|
|
4610
|
+
actorType: taggedActorIdentity.actorType,
|
|
4611
|
+
actorLabels: taggedActorIdentity.actorLabels,
|
|
4612
|
+
source: 'node-sdk',
|
|
4613
|
+
},
|
|
4614
|
+
t: requestEpochMs,
|
|
4615
|
+
}],
|
|
4616
|
+
}, { priority: 'high' });
|
|
4617
|
+
}
|
|
4618
|
+
const resolvePrivacy = () => getRuntimePrivacyState(captureCfg).policy ?? null;
|
|
4178
4619
|
const clockSkewMs = headerTs !== null ? headerTs - requestStartRaw : 0;
|
|
4179
|
-
const requestEpochMs = headerTs !== null ? headerTs : requestStartRaw + clockSkewMs;
|
|
4180
4620
|
const rid = nextRequestId(requestEpochMs);
|
|
4181
4621
|
const captureScope = createRequestCaptureScope(rid, 'http');
|
|
4622
|
+
emitRequestCaptureStart(captureCfg, sid, aid, captureScope, requestEpochMs);
|
|
4182
4623
|
const t0 = requestStartRaw;
|
|
4183
4624
|
const url = req.originalUrl || req.url || '/';
|
|
4184
4625
|
const urlPathOnly = (url || '/').split('?')[0] || '/';
|
|
@@ -4380,15 +4821,15 @@ function reproMiddleware(cfg) {
|
|
|
4380
4821
|
const activePrivacy = resolvePrivacy();
|
|
4381
4822
|
const requestBodyRaw = req.body;
|
|
4382
4823
|
const requestBodyMaterialization = limitRawInlinePrivacyValue('request.body', requestBodyRaw)
|
|
4383
|
-
?? await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(requestBodyRaw),
|
|
4824
|
+
?? await materializeInlinePrivacyValueAsync('request.body', sanitizeRequestSnapshot(requestBodyRaw), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
|
|
4384
4825
|
const requestBody = requestBodyMaterialization.value;
|
|
4385
|
-
const requestParams = await applyPrivacyThenMaskAsync('request.params', sanitizeRequestSnapshot(req.params),
|
|
4386
|
-
const requestQuery = await applyPrivacyThenMaskAsync('request.query', sanitizeRequestSnapshot(req.query),
|
|
4387
|
-
const maskedHeaders = await applyPrivacyThenMaskAsync('request.headers', requestHeaders,
|
|
4826
|
+
const requestParams = await applyPrivacyThenMaskAsync('request.params', sanitizeRequestSnapshot(req.params), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
|
|
4827
|
+
const requestQuery = await applyPrivacyThenMaskAsync('request.query', sanitizeRequestSnapshot(req.query), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
|
|
4828
|
+
const maskedHeaders = await applyPrivacyThenMaskAsync('request.headers', requestHeaders, captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
|
|
4388
4829
|
const responseBodyMaterialization = capturedBody === undefined
|
|
4389
4830
|
? { value: undefined }
|
|
4390
4831
|
: limitRawInlinePrivacyValue('response.body', capturedBody)
|
|
4391
|
-
?? await materializeInlinePrivacyValueAsync('response.body', sanitizeRequestSnapshot(capturedBody),
|
|
4832
|
+
?? await materializeInlinePrivacyValueAsync('response.body', sanitizeRequestSnapshot(capturedBody), captureCfg, maskReq, endpointTraceCtx, masking, activePrivacy);
|
|
4392
4833
|
const responseBody = responseBodyMaterialization.value;
|
|
4393
4834
|
const requestValueEntries = [];
|
|
4394
4835
|
const bodyValueCapture = requestBodyMaterialization.skipped
|
|
@@ -4398,7 +4839,7 @@ function reproMiddleware(cfg) {
|
|
|
4398
4839
|
rawValue: req.body,
|
|
4399
4840
|
previewValue: requestBody,
|
|
4400
4841
|
capture: {
|
|
4401
|
-
runtimeConfig:
|
|
4842
|
+
runtimeConfig: captureCfg,
|
|
4402
4843
|
captureHeaders: cfg.captureHeaders,
|
|
4403
4844
|
maskReq,
|
|
4404
4845
|
trace: endpointTraceCtx,
|
|
@@ -4411,7 +4852,7 @@ function reproMiddleware(cfg) {
|
|
|
4411
4852
|
rawValue: req.params,
|
|
4412
4853
|
previewValue: requestParams,
|
|
4413
4854
|
capture: {
|
|
4414
|
-
runtimeConfig:
|
|
4855
|
+
runtimeConfig: captureCfg,
|
|
4415
4856
|
captureHeaders: cfg.captureHeaders,
|
|
4416
4857
|
maskReq,
|
|
4417
4858
|
trace: endpointTraceCtx,
|
|
@@ -4424,7 +4865,7 @@ function reproMiddleware(cfg) {
|
|
|
4424
4865
|
rawValue: req.query,
|
|
4425
4866
|
previewValue: requestQuery,
|
|
4426
4867
|
capture: {
|
|
4427
|
-
runtimeConfig:
|
|
4868
|
+
runtimeConfig: captureCfg,
|
|
4428
4869
|
captureHeaders: cfg.captureHeaders,
|
|
4429
4870
|
maskReq,
|
|
4430
4871
|
trace: endpointTraceCtx,
|
|
@@ -4437,7 +4878,7 @@ function reproMiddleware(cfg) {
|
|
|
4437
4878
|
rawValue: req.headers,
|
|
4438
4879
|
previewValue: maskedHeaders,
|
|
4439
4880
|
capture: {
|
|
4440
|
-
runtimeConfig:
|
|
4881
|
+
runtimeConfig: captureCfg,
|
|
4441
4882
|
captureHeaders: cfg.captureHeaders,
|
|
4442
4883
|
maskReq,
|
|
4443
4884
|
trace: endpointTraceCtx,
|
|
@@ -4452,7 +4893,7 @@ function reproMiddleware(cfg) {
|
|
|
4452
4893
|
rawValue: capturedBody,
|
|
4453
4894
|
previewValue: responseBody,
|
|
4454
4895
|
capture: {
|
|
4455
|
-
runtimeConfig:
|
|
4896
|
+
runtimeConfig: captureCfg,
|
|
4456
4897
|
captureHeaders: cfg.captureHeaders,
|
|
4457
4898
|
maskReq,
|
|
4458
4899
|
trace: endpointTraceCtx,
|
|
@@ -4513,7 +4954,7 @@ function reproMiddleware(cfg) {
|
|
|
4513
4954
|
}
|
|
4514
4955
|
const { chosenEndpoint, hasTraceEvents } = chooseRequestEndpoint();
|
|
4515
4956
|
const { requestPayload, requestValueEntries } = await buildRequestCapturePayloadAsync(chosenEndpoint, hasTraceEvents);
|
|
4516
|
-
post(
|
|
4957
|
+
post(captureCfg, sid, {
|
|
4517
4958
|
entries: [{
|
|
4518
4959
|
actionId: aid,
|
|
4519
4960
|
request: requestPayload,
|
|
@@ -4531,7 +4972,7 @@ function reproMiddleware(cfg) {
|
|
|
4531
4972
|
if (requestCaptureManifestScheduled)
|
|
4532
4973
|
return;
|
|
4533
4974
|
requestCaptureManifestScheduled = true;
|
|
4534
|
-
post(
|
|
4975
|
+
post(captureCfg, sid, {
|
|
4535
4976
|
entries: [{
|
|
4536
4977
|
actionId: aid,
|
|
4537
4978
|
requestRid: rid,
|
|
@@ -4634,7 +5075,7 @@ function reproMiddleware(cfg) {
|
|
|
4634
5075
|
try {
|
|
4635
5076
|
const pendingEvents = preparePendingTraceEventsForFlush(events.slice());
|
|
4636
5077
|
const materializedEvents = await materializePendingTraceEventsAsync(pendingEvents, {
|
|
4637
|
-
cfg,
|
|
5078
|
+
cfg: captureCfg,
|
|
4638
5079
|
maskReq,
|
|
4639
5080
|
masking,
|
|
4640
5081
|
resolvePrivacy,
|
|
@@ -4662,7 +5103,7 @@ function reproMiddleware(cfg) {
|
|
|
4662
5103
|
for (let i = 0; i < traceBatches.length; i++) {
|
|
4663
5104
|
const batch = traceBatches[i];
|
|
4664
5105
|
const traceValues = collectBatchTraceValueEntries(batch, i);
|
|
4665
|
-
post(
|
|
5106
|
+
post(captureCfg, sid, {
|
|
4666
5107
|
entries: [{
|
|
4667
5108
|
actionId: aid,
|
|
4668
5109
|
trace: batch,
|
|
@@ -4684,6 +5125,9 @@ function reproMiddleware(cfg) {
|
|
|
4684
5125
|
}
|
|
4685
5126
|
sessionDrainWait = waitForSessionDrain(sid);
|
|
4686
5127
|
endSessionRequest(sid);
|
|
5128
|
+
if (backendLiveState && backendLiveChild) {
|
|
5129
|
+
releaseBackendLiveRequest(cfg, normalizeLiveModeConfig(cfg), backendLiveState, backendLiveChild, finishedAtAligned ?? alignedNow());
|
|
5130
|
+
}
|
|
4687
5131
|
endTrackedClientRequest();
|
|
4688
5132
|
if (__TRACER_READY) {
|
|
4689
5133
|
bumpIdle();
|
|
@@ -6093,6 +6537,7 @@ function patchKafkaProducerInstance(producer, cfg) {
|
|
|
6093
6537
|
const span = captureSpanContextFromTracer();
|
|
6094
6538
|
const publishRid = nextRequestId(alignedNow());
|
|
6095
6539
|
const captureScope = createRequestCaptureScope(publishRid, 'kafka');
|
|
6540
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
6096
6541
|
const payloadObj = payload && typeof payload === 'object' ? payload : {};
|
|
6097
6542
|
const topic = sanitizeKafkaTopic(payloadObj.topic);
|
|
6098
6543
|
const rawMessages = Array.isArray(payloadObj.messages) ? payloadObj.messages : [];
|
|
@@ -6201,6 +6646,7 @@ function patchKafkaProducerInstance(producer, cfg) {
|
|
|
6201
6646
|
const span = captureSpanContextFromTracer();
|
|
6202
6647
|
const publishRid = nextRequestId(alignedNow());
|
|
6203
6648
|
const captureScope = createRequestCaptureScope(publishRid, 'kafka');
|
|
6649
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
6204
6650
|
const payloadObj = payload && typeof payload === 'object' ? payload : {};
|
|
6205
6651
|
const topicMessages = Array.isArray(payloadObj.topicMessages)
|
|
6206
6652
|
? payloadObj.topicMessages
|
|
@@ -6342,6 +6788,7 @@ function wrapKafkaEachMessageHandler(eachMessage, cfg, consumer) {
|
|
|
6342
6788
|
const consumeRid = nextRequestId(`${topic}-${message?.offset ?? alignedNow()}`);
|
|
6343
6789
|
const traceId = consumeRid;
|
|
6344
6790
|
const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
|
|
6791
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
6345
6792
|
const rawTraceEvents = [];
|
|
6346
6793
|
const maskReq = {
|
|
6347
6794
|
method: 'KAFKA_CONSUME',
|
|
@@ -6456,6 +6903,7 @@ function wrapKafkaEachBatchHandler(eachBatch, cfg, consumer) {
|
|
|
6456
6903
|
const consumeRid = nextRequestId(`${topic}-batch-${alignedNow()}`);
|
|
6457
6904
|
const traceId = consumeRid;
|
|
6458
6905
|
const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
|
|
6906
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
6459
6907
|
const rawTraceEvents = [];
|
|
6460
6908
|
const maskReq = {
|
|
6461
6909
|
method: 'KAFKA_CONSUME_BATCH',
|
|
@@ -7084,6 +7532,7 @@ function patchAmqplibPublishMethod(channel, cfg, methodName) {
|
|
|
7084
7532
|
const key = `${method} ${methodName === 'publish' ? `${exchange || 'default'}/${routingKey}` : queue}`;
|
|
7085
7533
|
const content = methodName === 'publish' ? args[2] : args[1];
|
|
7086
7534
|
const captureScope = createRequestCaptureScope(publishRid, 'amqp');
|
|
7535
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
7087
7536
|
const t0 = Date.now();
|
|
7088
7537
|
let finished = false;
|
|
7089
7538
|
const finish = (status, error, result) => {
|
|
@@ -7170,6 +7619,7 @@ function wrapAmqplibConsumeHandler(onMessage, cfg, queue) {
|
|
|
7170
7619
|
const consumeRid = nextRequestId(`${queueName}-${deliveryTag}`);
|
|
7171
7620
|
const traceId = consumeRid;
|
|
7172
7621
|
const captureScope = createRequestCaptureScope(consumeRid, 'amqp');
|
|
7622
|
+
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
7173
7623
|
const rawTraceEvents = [];
|
|
7174
7624
|
const maskReq = {
|
|
7175
7625
|
method: 'AMQP_CONSUME',
|
|
@@ -7732,6 +8182,564 @@ function patchRedis(cfg) {
|
|
|
7732
8182
|
}
|
|
7733
8183
|
}
|
|
7734
8184
|
exports.patchRedis = patchRedis;
|
|
8185
|
+
const postgresSqlPatchConfigs = [];
|
|
8186
|
+
const postgresSqlSourceAls = new async_hooks_1.AsyncLocalStorage();
|
|
8187
|
+
const pgPoolQueryAls = new async_hooks_1.AsyncLocalStorage();
|
|
8188
|
+
let pgRequireHookInstalled = false;
|
|
8189
|
+
let postgresJsRequireHookInstalled = false;
|
|
8190
|
+
let typeOrmRequireHookInstalled = false;
|
|
8191
|
+
const PG_CLIENT_PATCHED = Symbol.for('@reproapp/node-sdk/pg-client-patched');
|
|
8192
|
+
const PG_POOL_PATCHED = Symbol.for('@reproapp/node-sdk/pg-pool-patched');
|
|
8193
|
+
const POSTGRES_JS_QUERY_PATCHED = Symbol.for('@reproapp/node-sdk/postgres-js-query-patched');
|
|
8194
|
+
const TYPEORM_DATASOURCE_PATCHED = Symbol.for('@reproapp/node-sdk/typeorm-datasource-patched');
|
|
8195
|
+
const TYPEORM_QUERY_RUNNER_PATCHED = Symbol.for('@reproapp/node-sdk/typeorm-query-runner-patched');
|
|
8196
|
+
function registerPostgreSqlPatchConfig(cfg) {
|
|
8197
|
+
if (!postgresSqlPatchConfigs.includes(cfg)) {
|
|
8198
|
+
postgresSqlPatchConfigs.push(cfg);
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
function resolvePostgreSqlCaptureContext(defaultSource, source) {
|
|
8202
|
+
const ctx = getCtx();
|
|
8203
|
+
const contextualSource = postgresSqlSourceAls.getStore();
|
|
8204
|
+
for (let i = postgresSqlPatchConfigs.length - 1; i >= 0; i -= 1) {
|
|
8205
|
+
const cfg = postgresSqlPatchConfigs[i];
|
|
8206
|
+
if (!cfg)
|
|
8207
|
+
continue;
|
|
8208
|
+
const fallback = cfg.resolveContext?.() ?? {};
|
|
8209
|
+
const sid = ctx.sid ?? fallback.sid ?? null;
|
|
8210
|
+
if (!sid)
|
|
8211
|
+
continue;
|
|
8212
|
+
return {
|
|
8213
|
+
cfg,
|
|
8214
|
+
sid,
|
|
8215
|
+
aid: ctx.aid ?? fallback.aid ?? null,
|
|
8216
|
+
spanContext: captureSpanContextFromTracer(source),
|
|
8217
|
+
source: contextualSource ?? defaultSource,
|
|
8218
|
+
};
|
|
8219
|
+
}
|
|
8220
|
+
return null;
|
|
8221
|
+
}
|
|
8222
|
+
function stripSqlComments(sql) {
|
|
8223
|
+
let remaining = sql.trimStart();
|
|
8224
|
+
while (remaining.length) {
|
|
8225
|
+
if (remaining.startsWith('--')) {
|
|
8226
|
+
const newline = remaining.indexOf('\n');
|
|
8227
|
+
remaining = newline < 0 ? '' : remaining.slice(newline + 1).trimStart();
|
|
8228
|
+
continue;
|
|
8229
|
+
}
|
|
8230
|
+
if (remaining.startsWith('/*')) {
|
|
8231
|
+
const end = remaining.indexOf('*/', 2);
|
|
8232
|
+
remaining = end < 0 ? '' : remaining.slice(end + 2).trimStart();
|
|
8233
|
+
continue;
|
|
8234
|
+
}
|
|
8235
|
+
break;
|
|
8236
|
+
}
|
|
8237
|
+
return remaining;
|
|
8238
|
+
}
|
|
8239
|
+
function normalizeSqlOperation(sql) {
|
|
8240
|
+
const normalized = stripSqlComments(sql);
|
|
8241
|
+
const first = normalized.match(/^([a-z]+)/i)?.[1]?.toLowerCase() ?? 'query';
|
|
8242
|
+
if (first !== 'with')
|
|
8243
|
+
return first;
|
|
8244
|
+
// CTEs can wrap any DML statement. The last top-level DML keyword is the
|
|
8245
|
+
// operation users care about, while nested SELECTs only build the CTE.
|
|
8246
|
+
const matches = Array.from(normalized.matchAll(/\b(select|insert|update|delete|merge)\b/gi));
|
|
8247
|
+
return matches.length ? String(matches[matches.length - 1][1]).toLowerCase() : 'query';
|
|
8248
|
+
}
|
|
8249
|
+
function cleanSqlIdentifier(value) {
|
|
8250
|
+
if (!value)
|
|
8251
|
+
return undefined;
|
|
8252
|
+
const cleaned = value
|
|
8253
|
+
.trim()
|
|
8254
|
+
.replace(/[;,]$/, '')
|
|
8255
|
+
.split('.')
|
|
8256
|
+
.map((part) => part.replace(/^['"`]|['"`]$/g, ''))
|
|
8257
|
+
.join('.');
|
|
8258
|
+
return cleaned || undefined;
|
|
8259
|
+
}
|
|
8260
|
+
function resolveSqlTable(sql, op) {
|
|
8261
|
+
const normalized = stripSqlComments(sql);
|
|
8262
|
+
let match = null;
|
|
8263
|
+
if (op === 'insert')
|
|
8264
|
+
match = normalized.match(/\binsert\s+into\s+([^\s(]+)/i);
|
|
8265
|
+
else if (op === 'update')
|
|
8266
|
+
match = normalized.match(/\bupdate\s+([^\s(]+)/i);
|
|
8267
|
+
else if (op === 'delete')
|
|
8268
|
+
match = normalized.match(/\bdelete\s+from\s+([^\s(]+)/i);
|
|
8269
|
+
else if (op === 'merge')
|
|
8270
|
+
match = normalized.match(/\bmerge\s+into\s+([^\s(]+)/i);
|
|
8271
|
+
else
|
|
8272
|
+
match = normalized.match(/\bfrom\s+([^\s(]+)/i);
|
|
8273
|
+
return cleanSqlIdentifier(match?.[1]) ?? 'unknown';
|
|
8274
|
+
}
|
|
8275
|
+
function buildPostgresQueryPayload(sql, parameters, source, extra) {
|
|
8276
|
+
return {
|
|
8277
|
+
sql,
|
|
8278
|
+
parameters,
|
|
8279
|
+
library: source.library,
|
|
8280
|
+
driver: source.driver,
|
|
8281
|
+
...(extra ?? {}),
|
|
8282
|
+
};
|
|
8283
|
+
}
|
|
8284
|
+
function summarizePostgresError(error) {
|
|
8285
|
+
return {
|
|
8286
|
+
name: error?.name ?? 'Error',
|
|
8287
|
+
message: error?.message ?? String(error),
|
|
8288
|
+
code: error?.code,
|
|
8289
|
+
severity: error?.severity,
|
|
8290
|
+
detail: error?.detail,
|
|
8291
|
+
hint: error?.hint,
|
|
8292
|
+
position: error?.position,
|
|
8293
|
+
internalPosition: error?.internalPosition,
|
|
8294
|
+
internalQuery: error?.internalQuery,
|
|
8295
|
+
where: error?.where,
|
|
8296
|
+
schema: error?.schema,
|
|
8297
|
+
table: error?.table,
|
|
8298
|
+
column: error?.column,
|
|
8299
|
+
dataType: error?.dataType,
|
|
8300
|
+
constraint: error?.constraint,
|
|
8301
|
+
file: error?.file,
|
|
8302
|
+
line: error?.line,
|
|
8303
|
+
routine: error?.routine,
|
|
8304
|
+
};
|
|
8305
|
+
}
|
|
8306
|
+
function normalizePgQueryInput(config, values) {
|
|
8307
|
+
if (typeof config === 'string') {
|
|
8308
|
+
return { sql: config, parameters: Array.isArray(values) ? values : [] };
|
|
8309
|
+
}
|
|
8310
|
+
const sql = typeof config?.text === 'string' ? config.text : '';
|
|
8311
|
+
const parameters = Array.isArray(config?.values)
|
|
8312
|
+
? config.values
|
|
8313
|
+
: Array.isArray(values)
|
|
8314
|
+
? values
|
|
8315
|
+
: [];
|
|
8316
|
+
return {
|
|
8317
|
+
sql,
|
|
8318
|
+
parameters,
|
|
8319
|
+
extra: {
|
|
8320
|
+
name: typeof config?.name === 'string' ? config.name : undefined,
|
|
8321
|
+
rowMode: config?.rowMode,
|
|
8322
|
+
binary: config?.binary,
|
|
8323
|
+
},
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8326
|
+
function normalizePgResult(result) {
|
|
8327
|
+
if (Array.isArray(result))
|
|
8328
|
+
return result.map(normalizePgResult);
|
|
8329
|
+
if (!result || typeof result !== 'object')
|
|
8330
|
+
return result;
|
|
8331
|
+
return {
|
|
8332
|
+
command: result.command,
|
|
8333
|
+
rowCount: result.rowCount,
|
|
8334
|
+
oid: result.oid,
|
|
8335
|
+
fields: Array.isArray(result.fields)
|
|
8336
|
+
? result.fields.map((field) => ({
|
|
8337
|
+
name: field?.name,
|
|
8338
|
+
tableID: field?.tableID,
|
|
8339
|
+
columnID: field?.columnID,
|
|
8340
|
+
dataTypeID: field?.dataTypeID,
|
|
8341
|
+
dataTypeSize: field?.dataTypeSize,
|
|
8342
|
+
dataTypeModifier: field?.dataTypeModifier,
|
|
8343
|
+
format: field?.format,
|
|
8344
|
+
}))
|
|
8345
|
+
: undefined,
|
|
8346
|
+
rows: result.rows,
|
|
8347
|
+
};
|
|
8348
|
+
}
|
|
8349
|
+
function emitPostgreSqlResult(capture, sql, parameters, startedAt, result, error, extra) {
|
|
8350
|
+
const op = normalizeSqlOperation(sql);
|
|
8351
|
+
emitDbQuery(capture.cfg, capture.sid, capture.aid ?? undefined, {
|
|
8352
|
+
type: 'postgresql',
|
|
8353
|
+
collection: resolveSqlTable(sql, op),
|
|
8354
|
+
op,
|
|
8355
|
+
queryRaw: buildPostgresQueryPayload(sql, parameters, capture.source, extra),
|
|
8356
|
+
resultMetaRaw: error ? undefined : result,
|
|
8357
|
+
error: error ? summarizePostgresError(error) : undefined,
|
|
8358
|
+
durMs: Date.now() - startedAt,
|
|
8359
|
+
t: alignedNow(),
|
|
8360
|
+
spanContext: capture.spanContext,
|
|
8361
|
+
});
|
|
8362
|
+
}
|
|
8363
|
+
function findPgCallbackIndex(args) {
|
|
8364
|
+
for (let i = args.length - 1; i >= 1; i -= 1) {
|
|
8365
|
+
if (typeof args[i] === 'function')
|
|
8366
|
+
return i;
|
|
8367
|
+
}
|
|
8368
|
+
return -1;
|
|
8369
|
+
}
|
|
8370
|
+
function patchPgClientPrototype(proto) {
|
|
8371
|
+
if (!proto || proto[PG_CLIENT_PATCHED])
|
|
8372
|
+
return;
|
|
8373
|
+
const original = proto.query;
|
|
8374
|
+
if (typeof original !== 'function')
|
|
8375
|
+
return;
|
|
8376
|
+
try {
|
|
8377
|
+
Object.defineProperty(proto, PG_CLIENT_PATCHED, { value: true, configurable: true });
|
|
8378
|
+
}
|
|
8379
|
+
catch {
|
|
8380
|
+
return;
|
|
8381
|
+
}
|
|
8382
|
+
proto.query = function reproPatchedPgQuery(...args) {
|
|
8383
|
+
if (pgPoolQueryAls.getStore())
|
|
8384
|
+
return original.apply(this, args);
|
|
8385
|
+
const capture = resolvePostgreSqlCaptureContext({ library: 'pg', driver: 'pg' }, this);
|
|
8386
|
+
if (!capture)
|
|
8387
|
+
return original.apply(this, args);
|
|
8388
|
+
const input = normalizePgQueryInput(args[0], args[1]);
|
|
8389
|
+
const startedAt = Date.now();
|
|
8390
|
+
const callbackIndex = findPgCallbackIndex(args);
|
|
8391
|
+
const actualArgs = callbackIndex >= 0 ? [...args] : args;
|
|
8392
|
+
let emitted = false;
|
|
8393
|
+
const emitSuccess = (result) => {
|
|
8394
|
+
if (emitted)
|
|
8395
|
+
return;
|
|
8396
|
+
emitted = true;
|
|
8397
|
+
emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, normalizePgResult(result), undefined, input.extra);
|
|
8398
|
+
};
|
|
8399
|
+
const emitFailure = (error) => {
|
|
8400
|
+
if (emitted)
|
|
8401
|
+
return;
|
|
8402
|
+
emitted = true;
|
|
8403
|
+
emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, undefined, error, input.extra);
|
|
8404
|
+
};
|
|
8405
|
+
if (callbackIndex >= 0) {
|
|
8406
|
+
const callback = args[callbackIndex];
|
|
8407
|
+
actualArgs[callbackIndex] = function reproPgQueryCallback(error, result, ...rest) {
|
|
8408
|
+
if (error)
|
|
8409
|
+
emitFailure(error);
|
|
8410
|
+
else
|
|
8411
|
+
emitSuccess(result);
|
|
8412
|
+
return callback.call(this, error, result, ...rest);
|
|
8413
|
+
};
|
|
8414
|
+
}
|
|
8415
|
+
try {
|
|
8416
|
+
const returned = original.apply(this, actualArgs);
|
|
8417
|
+
if (returned && typeof returned.then === 'function') {
|
|
8418
|
+
return returned.then((result) => {
|
|
8419
|
+
emitSuccess(result);
|
|
8420
|
+
return result;
|
|
8421
|
+
}, (error) => {
|
|
8422
|
+
emitFailure(error);
|
|
8423
|
+
throw error;
|
|
8424
|
+
});
|
|
8425
|
+
}
|
|
8426
|
+
if (returned && callbackIndex < 0 && typeof returned.once === 'function') {
|
|
8427
|
+
returned.once('end', emitSuccess);
|
|
8428
|
+
returned.once('error', emitFailure);
|
|
8429
|
+
}
|
|
8430
|
+
return returned;
|
|
8431
|
+
}
|
|
8432
|
+
catch (error) {
|
|
8433
|
+
emitFailure(error);
|
|
8434
|
+
throw error;
|
|
8435
|
+
}
|
|
8436
|
+
};
|
|
8437
|
+
}
|
|
8438
|
+
function patchPgPoolPrototype(proto) {
|
|
8439
|
+
if (!proto || proto[PG_POOL_PATCHED])
|
|
8440
|
+
return;
|
|
8441
|
+
const original = proto.query;
|
|
8442
|
+
if (typeof original !== 'function')
|
|
8443
|
+
return;
|
|
8444
|
+
try {
|
|
8445
|
+
Object.defineProperty(proto, PG_POOL_PATCHED, { value: true, configurable: true });
|
|
8446
|
+
}
|
|
8447
|
+
catch {
|
|
8448
|
+
return;
|
|
8449
|
+
}
|
|
8450
|
+
proto.query = function reproPatchedPgPoolQuery(...args) {
|
|
8451
|
+
const capture = resolvePostgreSqlCaptureContext({ library: 'pg', driver: 'pg' }, this);
|
|
8452
|
+
if (!capture)
|
|
8453
|
+
return original.apply(this, args);
|
|
8454
|
+
const input = normalizePgQueryInput(args[0], args[1]);
|
|
8455
|
+
const startedAt = Date.now();
|
|
8456
|
+
const callbackIndex = findPgCallbackIndex(args);
|
|
8457
|
+
const actualArgs = callbackIndex >= 0 ? [...args] : args;
|
|
8458
|
+
let emitted = false;
|
|
8459
|
+
const emitSuccess = (result) => {
|
|
8460
|
+
if (emitted)
|
|
8461
|
+
return;
|
|
8462
|
+
emitted = true;
|
|
8463
|
+
emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, normalizePgResult(result), undefined, input.extra);
|
|
8464
|
+
};
|
|
8465
|
+
const emitFailure = (error) => {
|
|
8466
|
+
if (emitted)
|
|
8467
|
+
return;
|
|
8468
|
+
emitted = true;
|
|
8469
|
+
emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, undefined, error, input.extra);
|
|
8470
|
+
};
|
|
8471
|
+
if (callbackIndex >= 0) {
|
|
8472
|
+
const callback = args[callbackIndex];
|
|
8473
|
+
actualArgs[callbackIndex] = function reproPgPoolQueryCallback(error, result, ...rest) {
|
|
8474
|
+
if (error)
|
|
8475
|
+
emitFailure(error);
|
|
8476
|
+
else
|
|
8477
|
+
emitSuccess(result);
|
|
8478
|
+
return callback.call(this, error, result, ...rest);
|
|
8479
|
+
};
|
|
8480
|
+
}
|
|
8481
|
+
try {
|
|
8482
|
+
const returned = pgPoolQueryAls.run(true, () => original.apply(this, actualArgs));
|
|
8483
|
+
if (returned && typeof returned.then === 'function') {
|
|
8484
|
+
return returned.then((result) => {
|
|
8485
|
+
emitSuccess(result);
|
|
8486
|
+
return result;
|
|
8487
|
+
}, (error) => {
|
|
8488
|
+
emitFailure(error);
|
|
8489
|
+
throw error;
|
|
8490
|
+
});
|
|
8491
|
+
}
|
|
8492
|
+
return returned;
|
|
8493
|
+
}
|
|
8494
|
+
catch (error) {
|
|
8495
|
+
emitFailure(error);
|
|
8496
|
+
throw error;
|
|
8497
|
+
}
|
|
8498
|
+
};
|
|
8499
|
+
}
|
|
8500
|
+
function patchPgFromModuleExports(pgModule) {
|
|
8501
|
+
if (!pgModule)
|
|
8502
|
+
return;
|
|
8503
|
+
const candidates = [pgModule, pgModule.default];
|
|
8504
|
+
for (const candidate of candidates) {
|
|
8505
|
+
patchPgClientPrototype(candidate?.Client?.prototype);
|
|
8506
|
+
patchPgClientPrototype(candidate?.native?.Client?.prototype);
|
|
8507
|
+
patchPgPoolPrototype(candidate?.Pool?.prototype);
|
|
8508
|
+
patchPgPoolPrototype(candidate?.native?.Pool?.prototype);
|
|
8509
|
+
}
|
|
8510
|
+
}
|
|
8511
|
+
function installPgRequireHook() {
|
|
8512
|
+
if (pgRequireHookInstalled)
|
|
8513
|
+
return;
|
|
8514
|
+
pgRequireHookInstalled = true;
|
|
8515
|
+
try {
|
|
8516
|
+
const rim = requireOptionalHostModule('require-in-the-middle');
|
|
8517
|
+
const Hook = rim?.Hook ?? rim;
|
|
8518
|
+
if (typeof Hook !== 'function')
|
|
8519
|
+
return;
|
|
8520
|
+
// eslint-disable-next-line no-new
|
|
8521
|
+
new Hook(['pg'], (exports) => {
|
|
8522
|
+
patchPgFromModuleExports(exports);
|
|
8523
|
+
return exports;
|
|
8524
|
+
});
|
|
8525
|
+
}
|
|
8526
|
+
catch { }
|
|
8527
|
+
}
|
|
8528
|
+
function normalizePostgresJsInput(query) {
|
|
8529
|
+
if (typeof query?.statement?.string === 'string') {
|
|
8530
|
+
return {
|
|
8531
|
+
sql: query.statement.string,
|
|
8532
|
+
parameters: Array.isArray(query?.parameters) ? query.parameters : [],
|
|
8533
|
+
extra: {
|
|
8534
|
+
tagged: query?.tagged === true,
|
|
8535
|
+
parameterTypes: Array.isArray(query?.statement?.types) ? query.statement.types : undefined,
|
|
8536
|
+
},
|
|
8537
|
+
};
|
|
8538
|
+
}
|
|
8539
|
+
const strings = Array.isArray(query?.strings) ? query.strings : [];
|
|
8540
|
+
const parameters = Array.isArray(query?.args) ? query.args : [];
|
|
8541
|
+
if (query?.tagged) {
|
|
8542
|
+
return {
|
|
8543
|
+
sql: strings.map((part, index) => `${String(part)}${index < parameters.length ? `$${index + 1}` : ''}`).join(''),
|
|
8544
|
+
parameters,
|
|
8545
|
+
extra: { tagged: true },
|
|
8546
|
+
};
|
|
8547
|
+
}
|
|
8548
|
+
return {
|
|
8549
|
+
sql: typeof strings[0] === 'string' ? strings[0] : '',
|
|
8550
|
+
parameters,
|
|
8551
|
+
extra: { tagged: false },
|
|
8552
|
+
};
|
|
8553
|
+
}
|
|
8554
|
+
function normalizePostgresJsResult(result) {
|
|
8555
|
+
if (!Array.isArray(result))
|
|
8556
|
+
return result;
|
|
8557
|
+
const metadata = result;
|
|
8558
|
+
return {
|
|
8559
|
+
command: metadata.command,
|
|
8560
|
+
count: metadata.count,
|
|
8561
|
+
columns: Array.isArray(metadata.columns)
|
|
8562
|
+
? metadata.columns.map((column) => ({
|
|
8563
|
+
name: column?.name,
|
|
8564
|
+
type: column?.type,
|
|
8565
|
+
table: column?.table,
|
|
8566
|
+
number: column?.number,
|
|
8567
|
+
}))
|
|
8568
|
+
: undefined,
|
|
8569
|
+
rows: Array.from(result),
|
|
8570
|
+
};
|
|
8571
|
+
}
|
|
8572
|
+
function patchPostgresJsQueryPrototype(proto) {
|
|
8573
|
+
if (!proto || proto[POSTGRES_JS_QUERY_PATCHED])
|
|
8574
|
+
return;
|
|
8575
|
+
const originalHandle = proto.handle;
|
|
8576
|
+
if (typeof originalHandle !== 'function')
|
|
8577
|
+
return;
|
|
8578
|
+
try {
|
|
8579
|
+
Object.defineProperty(proto, POSTGRES_JS_QUERY_PATCHED, { value: true, configurable: true });
|
|
8580
|
+
}
|
|
8581
|
+
catch {
|
|
8582
|
+
return;
|
|
8583
|
+
}
|
|
8584
|
+
proto.handle = function reproPatchedPostgresJsHandle(...args) {
|
|
8585
|
+
if (!this.__repro_postgres_capture_attached) {
|
|
8586
|
+
const capture = resolvePostgreSqlCaptureContext({ library: 'postgres', driver: 'postgres' }, this);
|
|
8587
|
+
if (capture) {
|
|
8588
|
+
try {
|
|
8589
|
+
Object.defineProperty(this, '__repro_postgres_capture_attached', { value: true, configurable: true });
|
|
8590
|
+
}
|
|
8591
|
+
catch {
|
|
8592
|
+
this.__repro_postgres_capture_attached = true;
|
|
8593
|
+
}
|
|
8594
|
+
const input = normalizePostgresJsInput(this);
|
|
8595
|
+
const startedAt = Date.now();
|
|
8596
|
+
Promise.prototype.then.call(this, (result) => {
|
|
8597
|
+
const executedInput = normalizePostgresJsInput(this);
|
|
8598
|
+
emitPostgreSqlResult(capture, executedInput.sql || input.sql, executedInput.parameters, startedAt, normalizePostgresJsResult(result), undefined, executedInput.extra);
|
|
8599
|
+
return result;
|
|
8600
|
+
}, (error) => {
|
|
8601
|
+
const executedInput = normalizePostgresJsInput(this);
|
|
8602
|
+
emitPostgreSqlResult(capture, executedInput.sql || input.sql, executedInput.parameters.length ? executedInput.parameters : input.parameters, startedAt, undefined, error, executedInput.extra);
|
|
8603
|
+
});
|
|
8604
|
+
}
|
|
8605
|
+
}
|
|
8606
|
+
return originalHandle.apply(this, args);
|
|
8607
|
+
};
|
|
8608
|
+
}
|
|
8609
|
+
function patchPostgresJsFromModuleExports(postgresModule) {
|
|
8610
|
+
const factories = new Set();
|
|
8611
|
+
const addFactory = (candidate) => {
|
|
8612
|
+
if (typeof candidate !== 'function')
|
|
8613
|
+
return;
|
|
8614
|
+
// getLoadedModuleExportsByPackage also returns internal postgres.js
|
|
8615
|
+
// helpers. Only the public factory carries these static exports.
|
|
8616
|
+
if (!candidate.PostgresError && !candidate.BigInt && !candidate.__repro_postgres_factory)
|
|
8617
|
+
return;
|
|
8618
|
+
factories.add(candidate);
|
|
8619
|
+
};
|
|
8620
|
+
addFactory(postgresModule);
|
|
8621
|
+
addFactory(postgresModule?.default);
|
|
8622
|
+
for (const factory of factories) {
|
|
8623
|
+
let sql;
|
|
8624
|
+
try {
|
|
8625
|
+
sql = factory({ max: 1, idle_timeout: 1, connect_timeout: 1 });
|
|
8626
|
+
const query = sql.unsafe('select 1');
|
|
8627
|
+
patchPostgresJsQueryPrototype(Object.getPrototypeOf(query));
|
|
8628
|
+
}
|
|
8629
|
+
catch { }
|
|
8630
|
+
try {
|
|
8631
|
+
const ended = sql?.end?.({ timeout: 0 });
|
|
8632
|
+
if (ended && typeof ended.catch === 'function')
|
|
8633
|
+
ended.catch(() => undefined);
|
|
8634
|
+
}
|
|
8635
|
+
catch { }
|
|
8636
|
+
}
|
|
8637
|
+
}
|
|
8638
|
+
function installPostgresJsRequireHook() {
|
|
8639
|
+
if (postgresJsRequireHookInstalled)
|
|
8640
|
+
return;
|
|
8641
|
+
postgresJsRequireHookInstalled = true;
|
|
8642
|
+
try {
|
|
8643
|
+
const rim = requireOptionalHostModule('require-in-the-middle');
|
|
8644
|
+
const Hook = rim?.Hook ?? rim;
|
|
8645
|
+
if (typeof Hook !== 'function')
|
|
8646
|
+
return;
|
|
8647
|
+
// eslint-disable-next-line no-new
|
|
8648
|
+
new Hook(['postgres'], (exports) => {
|
|
8649
|
+
patchPostgresJsFromModuleExports(exports);
|
|
8650
|
+
return exports;
|
|
8651
|
+
});
|
|
8652
|
+
}
|
|
8653
|
+
catch { }
|
|
8654
|
+
}
|
|
8655
|
+
function patchTypeOrmQueryRunner(queryRunner) {
|
|
8656
|
+
if (!queryRunner || queryRunner[TYPEORM_QUERY_RUNNER_PATCHED])
|
|
8657
|
+
return queryRunner;
|
|
8658
|
+
const original = queryRunner.query;
|
|
8659
|
+
if (typeof original !== 'function')
|
|
8660
|
+
return queryRunner;
|
|
8661
|
+
try {
|
|
8662
|
+
Object.defineProperty(queryRunner, TYPEORM_QUERY_RUNNER_PATCHED, { value: true, configurable: true });
|
|
8663
|
+
}
|
|
8664
|
+
catch {
|
|
8665
|
+
return queryRunner;
|
|
8666
|
+
}
|
|
8667
|
+
queryRunner.query = function reproPatchedTypeOrmQuery(...args) {
|
|
8668
|
+
return postgresSqlSourceAls.run({ library: 'typeorm', driver: 'pg' }, () => original.apply(this, args));
|
|
8669
|
+
};
|
|
8670
|
+
return queryRunner;
|
|
8671
|
+
}
|
|
8672
|
+
function patchTypeOrmDataSourcePrototype(proto) {
|
|
8673
|
+
if (!proto || proto[TYPEORM_DATASOURCE_PATCHED])
|
|
8674
|
+
return;
|
|
8675
|
+
const original = proto.createQueryRunner;
|
|
8676
|
+
if (typeof original !== 'function')
|
|
8677
|
+
return;
|
|
8678
|
+
try {
|
|
8679
|
+
Object.defineProperty(proto, TYPEORM_DATASOURCE_PATCHED, { value: true, configurable: true });
|
|
8680
|
+
}
|
|
8681
|
+
catch {
|
|
8682
|
+
return;
|
|
8683
|
+
}
|
|
8684
|
+
proto.createQueryRunner = function reproPatchedCreateQueryRunner(...args) {
|
|
8685
|
+
return patchTypeOrmQueryRunner(original.apply(this, args));
|
|
8686
|
+
};
|
|
8687
|
+
}
|
|
8688
|
+
function patchTypeOrmFromModuleExports(typeOrmModule) {
|
|
8689
|
+
if (!typeOrmModule)
|
|
8690
|
+
return;
|
|
8691
|
+
patchTypeOrmDataSourcePrototype(typeOrmModule?.DataSource?.prototype);
|
|
8692
|
+
patchTypeOrmDataSourcePrototype(typeOrmModule?.Connection?.prototype);
|
|
8693
|
+
patchTypeOrmDataSourcePrototype(typeOrmModule?.default?.DataSource?.prototype);
|
|
8694
|
+
}
|
|
8695
|
+
function installTypeOrmRequireHook() {
|
|
8696
|
+
if (typeOrmRequireHookInstalled)
|
|
8697
|
+
return;
|
|
8698
|
+
typeOrmRequireHookInstalled = true;
|
|
8699
|
+
try {
|
|
8700
|
+
const rim = requireOptionalHostModule('require-in-the-middle');
|
|
8701
|
+
const Hook = rim?.Hook ?? rim;
|
|
8702
|
+
if (typeof Hook !== 'function')
|
|
8703
|
+
return;
|
|
8704
|
+
// eslint-disable-next-line no-new
|
|
8705
|
+
new Hook(['typeorm'], (exports) => {
|
|
8706
|
+
patchTypeOrmFromModuleExports(exports);
|
|
8707
|
+
return exports;
|
|
8708
|
+
});
|
|
8709
|
+
}
|
|
8710
|
+
catch { }
|
|
8711
|
+
}
|
|
8712
|
+
function patchPostgreSql(cfg) {
|
|
8713
|
+
registerPostgreSqlPatchConfig(cfg);
|
|
8714
|
+
installPgRequireHook();
|
|
8715
|
+
installPostgresJsRequireHook();
|
|
8716
|
+
installTypeOrmRequireHook();
|
|
8717
|
+
const pgModules = new Set();
|
|
8718
|
+
const pg = requireOptionalHostModule('pg');
|
|
8719
|
+
if (pg)
|
|
8720
|
+
pgModules.add(pg);
|
|
8721
|
+
for (const loaded of getLoadedModuleExportsByPackage('pg'))
|
|
8722
|
+
pgModules.add(loaded);
|
|
8723
|
+
for (const loaded of pgModules)
|
|
8724
|
+
patchPgFromModuleExports(loaded);
|
|
8725
|
+
const postgresModules = new Set();
|
|
8726
|
+
const postgres = requireOptionalHostModule('postgres');
|
|
8727
|
+
if (postgres)
|
|
8728
|
+
postgresModules.add(postgres);
|
|
8729
|
+
for (const loaded of getLoadedModuleExportsByPackage('postgres'))
|
|
8730
|
+
postgresModules.add(loaded);
|
|
8731
|
+
for (const loaded of postgresModules)
|
|
8732
|
+
patchPostgresJsFromModuleExports(loaded);
|
|
8733
|
+
const typeOrmModules = new Set();
|
|
8734
|
+
const typeOrm = requireOptionalHostModule('typeorm');
|
|
8735
|
+
if (typeOrm)
|
|
8736
|
+
typeOrmModules.add(typeOrm);
|
|
8737
|
+
for (const loaded of getLoadedModuleExportsByPackage('typeorm'))
|
|
8738
|
+
typeOrmModules.add(loaded);
|
|
8739
|
+
for (const loaded of typeOrmModules)
|
|
8740
|
+
patchTypeOrmFromModuleExports(loaded);
|
|
8741
|
+
}
|
|
8742
|
+
exports.patchPostgreSql = patchPostgreSql;
|
|
7735
8743
|
const mongoDbPatchConfigs = [];
|
|
7736
8744
|
let mongoDbRequireHookInstalled = false;
|
|
7737
8745
|
let mongoDbImportHookInstalled = false;
|
|
@@ -8801,9 +9809,11 @@ async function initRepro(cfg) {
|
|
|
8801
9809
|
const ingestCfg = toIngestConfig(cfg);
|
|
8802
9810
|
reproAutoMiddlewareConfig = {
|
|
8803
9811
|
...ingestCfg,
|
|
9812
|
+
apiBase: cfg.apiBase,
|
|
8804
9813
|
captureHeaders: cfg.captureHeaders,
|
|
8805
9814
|
masking: cfg.masking,
|
|
8806
9815
|
privacy: cfg.privacy,
|
|
9816
|
+
liveMode: cfg.liveMode,
|
|
8807
9817
|
};
|
|
8808
9818
|
const integrationCfg = {
|
|
8809
9819
|
...reproAutoMiddlewareConfig,
|
|
@@ -8820,6 +9830,7 @@ async function initRepro(cfg) {
|
|
|
8820
9830
|
patchKafkaJs(integrationCfg);
|
|
8821
9831
|
patchAmqplib(integrationCfg);
|
|
8822
9832
|
patchRedis(integrationCfg);
|
|
9833
|
+
patchPostgreSql(integrationCfg);
|
|
8823
9834
|
patchMongoDb(integrationCfg);
|
|
8824
9835
|
await installMongoDbImportHook();
|
|
8825
9836
|
patchSendgridMail(integrationCfg);
|
|
@@ -8855,6 +9866,9 @@ exports.__reproTestHooks = {
|
|
|
8855
9866
|
lastClientActivityAt = 0;
|
|
8856
9867
|
sdkBackgroundQuietUntil = 0;
|
|
8857
9868
|
},
|
|
9869
|
+
setBackendLiveTimingForTest(override) {
|
|
9870
|
+
backendLiveTimingOverrideForTest = override;
|
|
9871
|
+
},
|
|
8858
9872
|
runWithRequestContextForTest(ctx, fn) {
|
|
8859
9873
|
return als.run({
|
|
8860
9874
|
...ctx,
|
|
@@ -8879,6 +9893,9 @@ exports.__reproTestHooks = {
|
|
|
8879
9893
|
patchAmqplibConnectionForTest: patchAmqplibConnection,
|
|
8880
9894
|
patchAmqplibChannelForTest: patchAmqplibChannel,
|
|
8881
9895
|
wrapAmqplibConsumeHandlerForTest: wrapAmqplibConsumeHandler,
|
|
9896
|
+
patchPgModuleForTest: patchPgFromModuleExports,
|
|
9897
|
+
patchPostgresJsModuleForTest: patchPostgresJsFromModuleExports,
|
|
9898
|
+
patchTypeOrmModuleForTest: patchTypeOrmFromModuleExports,
|
|
8882
9899
|
patchMongoDbModuleForTest: patchMongoDbFromModuleExports,
|
|
8883
9900
|
patchMongoCollectionPrototypeForTest: patchMongoCollectionPrototype,
|
|
8884
9901
|
chunkTraceEventsForTransportForTest: chunkTraceEventsForTransport,
|