repro-nest 0.0.205 → 0.0.208

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -29,6 +29,11 @@ type TracerApi = {
29
29
  init?: (opts: any) => void;
30
30
  tracer?: {
31
31
  on: (fn: (ev: any) => void) => () => void;
32
+ getCurrentTraceContext?: () => {
33
+ traceId: any;
34
+ spanId: any;
35
+ depth?: number;
36
+ };
32
37
  };
33
38
  getCurrentTraceId?: () => string | null;
34
39
  patchHttp?: () => void;
package/dist/index.js CHANGED
@@ -439,6 +439,13 @@ function initReproTracing(opts) {
439
439
  const initOpts = { ...defaultTracerInitOpts(), ...rest };
440
440
  tracerPkg.init?.(initOpts);
441
441
  tracerPkg.patchHttp?.();
442
+ // Ensure tracer exposes current trace context on the on-event API when available.
443
+ try {
444
+ if (!tracerPkg.tracer?.getCurrentTraceContext && tracerPkg.getCurrentTraceContext) {
445
+ tracerPkg.tracer.getCurrentTraceContext = tracerPkg.getCurrentTraceContext;
446
+ }
447
+ }
448
+ catch { }
442
449
  applyTraceLogPreference(tracerPkg);
443
450
  __TRACER_READY = true;
444
451
  patchAllKnownMongooseInstances();
@@ -800,7 +807,8 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
800
807
  if (Number.isFinite(env) && env >= 0)
801
808
  return env;
802
809
  // Bound wait for draining sessions to avoid lost flushes when a request hangs.
803
- return 10000;
810
+ // Default to 1 minute; caller can override via env.
811
+ return 60000;
804
812
  })();
805
813
  function isThenable(value) {
806
814
  return value != null && typeof value === 'object' && typeof value.then === 'function';
@@ -1327,6 +1335,9 @@ function reproMiddleware(cfg) {
1327
1335
  if (ev.args !== undefined) {
1328
1336
  evt.args = sanitizeTraceArgs(ev.args);
1329
1337
  }
1338
+ if (ev.receiver !== undefined) {
1339
+ evt.receiver = sanitizeTraceValue(ev.receiver);
1340
+ }
1330
1341
  if (ev.returnValue !== undefined) {
1331
1342
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1332
1343
  }
@@ -1888,6 +1899,12 @@ function dehydrateComplexValue(value) {
1888
1899
  function emitDbQuery(cfg, sid, aid, payload) {
1889
1900
  if (!sid)
1890
1901
  return;
1902
+ let traceCtx = null;
1903
+ try {
1904
+ const getCtx = __TRACER__?.tracer?.getCurrentTraceContext;
1905
+ traceCtx = typeof getCtx === 'function' ? getCtx() : null;
1906
+ }
1907
+ catch { }
1891
1908
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
1892
1909
  entries: [{
1893
1910
  actionId: aid ?? null,
@@ -1897,6 +1914,8 @@ function emitDbQuery(cfg, sid, aid, payload) {
1897
1914
  query: payload.query ?? undefined,
1898
1915
  resultMeta: payload.resultMeta ?? undefined,
1899
1916
  durMs: payload.durMs ?? undefined,
1917
+ traceId: traceCtx?.traceId ?? null,
1918
+ spanId: traceCtx?.spanId ?? null,
1900
1919
  pk: null, before: null, after: null,
1901
1920
  error: payload.error ?? undefined,
1902
1921
  }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.205",
3
+ "version": "0.0.208",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -174,7 +174,10 @@ function patchAllKnownMongooseInstances() {
174
174
  // ====== tiny, safe tracer auto-init (no node_modules patches) ======
175
175
  type TracerApi = {
176
176
  init?: (opts: any) => void;
177
- tracer?: { on: (fn: (ev: any) => void) => () => void };
177
+ tracer?: {
178
+ on: (fn: (ev: any) => void) => () => void;
179
+ getCurrentTraceContext?: () => { traceId: any; spanId: any; depth?: number };
180
+ };
178
181
  getCurrentTraceId?: () => string | null;
179
182
  patchHttp?: () => void; // optional in your tracer
180
183
  setFunctionLogsEnabled?: (enabled: boolean) => void;
@@ -210,6 +213,7 @@ type TraceEventRecord = {
210
213
  spanId?: string | number | null;
211
214
  parentSpanId?: string | number | null;
212
215
  args?: any;
216
+ receiver?: any;
213
217
  returnValue?: any;
214
218
  threw?: boolean;
215
219
  error?: any;
@@ -622,6 +626,12 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
622
626
  const initOpts = { ...defaultTracerInitOpts(), ...(rest as TracerInitOpts) };
623
627
  tracerPkg.init?.(initOpts);
624
628
  tracerPkg.patchHttp?.();
629
+ // Ensure tracer exposes current trace context on the on-event API when available.
630
+ try {
631
+ if (!tracerPkg.tracer?.getCurrentTraceContext && (tracerPkg as any).getCurrentTraceContext) {
632
+ (tracerPkg.tracer as any).getCurrentTraceContext = (tracerPkg as any).getCurrentTraceContext;
633
+ }
634
+ } catch {}
625
635
  applyTraceLogPreference(tracerPkg);
626
636
  __TRACER_READY = true;
627
637
  patchAllKnownMongooseInstances();
@@ -1023,7 +1033,8 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
1023
1033
  const env = Number(process.env.SESSION_DRAIN_TIMEOUT_MS);
1024
1034
  if (Number.isFinite(env) && env >= 0) return env;
1025
1035
  // Bound wait for draining sessions to avoid lost flushes when a request hangs.
1026
- return 10000;
1036
+ // Default to 1 minute; caller can override via env.
1037
+ return 60000;
1027
1038
  })();
1028
1039
 
1029
1040
  function isThenable(value: any): value is PromiseLike<any> {
@@ -1532,6 +1543,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1532
1543
  if (ev.args !== undefined) {
1533
1544
  evt.args = sanitizeTraceArgs(ev.args);
1534
1545
  }
1546
+ if (ev.receiver !== undefined) {
1547
+ evt.receiver = sanitizeTraceValue(ev.receiver);
1548
+ }
1535
1549
  if (ev.returnValue !== undefined) {
1536
1550
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1537
1551
  }
@@ -2100,6 +2114,12 @@ function dehydrateComplexValue(value: any) {
2100
2114
 
2101
2115
  function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
2102
2116
  if (!sid) return;
2117
+ let traceCtx: { traceId: any; spanId: any } | null = null;
2118
+ try {
2119
+ const getCtx = __TRACER__?.tracer?.getCurrentTraceContext;
2120
+ traceCtx = typeof getCtx === 'function' ? getCtx() : null;
2121
+ } catch {}
2122
+
2103
2123
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
2104
2124
  entries: [{
2105
2125
  actionId: aid ?? null,
@@ -2109,6 +2129,8 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
2109
2129
  query: payload.query ?? undefined,
2110
2130
  resultMeta: payload.resultMeta ?? undefined,
2111
2131
  durMs: payload.durMs ?? undefined,
2132
+ traceId: traceCtx?.traceId ?? null,
2133
+ spanId: traceCtx?.spanId ?? null,
2112
2134
  pk: null, before: null, after: null,
2113
2135
  error: payload.error ?? undefined,
2114
2136
  }],
package/tracer/runtime.js CHANGED
@@ -100,6 +100,17 @@ function popSpan(ctx) {
100
100
  const trace = {
101
101
  on(fn){ listeners.add(fn); return () => listeners.delete(fn); },
102
102
  withTrace(id, fn, depth = 0){ return als.run({ traceId: id, depth }, fn); },
103
+ getCurrentTraceContext(){
104
+ const store = als.getStore();
105
+ if (!store) return { traceId: null, spanId: null, depth: 0 };
106
+ const spanStack = Array.isArray(store.__repro_span_stack) ? store.__repro_span_stack : [];
107
+ const top = spanStack.length ? spanStack[spanStack.length - 1] : null;
108
+ return {
109
+ traceId: store.traceId || null,
110
+ spanId: top ? top.id ?? null : null,
111
+ depth: typeof store.depth === 'number' ? store.depth : (top?.depth ?? 0),
112
+ };
113
+ },
103
114
  enter(fn, meta, detail){
104
115
  const parentSpanIdOverride = meta && Object.prototype.hasOwnProperty.call(meta, 'parentSpanId')
105
116
  ? meta.parentSpanId
@@ -132,6 +143,7 @@ const trace = {
132
143
  traceId: ctx.traceId,
133
144
  depth: ctx.depth,
134
145
  args: detail?.args,
146
+ receiver: detail?.receiver,
135
147
  spanId: span.id,
136
148
  parentSpanId: span.parentId
137
149
  });
@@ -159,7 +171,8 @@ const trace = {
159
171
  returnValue: detail?.returnValue,
160
172
  error: detail?.error,
161
173
  threw: detail?.threw === true,
162
- unawaited: detail?.unawaited === true || frameUnawaited
174
+ unawaited: detail?.unawaited === true || frameUnawaited,
175
+ receiver: detail?.receiver
163
176
  };
164
177
 
165
178
  const promiseTaggedUnawaited = !!(baseDetail.returnValue && baseDetail.returnValue[SYM_UNAWAITED]);
@@ -179,6 +192,9 @@ const trace = {
179
192
  unawaited: overrides.hasOwnProperty('unawaited')
180
193
  ? overrides.unawaited
181
194
  : forceUnawaited,
195
+ receiver: overrides.hasOwnProperty('receiver')
196
+ ? overrides.receiver
197
+ : baseDetail.receiver,
182
198
  args: overrides.hasOwnProperty('args')
183
199
  ? overrides.args
184
200
  : baseDetail.args
@@ -199,6 +215,7 @@ const trace = {
199
215
  threw: finalDetail.threw === true,
200
216
  error: finalDetail.error,
201
217
  args: finalDetail.args,
218
+ receiver: finalDetail.receiver,
202
219
  unawaited: finalDetail.unawaited === true
203
220
  });
204
221
  };
@@ -475,6 +492,24 @@ function forkAlsStoreForUnawaited(baseStore) {
475
492
  return cloned;
476
493
  }
477
494
 
495
+ // Only capture receiver/collection snapshots for common array iteration helpers to keep noise low.
496
+ const RECEIVER_METHOD_NAMES = new Set([
497
+ // Array iterators
498
+ 'map', 'forEach', 'filter', 'find', 'findIndex', 'findLast', 'findLastIndex',
499
+ 'some', 'every', 'reduce', 'reduceRight', 'flatMap', 'flat',
500
+ // Set / Map mutators
501
+ 'add', 'delete', 'clear', 'set'
502
+ ]);
503
+
504
+ function shouldCaptureReceiver(label, receiver) {
505
+ if (!label || receiver === null || receiver === undefined) return false;
506
+ const method = String(label).split('.').pop() || '';
507
+ if (!RECEIVER_METHOD_NAMES.has(method)) return false;
508
+ if (Array.isArray(receiver)) return true;
509
+ if (typeof receiver === 'object' && typeof receiver[Symbol.iterator] === 'function') return true;
510
+ return false;
511
+ }
512
+
478
513
  // ========= Generic call-site shim (used by Babel transform) =========
479
514
  // Decides whether to emit a top-level event based on callee origin tags.
480
515
  // No hardcoded library names or file paths.
@@ -596,13 +631,15 @@ if (!global.__repro_call) {
596
631
  ? (label && label.length ? label : fn.name)
597
632
  : '(anonymous)';
598
633
  const sourceFile = fn[SYM_SRC_FILE];
634
+ const receiverForTrace = shouldCaptureReceiver(label, thisArg) ? thisArg : undefined;
599
635
  const meta = {
600
- file: sourceFile || callFile || null,
601
- line: sourceFile ? null : (callLine || null),
636
+ // Prefer callsite file/line so traces point to where the function was invoked.
637
+ file: callFile || sourceFile || null,
638
+ line: callLine ?? null,
602
639
  parentSpanId: forcedParentSpanId
603
640
  };
604
641
 
605
- trace.enter(name, meta, { args });
642
+ trace.enter(name, meta, { args, receiver: receiverForTrace });
606
643
  // After entering, snapshot a clean store that captures the parent + this call’s span,
607
644
  // so callbacks invoked during the callee run are isolated per invocation.
608
645
  const snapshotForArgs = cloneStore(als.getStore());
@@ -617,7 +654,8 @@ if (!global.__repro_call) {
617
654
  const exitDetailBase = {
618
655
  returnValue: out,
619
656
  args,
620
- unawaited: shouldForceExit
657
+ unawaited: shouldForceExit,
658
+ receiver: receiverForTrace
621
659
  };
622
660
 
623
661
  if (shouldForceExit) markPromiseUnawaited(out);
@@ -659,7 +697,8 @@ if (!global.__repro_call) {
659
697
  args,
660
698
  threw,
661
699
  error,
662
- unawaited: shouldForceExit
700
+ unawaited: shouldForceExit,
701
+ receiver: receiverForTrace
663
702
  };
664
703
  runExit(detail);
665
704
  return value;
@@ -679,7 +718,7 @@ if (!global.__repro_call) {
679
718
  runExit(exitDetailBase);
680
719
  return out;
681
720
  } catch (e) {
682
- trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args });
721
+ trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args, receiver: receiverForTrace });
683
722
  throw e;
684
723
  } finally {
685
724
  if (pendingMarker) {