repro-nest 0.0.208 → 0.0.210

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
@@ -25,17 +25,19 @@
25
25
  /// <reference types="mongoose/types/inferrawdoctype" />
26
26
  import type { Request, Response, NextFunction } from 'express';
27
27
  import type { Schema } from 'mongoose';
28
+ type SpanContext = {
29
+ traceId: string | null;
30
+ spanId: string | number | null;
31
+ parentSpanId: string | number | null;
32
+ depth: number | null;
33
+ };
28
34
  type TracerApi = {
29
35
  init?: (opts: any) => void;
30
36
  tracer?: {
31
37
  on: (fn: (ev: any) => void) => () => void;
32
- getCurrentTraceContext?: () => {
33
- traceId: any;
34
- spanId: any;
35
- depth?: number;
36
- };
37
38
  };
38
39
  getCurrentTraceId?: () => string | null;
40
+ getCurrentSpanContext?: () => SpanContext | null;
39
41
  patchHttp?: () => void;
40
42
  setFunctionLogsEnabled?: (enabled: boolean) => void;
41
43
  };
package/dist/index.js CHANGED
@@ -439,13 +439,6 @@ 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 { }
449
442
  applyTraceLogPreference(tracerPkg);
450
443
  __TRACER_READY = true;
451
444
  patchAllKnownMongooseInstances();
@@ -466,6 +459,52 @@ exports.initReproTracing = initReproTracing;
466
459
  /** Optional helper if users want to check it. */
467
460
  function isReproTracingEnabled() { return __TRACER_READY; }
468
461
  exports.isReproTracingEnabled = isReproTracingEnabled;
462
+ function captureSpanContext(source) {
463
+ try {
464
+ const fromSource = source && source.__repro_span_context;
465
+ if (fromSource) {
466
+ const span = {
467
+ traceId: fromSource.traceId ?? null,
468
+ spanId: fromSource.spanId ?? null,
469
+ parentSpanId: fromSource.parentSpanId ?? null,
470
+ depth: fromSource.depth ?? null,
471
+ };
472
+ return span;
473
+ }
474
+ const ctx = __TRACER__?.getCurrentSpanContext?.();
475
+ if (ctx) {
476
+ const span = {
477
+ traceId: ctx.traceId ?? __TRACER__?.getCurrentTraceId?.() ?? null,
478
+ spanId: ctx.spanId ?? null,
479
+ parentSpanId: ctx.parentSpanId ?? null,
480
+ depth: ctx.depth ?? null,
481
+ };
482
+ if (span.traceId || span.spanId !== null || span.parentSpanId !== null) {
483
+ return span;
484
+ }
485
+ }
486
+ else if (__TRACER__?.getCurrentTraceId) {
487
+ const traceId = __TRACER__?.getCurrentTraceId?.() ?? null;
488
+ if (traceId) {
489
+ return { traceId, spanId: null, parentSpanId: null, depth: null };
490
+ }
491
+ }
492
+ }
493
+ catch { }
494
+ return null;
495
+ }
496
+ function attachSpanContext(target, spanSource, fallbackSource) {
497
+ if (!target)
498
+ return target;
499
+ const ctx = spanSource ?? captureSpanContext(fallbackSource);
500
+ if (ctx) {
501
+ try {
502
+ target.spanContext = ctx;
503
+ }
504
+ catch { }
505
+ }
506
+ return target;
507
+ }
469
508
  const als = new async_hooks_1.AsyncLocalStorage();
470
509
  const getCtx = () => als.getStore() || {};
471
510
  function currentClockSkewMs() {
@@ -807,8 +846,7 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
807
846
  if (Number.isFinite(env) && env >= 0)
808
847
  return env;
809
848
  // Bound wait for draining sessions to avoid lost flushes when a request hangs.
810
- // Default to 1 minute; caller can override via env.
811
- return 60000;
849
+ return 10000;
812
850
  })();
813
851
  function isThenable(value) {
814
852
  return value != null && typeof value === 'object' && typeof value.then === 'function';
@@ -1335,9 +1373,6 @@ function reproMiddleware(cfg) {
1335
1373
  if (ev.args !== undefined) {
1336
1374
  evt.args = sanitizeTraceArgs(ev.args);
1337
1375
  }
1338
- if (ev.receiver !== undefined) {
1339
- evt.receiver = sanitizeTraceValue(ev.receiver);
1340
- }
1341
1376
  if (ev.returnValue !== undefined) {
1342
1377
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1343
1378
  }
@@ -1547,6 +1582,7 @@ function reproMongoosePlugin(cfg) {
1547
1582
  wasNew: this.isNew,
1548
1583
  before,
1549
1584
  collection: resolveCollectionOrWarn(this, 'doc'),
1585
+ spanContext: captureSpanContext(this),
1550
1586
  };
1551
1587
  next();
1552
1588
  });
@@ -1560,13 +1596,21 @@ function reproMongoosePlugin(cfg) {
1560
1596
  const before = meta.before ?? null;
1561
1597
  const after = this.toObject({ depopulate: true });
1562
1598
  const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
1599
+ const spanContext = meta.spanContext || captureSpanContext(this);
1563
1600
  const query = meta.wasNew
1564
1601
  ? { op: 'insertOne', doc: after }
1565
1602
  : { filter: { _id: this._id }, update: buildMinimalUpdate(before, after), options: { upsert: false } };
1566
1603
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1567
1604
  entries: [{
1568
1605
  actionId: getCtx().aid,
1569
- db: [{ collection, pk: { _id: this._id }, before, after, op: meta.wasNew ? 'insert' : 'update', query }],
1606
+ db: [attachSpanContext({
1607
+ collection,
1608
+ pk: { _id: this._id },
1609
+ before,
1610
+ after,
1611
+ op: meta.wasNew ? 'insert' : 'update',
1612
+ query,
1613
+ }, spanContext)],
1570
1614
  t: alignedNow(),
1571
1615
  }]
1572
1616
  });
@@ -1582,6 +1626,7 @@ function reproMongoosePlugin(cfg) {
1582
1626
  this.__repro_before = await model.findOne(filter).lean().exec();
1583
1627
  this.setOptions({ new: true });
1584
1628
  this.__repro_collection = resolveCollectionOrWarn(this, 'query');
1629
+ this.__repro_spanContext = captureSpanContext(this);
1585
1630
  }
1586
1631
  catch { }
1587
1632
  next();
@@ -1593,11 +1638,18 @@ function reproMongoosePlugin(cfg) {
1593
1638
  const before = this.__repro_before ?? null;
1594
1639
  const after = res ?? null;
1595
1640
  const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
1641
+ const spanContext = this.__repro_spanContext || captureSpanContext(this);
1596
1642
  const pk = after?._id ?? before?._id;
1597
1643
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1598
1644
  entries: [{
1599
1645
  actionId: getCtx().aid,
1600
- db: [{ collection, pk: { _id: pk }, before, after, op: after && before ? 'update' : after ? 'insert' : 'update' }],
1646
+ db: [attachSpanContext({
1647
+ collection,
1648
+ pk: { _id: pk },
1649
+ before,
1650
+ after,
1651
+ op: after && before ? 'update' : after ? 'insert' : 'update',
1652
+ }, spanContext)],
1601
1653
  t: alignedNow()
1602
1654
  }]
1603
1655
  });
@@ -1612,6 +1664,7 @@ function reproMongoosePlugin(cfg) {
1612
1664
  this.__repro_before = await this.model.findOne(filter).lean().exec();
1613
1665
  this.__repro_collection = resolveCollectionOrWarn(this, 'query');
1614
1666
  this.__repro_filter = filter;
1667
+ this.__repro_spanContext = captureSpanContext(this);
1615
1668
  }
1616
1669
  catch { }
1617
1670
  next();
@@ -1625,10 +1678,18 @@ function reproMongoosePlugin(cfg) {
1625
1678
  return;
1626
1679
  const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
1627
1680
  const filter = this.__repro_filter ?? { _id: before._id };
1681
+ const spanContext = this.__repro_spanContext || captureSpanContext(this);
1628
1682
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1629
1683
  entries: [{
1630
1684
  actionId: getCtx().aid,
1631
- db: [{ collection, pk: { _id: before._id }, before, after: null, op: 'delete', query: { filter } }],
1685
+ db: [attachSpanContext({
1686
+ collection,
1687
+ pk: { _id: before._id },
1688
+ before,
1689
+ after: null,
1690
+ op: 'delete',
1691
+ query: { filter },
1692
+ }, spanContext)],
1632
1693
  t: alignedNow()
1633
1694
  }]
1634
1695
  });
@@ -1666,6 +1727,7 @@ function reproMongoosePlugin(cfg) {
1666
1727
  t0: Date.now(),
1667
1728
  collection: this?.model?.collection?.name || 'unknown',
1668
1729
  op,
1730
+ spanContext: captureSpanContext(this),
1669
1731
  filter: sanitizeDbValue(this.getFilter?.() ?? this._conditions ?? undefined),
1670
1732
  update: sanitizeDbValue(this.getUpdate?.() ?? this._update ?? undefined),
1671
1733
  projection: sanitizeDbValue(this.projection?.() ?? this._fields ?? undefined),
@@ -1673,7 +1735,12 @@ function reproMongoosePlugin(cfg) {
1673
1735
  };
1674
1736
  }
1675
1737
  catch {
1676
- this.__repro_qmeta = { t0: Date.now(), collection: 'unknown', op };
1738
+ this.__repro_qmeta = {
1739
+ t0: Date.now(),
1740
+ collection: 'unknown',
1741
+ op,
1742
+ spanContext: captureSpanContext(this),
1743
+ };
1677
1744
  }
1678
1745
  next();
1679
1746
  });
@@ -1683,6 +1750,7 @@ function reproMongoosePlugin(cfg) {
1683
1750
  return;
1684
1751
  const meta = this.__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
1685
1752
  const resultMeta = summarizeQueryResult(op, res);
1753
+ const spanContext = meta.spanContext || captureSpanContext(this);
1686
1754
  emitDbQuery(cfg, sid, aid, {
1687
1755
  collection: meta.collection,
1688
1756
  op,
@@ -1690,6 +1758,7 @@ function reproMongoosePlugin(cfg) {
1690
1758
  resultMeta,
1691
1759
  durMs: Date.now() - meta.t0,
1692
1760
  t: alignedNow(),
1761
+ spanContext,
1693
1762
  });
1694
1763
  });
1695
1764
  }
@@ -1702,10 +1771,15 @@ function reproMongoosePlugin(cfg) {
1702
1771
  t0: Date.now(),
1703
1772
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1704
1773
  docs: sanitizeDbValue(docs),
1774
+ spanContext: captureSpanContext(this),
1705
1775
  };
1706
1776
  }
1707
1777
  catch {
1708
- this.__repro_insert_meta = { t0: Date.now(), collection: 'unknown' };
1778
+ this.__repro_insert_meta = {
1779
+ t0: Date.now(),
1780
+ collection: 'unknown',
1781
+ spanContext: captureSpanContext(this),
1782
+ };
1709
1783
  }
1710
1784
  next();
1711
1785
  });
@@ -1715,6 +1789,7 @@ function reproMongoosePlugin(cfg) {
1715
1789
  return;
1716
1790
  const meta = this.__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
1717
1791
  const resultMeta = Array.isArray(docs) ? { inserted: docs.length } : summarizeQueryResult('insertMany', docs);
1792
+ const spanContext = meta.spanContext || captureSpanContext(this);
1718
1793
  emitDbQuery(cfg, sid, aid, {
1719
1794
  collection: meta.collection,
1720
1795
  op: 'insertMany',
@@ -1722,6 +1797,7 @@ function reproMongoosePlugin(cfg) {
1722
1797
  resultMeta,
1723
1798
  durMs: Date.now() - meta.t0,
1724
1799
  t: alignedNow(),
1800
+ spanContext,
1725
1801
  });
1726
1802
  });
1727
1803
  schema.pre('bulkWrite', { document: false, query: false }, function (next, ops) {
@@ -1730,10 +1806,15 @@ function reproMongoosePlugin(cfg) {
1730
1806
  t0: Date.now(),
1731
1807
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1732
1808
  ops: sanitizeDbValue(ops),
1809
+ spanContext: captureSpanContext(this),
1733
1810
  };
1734
1811
  }
1735
1812
  catch {
1736
- this.__repro_bulk_meta = { t0: Date.now(), collection: 'unknown' };
1813
+ this.__repro_bulk_meta = {
1814
+ t0: Date.now(),
1815
+ collection: 'unknown',
1816
+ spanContext: captureSpanContext(this),
1817
+ };
1737
1818
  }
1738
1819
  next();
1739
1820
  });
@@ -1744,6 +1825,7 @@ function reproMongoosePlugin(cfg) {
1744
1825
  const meta = this.__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
1745
1826
  const bulkResult = summarizeBulkResult(res);
1746
1827
  const resultMeta = { ...bulkResult, result: sanitizeResultForMeta(res?.result ?? res) };
1828
+ const spanContext = meta.spanContext || captureSpanContext(this);
1747
1829
  emitDbQuery(cfg, sid, aid, {
1748
1830
  collection: meta.collection,
1749
1831
  op: 'bulkWrite',
@@ -1751,6 +1833,7 @@ function reproMongoosePlugin(cfg) {
1751
1833
  resultMeta,
1752
1834
  durMs: Date.now() - meta.t0,
1753
1835
  t: alignedNow(),
1836
+ spanContext,
1754
1837
  });
1755
1838
  });
1756
1839
  // Aggregate middleware (non-intrusive)
@@ -1762,11 +1845,17 @@ function reproMongoosePlugin(cfg) {
1762
1845
  this?._model?.collection?.name ||
1763
1846
  (this?.model && this.model.collection?.name) ||
1764
1847
  'unknown',
1848
+ spanContext: captureSpanContext(this),
1765
1849
  pipeline: sanitizeDbValue(this.pipeline?.() ?? this._pipeline ?? undefined),
1766
1850
  };
1767
1851
  }
1768
1852
  catch {
1769
- this.__repro_aggmeta = { t0: Date.now(), collection: 'unknown', pipeline: undefined };
1853
+ this.__repro_aggmeta = {
1854
+ t0: Date.now(),
1855
+ collection: 'unknown',
1856
+ pipeline: undefined,
1857
+ spanContext: captureSpanContext(this),
1858
+ };
1770
1859
  }
1771
1860
  next();
1772
1861
  });
@@ -1776,6 +1865,7 @@ function reproMongoosePlugin(cfg) {
1776
1865
  return;
1777
1866
  const meta = this.__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
1778
1867
  const resultMeta = summarizeQueryResult('aggregate', res);
1868
+ const spanContext = meta.spanContext || captureSpanContext(this);
1779
1869
  emitDbQuery(cfg, sid, aid, {
1780
1870
  collection: meta.collection,
1781
1871
  op: 'aggregate',
@@ -1783,6 +1873,7 @@ function reproMongoosePlugin(cfg) {
1783
1873
  resultMeta,
1784
1874
  durMs: Date.now() - meta.t0,
1785
1875
  t: alignedNow(),
1876
+ spanContext,
1786
1877
  });
1787
1878
  });
1788
1879
  };
@@ -1899,26 +1990,19 @@ function dehydrateComplexValue(value) {
1899
1990
  function emitDbQuery(cfg, sid, aid, payload) {
1900
1991
  if (!sid)
1901
1992
  return;
1902
- let traceCtx = null;
1903
- try {
1904
- const getCtx = __TRACER__?.tracer?.getCurrentTraceContext;
1905
- traceCtx = typeof getCtx === 'function' ? getCtx() : null;
1906
- }
1907
- catch { }
1993
+ const dbEntry = attachSpanContext({
1994
+ collection: payload.collection,
1995
+ op: payload.op,
1996
+ query: payload.query ?? undefined,
1997
+ resultMeta: payload.resultMeta ?? undefined,
1998
+ durMs: payload.durMs ?? undefined,
1999
+ pk: null, before: null, after: null,
2000
+ error: payload.error ?? undefined,
2001
+ }, payload?.spanContext);
1908
2002
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
1909
2003
  entries: [{
1910
2004
  actionId: aid ?? null,
1911
- db: [{
1912
- collection: payload.collection,
1913
- op: payload.op,
1914
- query: payload.query ?? undefined,
1915
- resultMeta: payload.resultMeta ?? undefined,
1916
- durMs: payload.durMs ?? undefined,
1917
- traceId: traceCtx?.traceId ?? null,
1918
- spanId: traceCtx?.spanId ?? null,
1919
- pk: null, before: null, after: null,
1920
- error: payload.error ?? undefined,
1921
- }],
2005
+ db: [dbEntry],
1922
2006
  t: payload.t,
1923
2007
  }]
1924
2008
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.208",
3
+ "version": "0.0.210",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -172,13 +172,18 @@ function patchAllKnownMongooseInstances() {
172
172
  }
173
173
 
174
174
  // ====== tiny, safe tracer auto-init (no node_modules patches) ======
175
+ type SpanContext = {
176
+ traceId: string | null;
177
+ spanId: string | number | null;
178
+ parentSpanId: string | number | null;
179
+ depth: number | null;
180
+ };
181
+
175
182
  type TracerApi = {
176
183
  init?: (opts: any) => void;
177
- tracer?: {
178
- on: (fn: (ev: any) => void) => () => void;
179
- getCurrentTraceContext?: () => { traceId: any; spanId: any; depth?: number };
180
- };
184
+ tracer?: { on: (fn: (ev: any) => void) => () => void };
181
185
  getCurrentTraceId?: () => string | null;
186
+ getCurrentSpanContext?: () => SpanContext | null;
182
187
  patchHttp?: () => void; // optional in your tracer
183
188
  setFunctionLogsEnabled?: (enabled: boolean) => void;
184
189
  };
@@ -213,7 +218,6 @@ type TraceEventRecord = {
213
218
  spanId?: string | number | null;
214
219
  parentSpanId?: string | number | null;
215
220
  args?: any;
216
- receiver?: any;
217
221
  returnValue?: any;
218
222
  threw?: boolean;
219
223
  error?: any;
@@ -626,12 +630,6 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
626
630
  const initOpts = { ...defaultTracerInitOpts(), ...(rest as TracerInitOpts) };
627
631
  tracerPkg.init?.(initOpts);
628
632
  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 {}
635
633
  applyTraceLogPreference(tracerPkg);
636
634
  __TRACER_READY = true;
637
635
  patchAllKnownMongooseInstances();
@@ -650,6 +648,49 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
650
648
  /** Optional helper if users want to check it. */
651
649
  export function isReproTracingEnabled() { return __TRACER_READY; }
652
650
 
651
+ function captureSpanContext(source?: any): SpanContext | null {
652
+ try {
653
+ const fromSource = source && source.__repro_span_context;
654
+ if (fromSource) {
655
+ const span: SpanContext = {
656
+ traceId: fromSource.traceId ?? null,
657
+ spanId: fromSource.spanId ?? null,
658
+ parentSpanId: fromSource.parentSpanId ?? null,
659
+ depth: fromSource.depth ?? null,
660
+ };
661
+ return span;
662
+ }
663
+
664
+ const ctx = __TRACER__?.getCurrentSpanContext?.();
665
+ if (ctx) {
666
+ const span: SpanContext = {
667
+ traceId: ctx.traceId ?? __TRACER__?.getCurrentTraceId?.() ?? null,
668
+ spanId: ctx.spanId ?? null,
669
+ parentSpanId: ctx.parentSpanId ?? null,
670
+ depth: ctx.depth ?? null,
671
+ };
672
+ if (span.traceId || span.spanId !== null || span.parentSpanId !== null) {
673
+ return span;
674
+ }
675
+ } else if (__TRACER__?.getCurrentTraceId) {
676
+ const traceId = __TRACER__?.getCurrentTraceId?.() ?? null;
677
+ if (traceId) {
678
+ return { traceId, spanId: null, parentSpanId: null, depth: null };
679
+ }
680
+ }
681
+ } catch {}
682
+ return null;
683
+ }
684
+
685
+ function attachSpanContext<T extends Record<string, any>>(target: T, spanSource?: SpanContext | null, fallbackSource?: any): T {
686
+ if (!target) return target;
687
+ const ctx = spanSource ?? captureSpanContext(fallbackSource);
688
+ if (ctx) {
689
+ try { (target as any).spanContext = ctx; } catch {}
690
+ }
691
+ return target;
692
+ }
693
+
653
694
  type Ctx = { sid?: string; aid?: string; clockSkewMs?: number };
654
695
  const als = new AsyncLocalStorage<Ctx>();
655
696
  const getCtx = () => als.getStore() || {};
@@ -1033,8 +1074,7 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
1033
1074
  const env = Number(process.env.SESSION_DRAIN_TIMEOUT_MS);
1034
1075
  if (Number.isFinite(env) && env >= 0) return env;
1035
1076
  // Bound wait for draining sessions to avoid lost flushes when a request hangs.
1036
- // Default to 1 minute; caller can override via env.
1037
- return 60000;
1077
+ return 10000;
1038
1078
  })();
1039
1079
 
1040
1080
  function isThenable(value: any): value is PromiseLike<any> {
@@ -1543,9 +1583,6 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1543
1583
  if (ev.args !== undefined) {
1544
1584
  evt.args = sanitizeTraceArgs(ev.args);
1545
1585
  }
1546
- if (ev.receiver !== undefined) {
1547
- evt.receiver = sanitizeTraceValue(ev.receiver);
1548
- }
1549
1586
  if (ev.returnValue !== undefined) {
1550
1587
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1551
1588
  }
@@ -1759,6 +1796,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1759
1796
  wasNew: this.isNew,
1760
1797
  before,
1761
1798
  collection: resolveCollectionOrWarn(this, 'doc'),
1799
+ spanContext: captureSpanContext(this),
1762
1800
  };
1763
1801
  next();
1764
1802
  });
@@ -1772,6 +1810,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1772
1810
  const before = meta.before ?? null;
1773
1811
  const after = this.toObject({ depopulate: true });
1774
1812
  const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
1813
+ const spanContext = meta.spanContext || captureSpanContext(this);
1775
1814
 
1776
1815
  const query = meta.wasNew
1777
1816
  ? { op: 'insertOne', doc: after }
@@ -1780,7 +1819,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1780
1819
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1781
1820
  entries: [{
1782
1821
  actionId: (getCtx() as Ctx).aid!,
1783
- db: [{ collection, pk: { _id: (this as any)._id }, before, after, op: meta.wasNew ? 'insert' : 'update', query }],
1822
+ db: [attachSpanContext({
1823
+ collection,
1824
+ pk: { _id: (this as any)._id },
1825
+ before,
1826
+ after,
1827
+ op: meta.wasNew ? 'insert' : 'update',
1828
+ query,
1829
+ }, spanContext)],
1784
1830
  t: alignedNow(),
1785
1831
  }]
1786
1832
  });
@@ -1796,6 +1842,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1796
1842
  (this as any).__repro_before = await model.findOne(filter).lean().exec();
1797
1843
  this.setOptions({ new: true });
1798
1844
  (this as any).__repro_collection = resolveCollectionOrWarn(this, 'query');
1845
+ (this as any).__repro_spanContext = captureSpanContext(this);
1799
1846
  } catch {}
1800
1847
  next();
1801
1848
  });
@@ -1807,12 +1854,19 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1807
1854
  const before = (this as any).__repro_before ?? null;
1808
1855
  const after = res ?? null;
1809
1856
  const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
1857
+ const spanContext = (this as any).__repro_spanContext || captureSpanContext(this);
1810
1858
  const pk = after?._id ?? before?._id;
1811
1859
 
1812
1860
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1813
1861
  entries: [{
1814
1862
  actionId: (getCtx() as Ctx).aid!,
1815
- db: [{ collection, pk: { _id: pk }, before, after, op: after && before ? 'update' : after ? 'insert' : 'update' }],
1863
+ db: [attachSpanContext({
1864
+ collection,
1865
+ pk: { _id: pk },
1866
+ before,
1867
+ after,
1868
+ op: after && before ? 'update' : after ? 'insert' : 'update',
1869
+ }, spanContext)],
1816
1870
  t: alignedNow()
1817
1871
  }]
1818
1872
  });
@@ -1826,6 +1880,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1826
1880
  (this as any).__repro_before = await (this.model as Model<any>).findOne(filter).lean().exec();
1827
1881
  (this as any).__repro_collection = resolveCollectionOrWarn(this, 'query');
1828
1882
  (this as any).__repro_filter = filter;
1883
+ (this as any).__repro_spanContext = captureSpanContext(this);
1829
1884
  } catch {}
1830
1885
  next();
1831
1886
  });
@@ -1836,10 +1891,18 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1836
1891
  if (!before) return;
1837
1892
  const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
1838
1893
  const filter = (this as any).__repro_filter ?? { _id: before._id };
1894
+ const spanContext = (this as any).__repro_spanContext || captureSpanContext(this);
1839
1895
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1840
1896
  entries: [{
1841
1897
  actionId: (getCtx() as Ctx).aid!,
1842
- db: [{ collection, pk: { _id: before._id }, before, after: null, op: 'delete', query: { filter } }],
1898
+ db: [attachSpanContext({
1899
+ collection,
1900
+ pk: { _id: before._id },
1901
+ before,
1902
+ after: null,
1903
+ op: 'delete',
1904
+ query: { filter },
1905
+ }, spanContext)],
1843
1906
  t: alignedNow()
1844
1907
  }]
1845
1908
  });
@@ -1881,13 +1944,19 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1881
1944
  t0: Date.now(),
1882
1945
  collection: this?.model?.collection?.name || 'unknown',
1883
1946
  op,
1947
+ spanContext: captureSpanContext(this),
1884
1948
  filter: sanitizeDbValue(this.getFilter?.() ?? this._conditions ?? undefined),
1885
1949
  update: sanitizeDbValue(this.getUpdate?.() ?? this._update ?? undefined),
1886
1950
  projection: sanitizeDbValue(this.projection?.() ?? this._fields ?? undefined),
1887
1951
  options: sanitizeDbValue(this.getOptions?.() ?? this.options ?? undefined),
1888
1952
  };
1889
1953
  } catch {
1890
- (this as any).__repro_qmeta = { t0: Date.now(), collection: 'unknown', op };
1954
+ (this as any).__repro_qmeta = {
1955
+ t0: Date.now(),
1956
+ collection: 'unknown',
1957
+ op,
1958
+ spanContext: captureSpanContext(this),
1959
+ };
1891
1960
  }
1892
1961
  next();
1893
1962
  });
@@ -1898,6 +1967,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1898
1967
 
1899
1968
  const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
1900
1969
  const resultMeta = summarizeQueryResult(op, res);
1970
+ const spanContext = meta.spanContext || captureSpanContext(this);
1901
1971
 
1902
1972
  emitDbQuery(cfg, sid, aid, {
1903
1973
  collection: meta.collection,
@@ -1906,6 +1976,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1906
1976
  resultMeta,
1907
1977
  durMs: Date.now() - meta.t0,
1908
1978
  t: alignedNow(),
1979
+ spanContext,
1909
1980
  });
1910
1981
  });
1911
1982
  }
@@ -1920,9 +1991,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1920
1991
  t0: Date.now(),
1921
1992
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1922
1993
  docs: sanitizeDbValue(docs),
1994
+ spanContext: captureSpanContext(this),
1923
1995
  };
1924
1996
  } catch {
1925
- (this as any).__repro_insert_meta = { t0: Date.now(), collection: 'unknown' };
1997
+ (this as any).__repro_insert_meta = {
1998
+ t0: Date.now(),
1999
+ collection: 'unknown',
2000
+ spanContext: captureSpanContext(this),
2001
+ };
1926
2002
  }
1927
2003
  next();
1928
2004
  } as any);
@@ -1932,6 +2008,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1932
2008
  if (!sid) return;
1933
2009
  const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
1934
2010
  const resultMeta = Array.isArray(docs) ? { inserted: docs.length } : summarizeQueryResult('insertMany', docs);
2011
+ const spanContext = meta.spanContext || captureSpanContext(this);
1935
2012
 
1936
2013
  emitDbQuery(cfg, sid, aid, {
1937
2014
  collection: meta.collection,
@@ -1940,6 +2017,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1940
2017
  resultMeta,
1941
2018
  durMs: Date.now() - meta.t0,
1942
2019
  t: alignedNow(),
2020
+ spanContext,
1943
2021
  });
1944
2022
  } as any);
1945
2023
 
@@ -1949,9 +2027,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1949
2027
  t0: Date.now(),
1950
2028
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1951
2029
  ops: sanitizeDbValue(ops),
2030
+ spanContext: captureSpanContext(this),
1952
2031
  };
1953
2032
  } catch {
1954
- (this as any).__repro_bulk_meta = { t0: Date.now(), collection: 'unknown' };
2033
+ (this as any).__repro_bulk_meta = {
2034
+ t0: Date.now(),
2035
+ collection: 'unknown',
2036
+ spanContext: captureSpanContext(this),
2037
+ };
1955
2038
  }
1956
2039
  next();
1957
2040
  } as any);
@@ -1962,6 +2045,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1962
2045
  const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
1963
2046
  const bulkResult = summarizeBulkResult(res);
1964
2047
  const resultMeta = { ...bulkResult, result: sanitizeResultForMeta(res?.result ?? res) };
2048
+ const spanContext = meta.spanContext || captureSpanContext(this);
1965
2049
 
1966
2050
  emitDbQuery(cfg, sid, aid, {
1967
2051
  collection: meta.collection,
@@ -1970,6 +2054,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1970
2054
  resultMeta,
1971
2055
  durMs: Date.now() - meta.t0,
1972
2056
  t: alignedNow(),
2057
+ spanContext,
1973
2058
  });
1974
2059
  } as any);
1975
2060
 
@@ -1983,10 +2068,16 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1983
2068
  this?._model?.collection?.name ||
1984
2069
  (this?.model && this.model.collection?.name) ||
1985
2070
  'unknown',
2071
+ spanContext: captureSpanContext(this),
1986
2072
  pipeline: sanitizeDbValue(this.pipeline?.() ?? this._pipeline ?? undefined),
1987
2073
  };
1988
2074
  } catch {
1989
- (this as any).__repro_aggmeta = { t0: Date.now(), collection: 'unknown', pipeline: undefined };
2075
+ (this as any).__repro_aggmeta = {
2076
+ t0: Date.now(),
2077
+ collection: 'unknown',
2078
+ pipeline: undefined,
2079
+ spanContext: captureSpanContext(this),
2080
+ };
1990
2081
  }
1991
2082
  next();
1992
2083
  });
@@ -1997,6 +2088,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1997
2088
 
1998
2089
  const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
1999
2090
  const resultMeta = summarizeQueryResult('aggregate', res);
2091
+ const spanContext = meta.spanContext || captureSpanContext(this);
2000
2092
 
2001
2093
  emitDbQuery(cfg, sid, aid, {
2002
2094
  collection: meta.collection,
@@ -2005,6 +2097,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
2005
2097
  resultMeta,
2006
2098
  durMs: Date.now() - meta.t0,
2007
2099
  t: alignedNow(),
2100
+ spanContext,
2008
2101
  });
2009
2102
  });
2010
2103
  };
@@ -2114,26 +2207,19 @@ function dehydrateComplexValue(value: any) {
2114
2207
 
2115
2208
  function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
2116
2209
  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
-
2210
+ const dbEntry = attachSpanContext({
2211
+ collection: payload.collection,
2212
+ op: payload.op,
2213
+ query: payload.query ?? undefined,
2214
+ resultMeta: payload.resultMeta ?? undefined,
2215
+ durMs: payload.durMs ?? undefined,
2216
+ pk: null, before: null, after: null,
2217
+ error: payload.error ?? undefined,
2218
+ }, payload?.spanContext);
2123
2219
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
2124
2220
  entries: [{
2125
2221
  actionId: aid ?? null,
2126
- db: [{
2127
- collection: payload.collection,
2128
- op: payload.op,
2129
- query: payload.query ?? undefined,
2130
- resultMeta: payload.resultMeta ?? undefined,
2131
- durMs: payload.durMs ?? undefined,
2132
- traceId: traceCtx?.traceId ?? null,
2133
- spanId: traceCtx?.spanId ?? null,
2134
- pk: null, before: null, after: null,
2135
- error: payload.error ?? undefined,
2136
- }],
2222
+ db: [dbEntry],
2137
2223
  t: payload.t,
2138
2224
  }]
2139
2225
  });
package/tracer/index.js CHANGED
@@ -9,6 +9,7 @@ const {
9
9
  printV8,
10
10
  patchConsole,
11
11
  getCurrentTraceId,
12
+ getCurrentSpanContext,
12
13
  setFunctionLogsEnabled
13
14
  } = require('./runtime');
14
15
  const { installCJS } = require('./cjs-hook');
@@ -60,6 +61,7 @@ function api(){
60
61
  tracer: trace,
61
62
  withTrace: trace.withTrace,
62
63
  getCurrentTraceId,
64
+ getCurrentSpanContext,
63
65
  setFunctionLogsEnabled,
64
66
  };
65
67
  }
package/tracer/runtime.js CHANGED
@@ -100,17 +100,6 @@ 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
- },
114
103
  enter(fn, meta, detail){
115
104
  const parentSpanIdOverride = meta && Object.prototype.hasOwnProperty.call(meta, 'parentSpanId')
116
105
  ? meta.parentSpanId
@@ -143,7 +132,6 @@ const trace = {
143
132
  traceId: ctx.traceId,
144
133
  depth: ctx.depth,
145
134
  args: detail?.args,
146
- receiver: detail?.receiver,
147
135
  spanId: span.id,
148
136
  parentSpanId: span.parentId
149
137
  });
@@ -171,8 +159,7 @@ const trace = {
171
159
  returnValue: detail?.returnValue,
172
160
  error: detail?.error,
173
161
  threw: detail?.threw === true,
174
- unawaited: detail?.unawaited === true || frameUnawaited,
175
- receiver: detail?.receiver
162
+ unawaited: detail?.unawaited === true || frameUnawaited
176
163
  };
177
164
 
178
165
  const promiseTaggedUnawaited = !!(baseDetail.returnValue && baseDetail.returnValue[SYM_UNAWAITED]);
@@ -192,9 +179,6 @@ const trace = {
192
179
  unawaited: overrides.hasOwnProperty('unawaited')
193
180
  ? overrides.unawaited
194
181
  : forceUnawaited,
195
- receiver: overrides.hasOwnProperty('receiver')
196
- ? overrides.receiver
197
- : baseDetail.receiver,
198
182
  args: overrides.hasOwnProperty('args')
199
183
  ? overrides.args
200
184
  : baseDetail.args
@@ -215,7 +199,6 @@ const trace = {
215
199
  threw: finalDetail.threw === true,
216
200
  error: finalDetail.error,
217
201
  args: finalDetail.args,
218
- receiver: finalDetail.receiver,
219
202
  unawaited: finalDetail.unawaited === true
220
203
  });
221
204
  };
@@ -279,6 +262,12 @@ const trace = {
279
262
  return;
280
263
  }
281
264
  if (queueQueryFinalizer(rv, finalize)) return;
265
+ setQuerySpanContext(rv, {
266
+ traceId: traceIdAtExit,
267
+ spanId: spanForExit.id,
268
+ parentSpanId: spanForExit.parentId,
269
+ depth: spanForExit.depth ?? depthAtExit
270
+ });
282
271
  emitNow({ unawaited: forceUnawaited, returnValue: rv }, spanForExit, spanStackForExit);
283
272
  return;
284
273
  }
@@ -289,6 +278,12 @@ const trace = {
289
278
  }
290
279
 
291
280
  if (isQuery) {
281
+ setQuerySpanContext(rv, {
282
+ traceId: traceIdAtExit,
283
+ spanId: spanInfoPeek.id,
284
+ parentSpanId: spanInfoPeek.parentId,
285
+ depth: spanInfoPeek.depth ?? depthAtExit
286
+ });
292
287
  emitNow({ unawaited: forceUnawaited });
293
288
  return;
294
289
  }
@@ -492,24 +487,6 @@ function forkAlsStoreForUnawaited(baseStore) {
492
487
  return cloned;
493
488
  }
494
489
 
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
-
513
490
  // ========= Generic call-site shim (used by Babel transform) =========
514
491
  // Decides whether to emit a top-level event based on callee origin tags.
515
492
  // No hardcoded library names or file paths.
@@ -631,15 +608,13 @@ if (!global.__repro_call) {
631
608
  ? (label && label.length ? label : fn.name)
632
609
  : '(anonymous)';
633
610
  const sourceFile = fn[SYM_SRC_FILE];
634
- const receiverForTrace = shouldCaptureReceiver(label, thisArg) ? thisArg : undefined;
635
611
  const meta = {
636
- // Prefer callsite file/line so traces point to where the function was invoked.
637
- file: callFile || sourceFile || null,
638
- line: callLine ?? null,
612
+ file: sourceFile || callFile || null,
613
+ line: sourceFile ? null : (callLine || null),
639
614
  parentSpanId: forcedParentSpanId
640
615
  };
641
616
 
642
- trace.enter(name, meta, { args, receiver: receiverForTrace });
617
+ trace.enter(name, meta, { args });
643
618
  // After entering, snapshot a clean store that captures the parent + this call’s span,
644
619
  // so callbacks invoked during the callee run are isolated per invocation.
645
620
  const snapshotForArgs = cloneStore(als.getStore());
@@ -654,8 +629,7 @@ if (!global.__repro_call) {
654
629
  const exitDetailBase = {
655
630
  returnValue: out,
656
631
  args,
657
- unawaited: shouldForceExit,
658
- receiver: receiverForTrace
632
+ unawaited: shouldForceExit
659
633
  };
660
634
 
661
635
  if (shouldForceExit) markPromiseUnawaited(out);
@@ -697,8 +671,7 @@ if (!global.__repro_call) {
697
671
  args,
698
672
  threw,
699
673
  error,
700
- unawaited: shouldForceExit,
701
- receiver: receiverForTrace
674
+ unawaited: shouldForceExit
702
675
  };
703
676
  runExit(detail);
704
677
  return value;
@@ -718,7 +691,7 @@ if (!global.__repro_call) {
718
691
  runExit(exitDetailBase);
719
692
  return out;
720
693
  } catch (e) {
721
- trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args, receiver: receiverForTrace });
694
+ trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args });
722
695
  throw e;
723
696
  } finally {
724
697
  if (pendingMarker) {
@@ -880,6 +853,54 @@ function getCurrentTraceId() {
880
853
  return s && s.traceId || null;
881
854
  }
882
855
 
856
+ function setQuerySpanContext(target, ctx) {
857
+ if (!target || typeof target !== 'object' || !ctx) return;
858
+ const safeCtx = {
859
+ traceId: ctx.traceId || null,
860
+ spanId: ctx.spanId ?? null,
861
+ parentSpanId: ctx.parentSpanId ?? null,
862
+ depth: ctx.depth ?? null
863
+ };
864
+ try {
865
+ if (!target.__repro_span_context) {
866
+ Object.defineProperty(target, '__repro_span_context', {
867
+ value: safeCtx,
868
+ configurable: true,
869
+ writable: true,
870
+ enumerable: false
871
+ });
872
+ }
873
+ } catch {
874
+ try { target.__repro_span_context = safeCtx; } catch {}
875
+ }
876
+ }
877
+
878
+ function getCurrentSpanContext() {
879
+ try {
880
+ const store = als.getStore();
881
+ if (!store) return null;
882
+
883
+ const stack = Array.isArray(store.__repro_span_stack) ? store.__repro_span_stack : [];
884
+ const top = stack.length ? stack[stack.length - 1] : null;
885
+ const spanId = top && top.id != null ? top.id : null;
886
+ const parentSpanId = top && top.parentId != null
887
+ ? top.parentId
888
+ : (stack.length >= 2 ? (stack[stack.length - 2]?.id ?? null) : null);
889
+ const depth = top && top.depth != null
890
+ ? top.depth
891
+ : (typeof store.depth === 'number'
892
+ ? store.depth
893
+ : (stack.length ? stack.length : null));
894
+
895
+ const traceId = store.traceId || null;
896
+
897
+ if (spanId === null && parentSpanId === null && traceId === null) return null;
898
+ return { traceId, spanId, parentSpanId, depth: depth == null ? null : depth };
899
+ } catch {
900
+ return null;
901
+ }
902
+ }
903
+
883
904
  // ---- Promise propagation fallback: ensure continuations run with the captured store ----
884
905
  let PROMISE_PATCHED = false;
885
906
  function patchPromise() {
@@ -924,6 +945,7 @@ module.exports = {
924
945
  printV8,
925
946
  patchConsole,
926
947
  getCurrentTraceId,
948
+ getCurrentSpanContext,
927
949
  setFunctionLogsEnabled,
928
950
  // export symbols so the require hook can tag function origins
929
951
  SYM_SRC_FILE,