@reproapp/node-sdk 0.0.39 → 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/src/index.ts CHANGED
@@ -1223,6 +1223,35 @@ function buildRequestCaptureManifestPayload(
1223
1223
  };
1224
1224
  }
1225
1225
 
1226
+ function emitRequestCaptureStart(
1227
+ cfg: IngestClientConfig,
1228
+ sessionId: string | null | undefined,
1229
+ actionId: string | null | undefined,
1230
+ scope: RequestCaptureScopeState,
1231
+ openedAtMs?: number | null,
1232
+ ): void {
1233
+ if (!sessionId) {
1234
+ return;
1235
+ }
1236
+ const openedAt =
1237
+ typeof openedAtMs === 'number' && Number.isFinite(openedAtMs)
1238
+ ? openedAtMs
1239
+ : alignedNow();
1240
+ post(cfg, sessionId, {
1241
+ entries: [{
1242
+ actionId: actionId ?? null,
1243
+ requestRid: scope.requestRid,
1244
+ requestCaptureStart: {
1245
+ scopeId: `req:${scope.requestRid}`,
1246
+ requestRid: scope.requestRid,
1247
+ transport: scope.transport,
1248
+ openedAt,
1249
+ },
1250
+ t: openedAt,
1251
+ }],
1252
+ }, { priority: 'high' });
1253
+ }
1254
+
1226
1255
  const als = new AsyncLocalStorage<Ctx>();
1227
1256
  const getCtx = () => als.getStore() || {};
1228
1257
 
@@ -5712,6 +5741,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
5712
5741
  const clockSkewMs = headerTs !== null ? headerTs - requestStartRaw : 0;
5713
5742
  const rid = nextRequestId(requestEpochMs);
5714
5743
  const captureScope = createRequestCaptureScope(rid, 'http');
5744
+ emitRequestCaptureStart(captureCfg, sid, aid, captureScope, requestEpochMs);
5715
5745
  const t0 = requestStartRaw;
5716
5746
  const url = (req as any).originalUrl || req.url || '/';
5717
5747
  const urlPathOnly = (url || '/').split('?')[0] || '/';
@@ -7810,6 +7840,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
7810
7840
  const span = captureSpanContextFromTracer();
7811
7841
  const publishRid = nextRequestId(alignedNow());
7812
7842
  const captureScope = createRequestCaptureScope(publishRid, 'kafka');
7843
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
7813
7844
  const payloadObj = payload && typeof payload === 'object' ? payload : {};
7814
7845
  const topic = sanitizeKafkaTopic((payloadObj as any).topic);
7815
7846
  const rawMessages = Array.isArray((payloadObj as any).messages) ? (payloadObj as any).messages : [];
@@ -7924,6 +7955,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
7924
7955
  const span = captureSpanContextFromTracer();
7925
7956
  const publishRid = nextRequestId(alignedNow());
7926
7957
  const captureScope = createRequestCaptureScope(publishRid, 'kafka');
7958
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
7927
7959
 
7928
7960
  const payloadObj = payload && typeof payload === 'object' ? payload : {};
7929
7961
  const topicMessages = Array.isArray((payloadObj as any).topicMessages)
@@ -8074,6 +8106,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
8074
8106
  const consumeRid = nextRequestId(`${topic}-${message?.offset ?? alignedNow()}`);
8075
8107
  const traceId = consumeRid;
8076
8108
  const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
8109
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
8077
8110
  const rawTraceEvents: any[] = [];
8078
8111
  const maskReq: MaskRequestContext = {
8079
8112
  method: 'KAFKA_CONSUME',
@@ -8184,6 +8217,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
8184
8217
  const consumeRid = nextRequestId(`${topic}-batch-${alignedNow()}`);
8185
8218
  const traceId = consumeRid;
8186
8219
  const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
8220
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
8187
8221
  const rawTraceEvents: any[] = [];
8188
8222
  const maskReq: MaskRequestContext = {
8189
8223
  method: 'KAFKA_CONSUME_BATCH',
@@ -8891,6 +8925,7 @@ function patchAmqplibPublishMethod(
8891
8925
  const key = `${method} ${methodName === 'publish' ? `${exchange || 'default'}/${routingKey}` : queue}`;
8892
8926
  const content = methodName === 'publish' ? args[2] : args[1];
8893
8927
  const captureScope = createRequestCaptureScope(publishRid, 'amqp');
8928
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
8894
8929
  const t0 = Date.now();
8895
8930
  let finished = false;
8896
8931
 
@@ -8980,6 +9015,7 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
8980
9015
  const consumeRid = nextRequestId(`${queueName}-${deliveryTag}`);
8981
9016
  const traceId = consumeRid;
8982
9017
  const captureScope = createRequestCaptureScope(consumeRid, 'amqp');
9018
+ emitRequestCaptureStart(cfg, sid, aid, captureScope);
8983
9019
  const rawTraceEvents: any[] = [];
8984
9020
  const maskReq: MaskRequestContext = {
8985
9021
  method: 'AMQP_CONSUME',
@@ -9583,6 +9619,616 @@ export function patchRedis(cfg: RedisPatchConfig) {
9583
9619
  }
9584
9620
  }
9585
9621
 
9622
+ // ===================================================================
9623
+ // PostgreSQL integrations: pg, postgres.js, and TypeORM
9624
+ // ===================================================================
9625
+ export type PostgreSqlPatchConfig = ReproMiddlewareConfig & {
9626
+ resolveContext?: () => { sid?: string; aid?: string } | undefined;
9627
+ };
9628
+
9629
+ type PostgreSqlSource = {
9630
+ library: 'pg' | 'postgres' | 'typeorm';
9631
+ driver: 'pg' | 'postgres';
9632
+ };
9633
+
9634
+ type PostgreSqlCaptureContext = {
9635
+ cfg: PostgreSqlPatchConfig;
9636
+ sid: string;
9637
+ aid: string | null;
9638
+ spanContext: SpanContext | null;
9639
+ source: PostgreSqlSource;
9640
+ };
9641
+
9642
+ const postgresSqlPatchConfigs: PostgreSqlPatchConfig[] = [];
9643
+ const postgresSqlSourceAls = new AsyncLocalStorage<PostgreSqlSource>();
9644
+ const pgPoolQueryAls = new AsyncLocalStorage<boolean>();
9645
+ let pgRequireHookInstalled = false;
9646
+ let postgresJsRequireHookInstalled = false;
9647
+ let typeOrmRequireHookInstalled = false;
9648
+
9649
+ const PG_CLIENT_PATCHED = Symbol.for('@reproapp/node-sdk/pg-client-patched');
9650
+ const PG_POOL_PATCHED = Symbol.for('@reproapp/node-sdk/pg-pool-patched');
9651
+ const POSTGRES_JS_QUERY_PATCHED = Symbol.for('@reproapp/node-sdk/postgres-js-query-patched');
9652
+ const TYPEORM_DATASOURCE_PATCHED = Symbol.for('@reproapp/node-sdk/typeorm-datasource-patched');
9653
+ const TYPEORM_QUERY_RUNNER_PATCHED = Symbol.for('@reproapp/node-sdk/typeorm-query-runner-patched');
9654
+
9655
+ function registerPostgreSqlPatchConfig(cfg: PostgreSqlPatchConfig): void {
9656
+ if (!postgresSqlPatchConfigs.includes(cfg)) {
9657
+ postgresSqlPatchConfigs.push(cfg);
9658
+ }
9659
+ }
9660
+
9661
+ function resolvePostgreSqlCaptureContext(defaultSource: PostgreSqlSource, source?: any): PostgreSqlCaptureContext | null {
9662
+ const ctx = getCtx();
9663
+ const contextualSource = postgresSqlSourceAls.getStore();
9664
+ for (let i = postgresSqlPatchConfigs.length - 1; i >= 0; i -= 1) {
9665
+ const cfg = postgresSqlPatchConfigs[i];
9666
+ if (!cfg) continue;
9667
+ const fallback = cfg.resolveContext?.() ?? {};
9668
+ const sid = ctx.sid ?? fallback.sid ?? null;
9669
+ if (!sid) continue;
9670
+ return {
9671
+ cfg,
9672
+ sid,
9673
+ aid: ctx.aid ?? fallback.aid ?? null,
9674
+ spanContext: captureSpanContextFromTracer(source),
9675
+ source: contextualSource ?? defaultSource,
9676
+ };
9677
+ }
9678
+ return null;
9679
+ }
9680
+
9681
+ function stripSqlComments(sql: string): string {
9682
+ let remaining = sql.trimStart();
9683
+ while (remaining.length) {
9684
+ if (remaining.startsWith('--')) {
9685
+ const newline = remaining.indexOf('\n');
9686
+ remaining = newline < 0 ? '' : remaining.slice(newline + 1).trimStart();
9687
+ continue;
9688
+ }
9689
+ if (remaining.startsWith('/*')) {
9690
+ const end = remaining.indexOf('*/', 2);
9691
+ remaining = end < 0 ? '' : remaining.slice(end + 2).trimStart();
9692
+ continue;
9693
+ }
9694
+ break;
9695
+ }
9696
+ return remaining;
9697
+ }
9698
+
9699
+ function normalizeSqlOperation(sql: string): string {
9700
+ const normalized = stripSqlComments(sql);
9701
+ const first = normalized.match(/^([a-z]+)/i)?.[1]?.toLowerCase() ?? 'query';
9702
+ if (first !== 'with') return first;
9703
+
9704
+ // CTEs can wrap any DML statement. The last top-level DML keyword is the
9705
+ // operation users care about, while nested SELECTs only build the CTE.
9706
+ const matches = Array.from(normalized.matchAll(/\b(select|insert|update|delete|merge)\b/gi));
9707
+ return matches.length ? String(matches[matches.length - 1][1]).toLowerCase() : 'query';
9708
+ }
9709
+
9710
+ function cleanSqlIdentifier(value: string | undefined): string | undefined {
9711
+ if (!value) return undefined;
9712
+ const cleaned = value
9713
+ .trim()
9714
+ .replace(/[;,]$/, '')
9715
+ .split('.')
9716
+ .map((part) => part.replace(/^['"`]|['"`]$/g, ''))
9717
+ .join('.');
9718
+ return cleaned || undefined;
9719
+ }
9720
+
9721
+ function resolveSqlTable(sql: string, op: string): string {
9722
+ const normalized = stripSqlComments(sql);
9723
+ let match: RegExpMatchArray | null = null;
9724
+ if (op === 'insert') match = normalized.match(/\binsert\s+into\s+([^\s(]+)/i);
9725
+ else if (op === 'update') match = normalized.match(/\bupdate\s+([^\s(]+)/i);
9726
+ else if (op === 'delete') match = normalized.match(/\bdelete\s+from\s+([^\s(]+)/i);
9727
+ else if (op === 'merge') match = normalized.match(/\bmerge\s+into\s+([^\s(]+)/i);
9728
+ else match = normalized.match(/\bfrom\s+([^\s(]+)/i);
9729
+ return cleanSqlIdentifier(match?.[1]) ?? 'unknown';
9730
+ }
9731
+
9732
+ function buildPostgresQueryPayload(
9733
+ sql: string,
9734
+ parameters: any[],
9735
+ source: PostgreSqlSource,
9736
+ extra?: Record<string, any>,
9737
+ ): Record<string, any> {
9738
+ return {
9739
+ sql,
9740
+ parameters,
9741
+ library: source.library,
9742
+ driver: source.driver,
9743
+ ...(extra ?? {}),
9744
+ };
9745
+ }
9746
+
9747
+ function summarizePostgresError(error: any): Record<string, any> {
9748
+ return {
9749
+ name: error?.name ?? 'Error',
9750
+ message: error?.message ?? String(error),
9751
+ code: error?.code,
9752
+ severity: error?.severity,
9753
+ detail: error?.detail,
9754
+ hint: error?.hint,
9755
+ position: error?.position,
9756
+ internalPosition: error?.internalPosition,
9757
+ internalQuery: error?.internalQuery,
9758
+ where: error?.where,
9759
+ schema: error?.schema,
9760
+ table: error?.table,
9761
+ column: error?.column,
9762
+ dataType: error?.dataType,
9763
+ constraint: error?.constraint,
9764
+ file: error?.file,
9765
+ line: error?.line,
9766
+ routine: error?.routine,
9767
+ };
9768
+ }
9769
+
9770
+ function normalizePgQueryInput(config: any, values: any): { sql: string; parameters: any[]; extra?: Record<string, any> } {
9771
+ if (typeof config === 'string') {
9772
+ return { sql: config, parameters: Array.isArray(values) ? values : [] };
9773
+ }
9774
+ const sql = typeof config?.text === 'string' ? config.text : '';
9775
+ const parameters = Array.isArray(config?.values)
9776
+ ? config.values
9777
+ : Array.isArray(values)
9778
+ ? values
9779
+ : [];
9780
+ return {
9781
+ sql,
9782
+ parameters,
9783
+ extra: {
9784
+ name: typeof config?.name === 'string' ? config.name : undefined,
9785
+ rowMode: config?.rowMode,
9786
+ binary: config?.binary,
9787
+ },
9788
+ };
9789
+ }
9790
+
9791
+ function normalizePgResult(result: any): any {
9792
+ if (Array.isArray(result)) return result.map(normalizePgResult);
9793
+ if (!result || typeof result !== 'object') return result;
9794
+ return {
9795
+ command: result.command,
9796
+ rowCount: result.rowCount,
9797
+ oid: result.oid,
9798
+ fields: Array.isArray(result.fields)
9799
+ ? result.fields.map((field: any) => ({
9800
+ name: field?.name,
9801
+ tableID: field?.tableID,
9802
+ columnID: field?.columnID,
9803
+ dataTypeID: field?.dataTypeID,
9804
+ dataTypeSize: field?.dataTypeSize,
9805
+ dataTypeModifier: field?.dataTypeModifier,
9806
+ format: field?.format,
9807
+ }))
9808
+ : undefined,
9809
+ rows: result.rows,
9810
+ };
9811
+ }
9812
+
9813
+ function emitPostgreSqlResult(
9814
+ capture: PostgreSqlCaptureContext,
9815
+ sql: string,
9816
+ parameters: any[],
9817
+ startedAt: number,
9818
+ result: any,
9819
+ error?: any,
9820
+ extra?: Record<string, any>,
9821
+ ): void {
9822
+ const op = normalizeSqlOperation(sql);
9823
+ emitDbQuery(capture.cfg, capture.sid, capture.aid ?? undefined, {
9824
+ type: 'postgresql',
9825
+ collection: resolveSqlTable(sql, op),
9826
+ op,
9827
+ queryRaw: buildPostgresQueryPayload(sql, parameters, capture.source, extra),
9828
+ resultMetaRaw: error ? undefined : result,
9829
+ error: error ? summarizePostgresError(error) : undefined,
9830
+ durMs: Date.now() - startedAt,
9831
+ t: alignedNow(),
9832
+ spanContext: capture.spanContext,
9833
+ });
9834
+ }
9835
+
9836
+ function findPgCallbackIndex(args: any[]): number {
9837
+ for (let i = args.length - 1; i >= 1; i -= 1) {
9838
+ if (typeof args[i] === 'function') return i;
9839
+ }
9840
+ return -1;
9841
+ }
9842
+
9843
+ function patchPgClientPrototype(proto: any): void {
9844
+ if (!proto || proto[PG_CLIENT_PATCHED]) return;
9845
+ const original = proto.query;
9846
+ if (typeof original !== 'function') return;
9847
+ try {
9848
+ Object.defineProperty(proto, PG_CLIENT_PATCHED, { value: true, configurable: true });
9849
+ } catch {
9850
+ return;
9851
+ }
9852
+
9853
+ proto.query = function reproPatchedPgQuery(this: any, ...args: any[]) {
9854
+ if (pgPoolQueryAls.getStore()) return original.apply(this, args);
9855
+ const capture = resolvePostgreSqlCaptureContext({ library: 'pg', driver: 'pg' }, this);
9856
+ if (!capture) return original.apply(this, args);
9857
+
9858
+ const input = normalizePgQueryInput(args[0], args[1]);
9859
+ const startedAt = Date.now();
9860
+ const callbackIndex = findPgCallbackIndex(args);
9861
+ const actualArgs = callbackIndex >= 0 ? [...args] : args;
9862
+ let emitted = false;
9863
+ const emitSuccess = (result: any) => {
9864
+ if (emitted) return;
9865
+ emitted = true;
9866
+ emitPostgreSqlResult(
9867
+ capture,
9868
+ input.sql,
9869
+ input.parameters,
9870
+ startedAt,
9871
+ normalizePgResult(result),
9872
+ undefined,
9873
+ input.extra,
9874
+ );
9875
+ };
9876
+ const emitFailure = (error: any) => {
9877
+ if (emitted) return;
9878
+ emitted = true;
9879
+ emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, undefined, error, input.extra);
9880
+ };
9881
+
9882
+ if (callbackIndex >= 0) {
9883
+ const callback = args[callbackIndex];
9884
+ actualArgs[callbackIndex] = function reproPgQueryCallback(this: any, error: any, result: any, ...rest: any[]) {
9885
+ if (error) emitFailure(error);
9886
+ else emitSuccess(result);
9887
+ return callback.call(this, error, result, ...rest);
9888
+ };
9889
+ }
9890
+
9891
+ try {
9892
+ const returned = original.apply(this, actualArgs);
9893
+ if (returned && typeof returned.then === 'function') {
9894
+ return returned.then(
9895
+ (result: any) => {
9896
+ emitSuccess(result);
9897
+ return result;
9898
+ },
9899
+ (error: any) => {
9900
+ emitFailure(error);
9901
+ throw error;
9902
+ },
9903
+ );
9904
+ }
9905
+ if (returned && callbackIndex < 0 && typeof returned.once === 'function') {
9906
+ returned.once('end', emitSuccess);
9907
+ returned.once('error', emitFailure);
9908
+ }
9909
+ return returned;
9910
+ } catch (error) {
9911
+ emitFailure(error);
9912
+ throw error;
9913
+ }
9914
+ };
9915
+ }
9916
+
9917
+ function patchPgPoolPrototype(proto: any): void {
9918
+ if (!proto || proto[PG_POOL_PATCHED]) return;
9919
+ const original = proto.query;
9920
+ if (typeof original !== 'function') return;
9921
+ try {
9922
+ Object.defineProperty(proto, PG_POOL_PATCHED, { value: true, configurable: true });
9923
+ } catch {
9924
+ return;
9925
+ }
9926
+
9927
+ proto.query = function reproPatchedPgPoolQuery(this: any, ...args: any[]) {
9928
+ const capture = resolvePostgreSqlCaptureContext({ library: 'pg', driver: 'pg' }, this);
9929
+ if (!capture) return original.apply(this, args);
9930
+
9931
+ const input = normalizePgQueryInput(args[0], args[1]);
9932
+ const startedAt = Date.now();
9933
+ const callbackIndex = findPgCallbackIndex(args);
9934
+ const actualArgs = callbackIndex >= 0 ? [...args] : args;
9935
+ let emitted = false;
9936
+ const emitSuccess = (result: any) => {
9937
+ if (emitted) return;
9938
+ emitted = true;
9939
+ emitPostgreSqlResult(
9940
+ capture,
9941
+ input.sql,
9942
+ input.parameters,
9943
+ startedAt,
9944
+ normalizePgResult(result),
9945
+ undefined,
9946
+ input.extra,
9947
+ );
9948
+ };
9949
+ const emitFailure = (error: any) => {
9950
+ if (emitted) return;
9951
+ emitted = true;
9952
+ emitPostgreSqlResult(capture, input.sql, input.parameters, startedAt, undefined, error, input.extra);
9953
+ };
9954
+
9955
+ if (callbackIndex >= 0) {
9956
+ const callback = args[callbackIndex];
9957
+ actualArgs[callbackIndex] = function reproPgPoolQueryCallback(this: any, error: any, result: any, ...rest: any[]) {
9958
+ if (error) emitFailure(error);
9959
+ else emitSuccess(result);
9960
+ return callback.call(this, error, result, ...rest);
9961
+ };
9962
+ }
9963
+
9964
+ try {
9965
+ const returned = pgPoolQueryAls.run(true, () => original.apply(this, actualArgs));
9966
+ if (returned && typeof returned.then === 'function') {
9967
+ return returned.then(
9968
+ (result: any) => {
9969
+ emitSuccess(result);
9970
+ return result;
9971
+ },
9972
+ (error: any) => {
9973
+ emitFailure(error);
9974
+ throw error;
9975
+ },
9976
+ );
9977
+ }
9978
+ return returned;
9979
+ } catch (error) {
9980
+ emitFailure(error);
9981
+ throw error;
9982
+ }
9983
+ };
9984
+ }
9985
+
9986
+ function patchPgFromModuleExports(pgModule: any): void {
9987
+ if (!pgModule) return;
9988
+ const candidates = [pgModule, pgModule.default];
9989
+ for (const candidate of candidates) {
9990
+ patchPgClientPrototype(candidate?.Client?.prototype);
9991
+ patchPgClientPrototype(candidate?.native?.Client?.prototype);
9992
+ patchPgPoolPrototype(candidate?.Pool?.prototype);
9993
+ patchPgPoolPrototype(candidate?.native?.Pool?.prototype);
9994
+ }
9995
+ }
9996
+
9997
+ function installPgRequireHook(): void {
9998
+ if (pgRequireHookInstalled) return;
9999
+ pgRequireHookInstalled = true;
10000
+ try {
10001
+ const rim = requireOptionalHostModule('require-in-the-middle');
10002
+ const Hook = rim?.Hook ?? rim;
10003
+ if (typeof Hook !== 'function') return;
10004
+ // eslint-disable-next-line no-new
10005
+ new Hook(['pg'], (exports: any) => {
10006
+ patchPgFromModuleExports(exports);
10007
+ return exports;
10008
+ });
10009
+ } catch {}
10010
+ }
10011
+
10012
+ function normalizePostgresJsInput(query: any): { sql: string; parameters: any[]; extra: Record<string, any> } {
10013
+ if (typeof query?.statement?.string === 'string') {
10014
+ return {
10015
+ sql: query.statement.string,
10016
+ parameters: Array.isArray(query?.parameters) ? query.parameters : [],
10017
+ extra: {
10018
+ tagged: query?.tagged === true,
10019
+ parameterTypes: Array.isArray(query?.statement?.types) ? query.statement.types : undefined,
10020
+ },
10021
+ };
10022
+ }
10023
+ const strings = Array.isArray(query?.strings) ? query.strings : [];
10024
+ const parameters = Array.isArray(query?.args) ? query.args : [];
10025
+ if (query?.tagged) {
10026
+ return {
10027
+ sql: strings.map((part: any, index: number) => `${String(part)}${index < parameters.length ? `$${index + 1}` : ''}`).join(''),
10028
+ parameters,
10029
+ extra: { tagged: true },
10030
+ };
10031
+ }
10032
+ return {
10033
+ sql: typeof strings[0] === 'string' ? strings[0] : '',
10034
+ parameters,
10035
+ extra: { tagged: false },
10036
+ };
10037
+ }
10038
+
10039
+ function normalizePostgresJsResult(result: any): any {
10040
+ if (!Array.isArray(result)) return result;
10041
+ const metadata = result as any;
10042
+ return {
10043
+ command: metadata.command,
10044
+ count: metadata.count,
10045
+ columns: Array.isArray(metadata.columns)
10046
+ ? metadata.columns.map((column: any) => ({
10047
+ name: column?.name,
10048
+ type: column?.type,
10049
+ table: column?.table,
10050
+ number: column?.number,
10051
+ }))
10052
+ : undefined,
10053
+ rows: Array.from(result),
10054
+ };
10055
+ }
10056
+
10057
+ function patchPostgresJsQueryPrototype(proto: any): void {
10058
+ if (!proto || proto[POSTGRES_JS_QUERY_PATCHED]) return;
10059
+ const originalHandle = proto.handle;
10060
+ if (typeof originalHandle !== 'function') return;
10061
+ try {
10062
+ Object.defineProperty(proto, POSTGRES_JS_QUERY_PATCHED, { value: true, configurable: true });
10063
+ } catch {
10064
+ return;
10065
+ }
10066
+
10067
+ proto.handle = function reproPatchedPostgresJsHandle(this: any, ...args: any[]) {
10068
+ if (!this.__repro_postgres_capture_attached) {
10069
+ const capture = resolvePostgreSqlCaptureContext({ library: 'postgres', driver: 'postgres' }, this);
10070
+ if (capture) {
10071
+ try {
10072
+ Object.defineProperty(this, '__repro_postgres_capture_attached', { value: true, configurable: true });
10073
+ } catch {
10074
+ this.__repro_postgres_capture_attached = true;
10075
+ }
10076
+ const input = normalizePostgresJsInput(this);
10077
+ const startedAt = Date.now();
10078
+ Promise.prototype.then.call(
10079
+ this,
10080
+ (result: any) => {
10081
+ const executedInput = normalizePostgresJsInput(this);
10082
+ emitPostgreSqlResult(
10083
+ capture,
10084
+ executedInput.sql || input.sql,
10085
+ executedInput.parameters,
10086
+ startedAt,
10087
+ normalizePostgresJsResult(result),
10088
+ undefined,
10089
+ executedInput.extra,
10090
+ );
10091
+ return result;
10092
+ },
10093
+ (error: any) => {
10094
+ const executedInput = normalizePostgresJsInput(this);
10095
+ emitPostgreSqlResult(
10096
+ capture,
10097
+ executedInput.sql || input.sql,
10098
+ executedInput.parameters.length ? executedInput.parameters : input.parameters,
10099
+ startedAt,
10100
+ undefined,
10101
+ error,
10102
+ executedInput.extra,
10103
+ );
10104
+ },
10105
+ );
10106
+ }
10107
+ }
10108
+ return originalHandle.apply(this, args);
10109
+ };
10110
+ }
10111
+
10112
+ function patchPostgresJsFromModuleExports(postgresModule: any): void {
10113
+ const factories = new Set<any>();
10114
+ const addFactory = (candidate: any) => {
10115
+ if (typeof candidate !== 'function') return;
10116
+ // getLoadedModuleExportsByPackage also returns internal postgres.js
10117
+ // helpers. Only the public factory carries these static exports.
10118
+ if (!candidate.PostgresError && !candidate.BigInt && !candidate.__repro_postgres_factory) return;
10119
+ factories.add(candidate);
10120
+ };
10121
+ addFactory(postgresModule);
10122
+ addFactory(postgresModule?.default);
10123
+
10124
+ for (const factory of factories) {
10125
+ let sql: any;
10126
+ try {
10127
+ sql = factory({ max: 1, idle_timeout: 1, connect_timeout: 1 });
10128
+ const query = sql.unsafe('select 1');
10129
+ patchPostgresJsQueryPrototype(Object.getPrototypeOf(query));
10130
+ } catch {}
10131
+ try {
10132
+ const ended = sql?.end?.({ timeout: 0 });
10133
+ if (ended && typeof ended.catch === 'function') ended.catch(() => undefined);
10134
+ } catch {}
10135
+ }
10136
+ }
10137
+
10138
+ function installPostgresJsRequireHook(): void {
10139
+ if (postgresJsRequireHookInstalled) return;
10140
+ postgresJsRequireHookInstalled = true;
10141
+ try {
10142
+ const rim = requireOptionalHostModule('require-in-the-middle');
10143
+ const Hook = rim?.Hook ?? rim;
10144
+ if (typeof Hook !== 'function') return;
10145
+ // eslint-disable-next-line no-new
10146
+ new Hook(['postgres'], (exports: any) => {
10147
+ patchPostgresJsFromModuleExports(exports);
10148
+ return exports;
10149
+ });
10150
+ } catch {}
10151
+ }
10152
+
10153
+ function patchTypeOrmQueryRunner(queryRunner: any): any {
10154
+ if (!queryRunner || queryRunner[TYPEORM_QUERY_RUNNER_PATCHED]) return queryRunner;
10155
+ const original = queryRunner.query;
10156
+ if (typeof original !== 'function') return queryRunner;
10157
+ try {
10158
+ Object.defineProperty(queryRunner, TYPEORM_QUERY_RUNNER_PATCHED, { value: true, configurable: true });
10159
+ } catch {
10160
+ return queryRunner;
10161
+ }
10162
+ queryRunner.query = function reproPatchedTypeOrmQuery(this: any, ...args: any[]) {
10163
+ return postgresSqlSourceAls.run(
10164
+ { library: 'typeorm', driver: 'pg' },
10165
+ () => original.apply(this, args),
10166
+ );
10167
+ };
10168
+ return queryRunner;
10169
+ }
10170
+
10171
+ function patchTypeOrmDataSourcePrototype(proto: any): void {
10172
+ if (!proto || proto[TYPEORM_DATASOURCE_PATCHED]) return;
10173
+ const original = proto.createQueryRunner;
10174
+ if (typeof original !== 'function') return;
10175
+ try {
10176
+ Object.defineProperty(proto, TYPEORM_DATASOURCE_PATCHED, { value: true, configurable: true });
10177
+ } catch {
10178
+ return;
10179
+ }
10180
+ proto.createQueryRunner = function reproPatchedCreateQueryRunner(this: any, ...args: any[]) {
10181
+ return patchTypeOrmQueryRunner(original.apply(this, args));
10182
+ };
10183
+ }
10184
+
10185
+ function patchTypeOrmFromModuleExports(typeOrmModule: any): void {
10186
+ if (!typeOrmModule) return;
10187
+ patchTypeOrmDataSourcePrototype(typeOrmModule?.DataSource?.prototype);
10188
+ patchTypeOrmDataSourcePrototype(typeOrmModule?.Connection?.prototype);
10189
+ patchTypeOrmDataSourcePrototype(typeOrmModule?.default?.DataSource?.prototype);
10190
+ }
10191
+
10192
+ function installTypeOrmRequireHook(): void {
10193
+ if (typeOrmRequireHookInstalled) return;
10194
+ typeOrmRequireHookInstalled = true;
10195
+ try {
10196
+ const rim = requireOptionalHostModule('require-in-the-middle');
10197
+ const Hook = rim?.Hook ?? rim;
10198
+ if (typeof Hook !== 'function') return;
10199
+ // eslint-disable-next-line no-new
10200
+ new Hook(['typeorm'], (exports: any) => {
10201
+ patchTypeOrmFromModuleExports(exports);
10202
+ return exports;
10203
+ });
10204
+ } catch {}
10205
+ }
10206
+
10207
+ export function patchPostgreSql(cfg: PostgreSqlPatchConfig): void {
10208
+ registerPostgreSqlPatchConfig(cfg);
10209
+ installPgRequireHook();
10210
+ installPostgresJsRequireHook();
10211
+ installTypeOrmRequireHook();
10212
+
10213
+ const pgModules = new Set<any>();
10214
+ const pg = requireOptionalHostModule('pg');
10215
+ if (pg) pgModules.add(pg);
10216
+ for (const loaded of getLoadedModuleExportsByPackage('pg')) pgModules.add(loaded);
10217
+ for (const loaded of pgModules) patchPgFromModuleExports(loaded);
10218
+
10219
+ const postgresModules = new Set<any>();
10220
+ const postgres = requireOptionalHostModule('postgres');
10221
+ if (postgres) postgresModules.add(postgres);
10222
+ for (const loaded of getLoadedModuleExportsByPackage('postgres')) postgresModules.add(loaded);
10223
+ for (const loaded of postgresModules) patchPostgresJsFromModuleExports(loaded);
10224
+
10225
+ const typeOrmModules = new Set<any>();
10226
+ const typeOrm = requireOptionalHostModule('typeorm');
10227
+ if (typeOrm) typeOrmModules.add(typeOrm);
10228
+ for (const loaded of getLoadedModuleExportsByPackage('typeorm')) typeOrmModules.add(loaded);
10229
+ for (const loaded of typeOrmModules) patchTypeOrmFromModuleExports(loaded);
10230
+ }
10231
+
9586
10232
  // ===================================================================
9587
10233
  // Native MongoDB driver integration
9588
10234
  // ===================================================================
@@ -10713,6 +11359,7 @@ export async function initRepro(cfg: ReproInitConfig): Promise<void> {
10713
11359
  patchKafkaJs(integrationCfg);
10714
11360
  patchAmqplib(integrationCfg);
10715
11361
  patchRedis(integrationCfg);
11362
+ patchPostgreSql(integrationCfg);
10716
11363
  patchMongoDb(integrationCfg);
10717
11364
  await installMongoDbImportHook();
10718
11365
  patchSendgridMail(integrationCfg);
@@ -10779,6 +11426,9 @@ export const __reproTestHooks = {
10779
11426
  patchAmqplibConnectionForTest: patchAmqplibConnection,
10780
11427
  patchAmqplibChannelForTest: patchAmqplibChannel,
10781
11428
  wrapAmqplibConsumeHandlerForTest: wrapAmqplibConsumeHandler,
11429
+ patchPgModuleForTest: patchPgFromModuleExports,
11430
+ patchPostgresJsModuleForTest: patchPostgresJsFromModuleExports,
11431
+ patchTypeOrmModuleForTest: patchTypeOrmFromModuleExports,
10782
11432
  patchMongoDbModuleForTest: patchMongoDbFromModuleExports,
10783
11433
  patchMongoCollectionPrototypeForTest: patchMongoCollectionPrototype,
10784
11434
  chunkTraceEventsForTransportForTest: chunkTraceEventsForTransport,