repro-nest 0.0.208 → 0.0.209

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,42 @@ 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 captureSpanContextFromTracer() {
463
+ try {
464
+ const ctx = __TRACER__?.getCurrentSpanContext?.();
465
+ if (ctx) {
466
+ const span = {
467
+ traceId: ctx.traceId ?? __TRACER__?.getCurrentTraceId?.() ?? null,
468
+ spanId: ctx.spanId ?? null,
469
+ parentSpanId: ctx.parentSpanId ?? null,
470
+ depth: ctx.depth ?? null,
471
+ };
472
+ if (span.traceId || span.spanId !== null || span.parentSpanId !== null) {
473
+ return span;
474
+ }
475
+ }
476
+ else if (__TRACER__?.getCurrentTraceId) {
477
+ const traceId = __TRACER__?.getCurrentTraceId?.() ?? null;
478
+ if (traceId) {
479
+ return { traceId, spanId: null, parentSpanId: null, depth: null };
480
+ }
481
+ }
482
+ }
483
+ catch { }
484
+ return null;
485
+ }
486
+ function attachSpanContext(target, span) {
487
+ if (!target)
488
+ return target;
489
+ const ctx = span ?? captureSpanContextFromTracer();
490
+ if (ctx) {
491
+ try {
492
+ target.spanContext = ctx;
493
+ }
494
+ catch { }
495
+ }
496
+ return target;
497
+ }
469
498
  const als = new async_hooks_1.AsyncLocalStorage();
470
499
  const getCtx = () => als.getStore() || {};
471
500
  function currentClockSkewMs() {
@@ -807,8 +836,7 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
807
836
  if (Number.isFinite(env) && env >= 0)
808
837
  return env;
809
838
  // 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;
839
+ return 10000;
812
840
  })();
813
841
  function isThenable(value) {
814
842
  return value != null && typeof value === 'object' && typeof value.then === 'function';
@@ -1335,9 +1363,6 @@ function reproMiddleware(cfg) {
1335
1363
  if (ev.args !== undefined) {
1336
1364
  evt.args = sanitizeTraceArgs(ev.args);
1337
1365
  }
1338
- if (ev.receiver !== undefined) {
1339
- evt.receiver = sanitizeTraceValue(ev.receiver);
1340
- }
1341
1366
  if (ev.returnValue !== undefined) {
1342
1367
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1343
1368
  }
@@ -1547,6 +1572,7 @@ function reproMongoosePlugin(cfg) {
1547
1572
  wasNew: this.isNew,
1548
1573
  before,
1549
1574
  collection: resolveCollectionOrWarn(this, 'doc'),
1575
+ spanContext: captureSpanContextFromTracer(),
1550
1576
  };
1551
1577
  next();
1552
1578
  });
@@ -1560,13 +1586,21 @@ function reproMongoosePlugin(cfg) {
1560
1586
  const before = meta.before ?? null;
1561
1587
  const after = this.toObject({ depopulate: true });
1562
1588
  const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
1589
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1563
1590
  const query = meta.wasNew
1564
1591
  ? { op: 'insertOne', doc: after }
1565
1592
  : { filter: { _id: this._id }, update: buildMinimalUpdate(before, after), options: { upsert: false } };
1566
1593
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1567
1594
  entries: [{
1568
1595
  actionId: getCtx().aid,
1569
- db: [{ collection, pk: { _id: this._id }, before, after, op: meta.wasNew ? 'insert' : 'update', query }],
1596
+ db: [attachSpanContext({
1597
+ collection,
1598
+ pk: { _id: this._id },
1599
+ before,
1600
+ after,
1601
+ op: meta.wasNew ? 'insert' : 'update',
1602
+ query,
1603
+ }, spanContext)],
1570
1604
  t: alignedNow(),
1571
1605
  }]
1572
1606
  });
@@ -1582,6 +1616,7 @@ function reproMongoosePlugin(cfg) {
1582
1616
  this.__repro_before = await model.findOne(filter).lean().exec();
1583
1617
  this.setOptions({ new: true });
1584
1618
  this.__repro_collection = resolveCollectionOrWarn(this, 'query');
1619
+ this.__repro_spanContext = captureSpanContextFromTracer();
1585
1620
  }
1586
1621
  catch { }
1587
1622
  next();
@@ -1593,11 +1628,18 @@ function reproMongoosePlugin(cfg) {
1593
1628
  const before = this.__repro_before ?? null;
1594
1629
  const after = res ?? null;
1595
1630
  const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
1631
+ const spanContext = this.__repro_spanContext || captureSpanContextFromTracer();
1596
1632
  const pk = after?._id ?? before?._id;
1597
1633
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1598
1634
  entries: [{
1599
1635
  actionId: getCtx().aid,
1600
- db: [{ collection, pk: { _id: pk }, before, after, op: after && before ? 'update' : after ? 'insert' : 'update' }],
1636
+ db: [attachSpanContext({
1637
+ collection,
1638
+ pk: { _id: pk },
1639
+ before,
1640
+ after,
1641
+ op: after && before ? 'update' : after ? 'insert' : 'update',
1642
+ }, spanContext)],
1601
1643
  t: alignedNow()
1602
1644
  }]
1603
1645
  });
@@ -1612,6 +1654,7 @@ function reproMongoosePlugin(cfg) {
1612
1654
  this.__repro_before = await this.model.findOne(filter).lean().exec();
1613
1655
  this.__repro_collection = resolveCollectionOrWarn(this, 'query');
1614
1656
  this.__repro_filter = filter;
1657
+ this.__repro_spanContext = captureSpanContextFromTracer();
1615
1658
  }
1616
1659
  catch { }
1617
1660
  next();
@@ -1625,10 +1668,18 @@ function reproMongoosePlugin(cfg) {
1625
1668
  return;
1626
1669
  const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
1627
1670
  const filter = this.__repro_filter ?? { _id: before._id };
1671
+ const spanContext = this.__repro_spanContext || captureSpanContextFromTracer();
1628
1672
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
1629
1673
  entries: [{
1630
1674
  actionId: getCtx().aid,
1631
- db: [{ collection, pk: { _id: before._id }, before, after: null, op: 'delete', query: { filter } }],
1675
+ db: [attachSpanContext({
1676
+ collection,
1677
+ pk: { _id: before._id },
1678
+ before,
1679
+ after: null,
1680
+ op: 'delete',
1681
+ query: { filter },
1682
+ }, spanContext)],
1632
1683
  t: alignedNow()
1633
1684
  }]
1634
1685
  });
@@ -1666,6 +1717,7 @@ function reproMongoosePlugin(cfg) {
1666
1717
  t0: Date.now(),
1667
1718
  collection: this?.model?.collection?.name || 'unknown',
1668
1719
  op,
1720
+ spanContext: captureSpanContextFromTracer(),
1669
1721
  filter: sanitizeDbValue(this.getFilter?.() ?? this._conditions ?? undefined),
1670
1722
  update: sanitizeDbValue(this.getUpdate?.() ?? this._update ?? undefined),
1671
1723
  projection: sanitizeDbValue(this.projection?.() ?? this._fields ?? undefined),
@@ -1673,7 +1725,12 @@ function reproMongoosePlugin(cfg) {
1673
1725
  };
1674
1726
  }
1675
1727
  catch {
1676
- this.__repro_qmeta = { t0: Date.now(), collection: 'unknown', op };
1728
+ this.__repro_qmeta = {
1729
+ t0: Date.now(),
1730
+ collection: 'unknown',
1731
+ op,
1732
+ spanContext: captureSpanContextFromTracer(),
1733
+ };
1677
1734
  }
1678
1735
  next();
1679
1736
  });
@@ -1683,6 +1740,7 @@ function reproMongoosePlugin(cfg) {
1683
1740
  return;
1684
1741
  const meta = this.__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
1685
1742
  const resultMeta = summarizeQueryResult(op, res);
1743
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1686
1744
  emitDbQuery(cfg, sid, aid, {
1687
1745
  collection: meta.collection,
1688
1746
  op,
@@ -1690,6 +1748,7 @@ function reproMongoosePlugin(cfg) {
1690
1748
  resultMeta,
1691
1749
  durMs: Date.now() - meta.t0,
1692
1750
  t: alignedNow(),
1751
+ spanContext,
1693
1752
  });
1694
1753
  });
1695
1754
  }
@@ -1702,10 +1761,15 @@ function reproMongoosePlugin(cfg) {
1702
1761
  t0: Date.now(),
1703
1762
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1704
1763
  docs: sanitizeDbValue(docs),
1764
+ spanContext: captureSpanContextFromTracer(),
1705
1765
  };
1706
1766
  }
1707
1767
  catch {
1708
- this.__repro_insert_meta = { t0: Date.now(), collection: 'unknown' };
1768
+ this.__repro_insert_meta = {
1769
+ t0: Date.now(),
1770
+ collection: 'unknown',
1771
+ spanContext: captureSpanContextFromTracer(),
1772
+ };
1709
1773
  }
1710
1774
  next();
1711
1775
  });
@@ -1715,6 +1779,7 @@ function reproMongoosePlugin(cfg) {
1715
1779
  return;
1716
1780
  const meta = this.__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
1717
1781
  const resultMeta = Array.isArray(docs) ? { inserted: docs.length } : summarizeQueryResult('insertMany', docs);
1782
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1718
1783
  emitDbQuery(cfg, sid, aid, {
1719
1784
  collection: meta.collection,
1720
1785
  op: 'insertMany',
@@ -1722,6 +1787,7 @@ function reproMongoosePlugin(cfg) {
1722
1787
  resultMeta,
1723
1788
  durMs: Date.now() - meta.t0,
1724
1789
  t: alignedNow(),
1790
+ spanContext,
1725
1791
  });
1726
1792
  });
1727
1793
  schema.pre('bulkWrite', { document: false, query: false }, function (next, ops) {
@@ -1730,10 +1796,15 @@ function reproMongoosePlugin(cfg) {
1730
1796
  t0: Date.now(),
1731
1797
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1732
1798
  ops: sanitizeDbValue(ops),
1799
+ spanContext: captureSpanContextFromTracer(),
1733
1800
  };
1734
1801
  }
1735
1802
  catch {
1736
- this.__repro_bulk_meta = { t0: Date.now(), collection: 'unknown' };
1803
+ this.__repro_bulk_meta = {
1804
+ t0: Date.now(),
1805
+ collection: 'unknown',
1806
+ spanContext: captureSpanContextFromTracer(),
1807
+ };
1737
1808
  }
1738
1809
  next();
1739
1810
  });
@@ -1744,6 +1815,7 @@ function reproMongoosePlugin(cfg) {
1744
1815
  const meta = this.__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
1745
1816
  const bulkResult = summarizeBulkResult(res);
1746
1817
  const resultMeta = { ...bulkResult, result: sanitizeResultForMeta(res?.result ?? res) };
1818
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1747
1819
  emitDbQuery(cfg, sid, aid, {
1748
1820
  collection: meta.collection,
1749
1821
  op: 'bulkWrite',
@@ -1751,6 +1823,7 @@ function reproMongoosePlugin(cfg) {
1751
1823
  resultMeta,
1752
1824
  durMs: Date.now() - meta.t0,
1753
1825
  t: alignedNow(),
1826
+ spanContext,
1754
1827
  });
1755
1828
  });
1756
1829
  // Aggregate middleware (non-intrusive)
@@ -1762,11 +1835,17 @@ function reproMongoosePlugin(cfg) {
1762
1835
  this?._model?.collection?.name ||
1763
1836
  (this?.model && this.model.collection?.name) ||
1764
1837
  'unknown',
1838
+ spanContext: captureSpanContextFromTracer(),
1765
1839
  pipeline: sanitizeDbValue(this.pipeline?.() ?? this._pipeline ?? undefined),
1766
1840
  };
1767
1841
  }
1768
1842
  catch {
1769
- this.__repro_aggmeta = { t0: Date.now(), collection: 'unknown', pipeline: undefined };
1843
+ this.__repro_aggmeta = {
1844
+ t0: Date.now(),
1845
+ collection: 'unknown',
1846
+ pipeline: undefined,
1847
+ spanContext: captureSpanContextFromTracer(),
1848
+ };
1770
1849
  }
1771
1850
  next();
1772
1851
  });
@@ -1776,6 +1855,7 @@ function reproMongoosePlugin(cfg) {
1776
1855
  return;
1777
1856
  const meta = this.__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
1778
1857
  const resultMeta = summarizeQueryResult('aggregate', res);
1858
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1779
1859
  emitDbQuery(cfg, sid, aid, {
1780
1860
  collection: meta.collection,
1781
1861
  op: 'aggregate',
@@ -1783,6 +1863,7 @@ function reproMongoosePlugin(cfg) {
1783
1863
  resultMeta,
1784
1864
  durMs: Date.now() - meta.t0,
1785
1865
  t: alignedNow(),
1866
+ spanContext,
1786
1867
  });
1787
1868
  });
1788
1869
  };
@@ -1899,26 +1980,19 @@ function dehydrateComplexValue(value) {
1899
1980
  function emitDbQuery(cfg, sid, aid, payload) {
1900
1981
  if (!sid)
1901
1982
  return;
1902
- let traceCtx = null;
1903
- try {
1904
- const getCtx = __TRACER__?.tracer?.getCurrentTraceContext;
1905
- traceCtx = typeof getCtx === 'function' ? getCtx() : null;
1906
- }
1907
- catch { }
1983
+ const dbEntry = attachSpanContext({
1984
+ collection: payload.collection,
1985
+ op: payload.op,
1986
+ query: payload.query ?? undefined,
1987
+ resultMeta: payload.resultMeta ?? undefined,
1988
+ durMs: payload.durMs ?? undefined,
1989
+ pk: null, before: null, after: null,
1990
+ error: payload.error ?? undefined,
1991
+ }, payload?.spanContext);
1908
1992
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
1909
1993
  entries: [{
1910
1994
  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
- }],
1995
+ db: [dbEntry],
1922
1996
  t: payload.t,
1923
1997
  }]
1924
1998
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repro-nest",
3
- "version": "0.0.208",
3
+ "version": "0.0.209",
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,38 @@ 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 captureSpanContextFromTracer(): SpanContext | null {
652
+ try {
653
+ const ctx = __TRACER__?.getCurrentSpanContext?.();
654
+ if (ctx) {
655
+ const span: SpanContext = {
656
+ traceId: ctx.traceId ?? __TRACER__?.getCurrentTraceId?.() ?? null,
657
+ spanId: ctx.spanId ?? null,
658
+ parentSpanId: ctx.parentSpanId ?? null,
659
+ depth: ctx.depth ?? null,
660
+ };
661
+ if (span.traceId || span.spanId !== null || span.parentSpanId !== null) {
662
+ return span;
663
+ }
664
+ } else if (__TRACER__?.getCurrentTraceId) {
665
+ const traceId = __TRACER__?.getCurrentTraceId?.() ?? null;
666
+ if (traceId) {
667
+ return { traceId, spanId: null, parentSpanId: null, depth: null };
668
+ }
669
+ }
670
+ } catch {}
671
+ return null;
672
+ }
673
+
674
+ function attachSpanContext<T extends Record<string, any>>(target: T, span?: SpanContext | null): T {
675
+ if (!target) return target;
676
+ const ctx = span ?? captureSpanContextFromTracer();
677
+ if (ctx) {
678
+ try { (target as any).spanContext = ctx; } catch {}
679
+ }
680
+ return target;
681
+ }
682
+
653
683
  type Ctx = { sid?: string; aid?: string; clockSkewMs?: number };
654
684
  const als = new AsyncLocalStorage<Ctx>();
655
685
  const getCtx = () => als.getStore() || {};
@@ -1033,8 +1063,7 @@ const SESSION_DRAIN_TIMEOUT_MS = (() => {
1033
1063
  const env = Number(process.env.SESSION_DRAIN_TIMEOUT_MS);
1034
1064
  if (Number.isFinite(env) && env >= 0) return env;
1035
1065
  // 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;
1066
+ return 10000;
1038
1067
  })();
1039
1068
 
1040
1069
  function isThenable(value: any): value is PromiseLike<any> {
@@ -1543,9 +1572,6 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
1543
1572
  if (ev.args !== undefined) {
1544
1573
  evt.args = sanitizeTraceArgs(ev.args);
1545
1574
  }
1546
- if (ev.receiver !== undefined) {
1547
- evt.receiver = sanitizeTraceValue(ev.receiver);
1548
- }
1549
1575
  if (ev.returnValue !== undefined) {
1550
1576
  evt.returnValue = sanitizeTraceValue(ev.returnValue);
1551
1577
  }
@@ -1759,6 +1785,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1759
1785
  wasNew: this.isNew,
1760
1786
  before,
1761
1787
  collection: resolveCollectionOrWarn(this, 'doc'),
1788
+ spanContext: captureSpanContextFromTracer(),
1762
1789
  };
1763
1790
  next();
1764
1791
  });
@@ -1772,6 +1799,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1772
1799
  const before = meta.before ?? null;
1773
1800
  const after = this.toObject({ depopulate: true });
1774
1801
  const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
1802
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1775
1803
 
1776
1804
  const query = meta.wasNew
1777
1805
  ? { op: 'insertOne', doc: after }
@@ -1780,7 +1808,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1780
1808
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1781
1809
  entries: [{
1782
1810
  actionId: (getCtx() as Ctx).aid!,
1783
- db: [{ collection, pk: { _id: (this as any)._id }, before, after, op: meta.wasNew ? 'insert' : 'update', query }],
1811
+ db: [attachSpanContext({
1812
+ collection,
1813
+ pk: { _id: (this as any)._id },
1814
+ before,
1815
+ after,
1816
+ op: meta.wasNew ? 'insert' : 'update',
1817
+ query,
1818
+ }, spanContext)],
1784
1819
  t: alignedNow(),
1785
1820
  }]
1786
1821
  });
@@ -1796,6 +1831,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1796
1831
  (this as any).__repro_before = await model.findOne(filter).lean().exec();
1797
1832
  this.setOptions({ new: true });
1798
1833
  (this as any).__repro_collection = resolveCollectionOrWarn(this, 'query');
1834
+ (this as any).__repro_spanContext = captureSpanContextFromTracer();
1799
1835
  } catch {}
1800
1836
  next();
1801
1837
  });
@@ -1807,12 +1843,19 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1807
1843
  const before = (this as any).__repro_before ?? null;
1808
1844
  const after = res ?? null;
1809
1845
  const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
1846
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer();
1810
1847
  const pk = after?._id ?? before?._id;
1811
1848
 
1812
1849
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1813
1850
  entries: [{
1814
1851
  actionId: (getCtx() as Ctx).aid!,
1815
- db: [{ collection, pk: { _id: pk }, before, after, op: after && before ? 'update' : after ? 'insert' : 'update' }],
1852
+ db: [attachSpanContext({
1853
+ collection,
1854
+ pk: { _id: pk },
1855
+ before,
1856
+ after,
1857
+ op: after && before ? 'update' : after ? 'insert' : 'update',
1858
+ }, spanContext)],
1816
1859
  t: alignedNow()
1817
1860
  }]
1818
1861
  });
@@ -1826,6 +1869,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1826
1869
  (this as any).__repro_before = await (this.model as Model<any>).findOne(filter).lean().exec();
1827
1870
  (this as any).__repro_collection = resolveCollectionOrWarn(this, 'query');
1828
1871
  (this as any).__repro_filter = filter;
1872
+ (this as any).__repro_spanContext = captureSpanContextFromTracer();
1829
1873
  } catch {}
1830
1874
  next();
1831
1875
  });
@@ -1836,10 +1880,18 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1836
1880
  if (!before) return;
1837
1881
  const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
1838
1882
  const filter = (this as any).__repro_filter ?? { _id: before._id };
1883
+ const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer();
1839
1884
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
1840
1885
  entries: [{
1841
1886
  actionId: (getCtx() as Ctx).aid!,
1842
- db: [{ collection, pk: { _id: before._id }, before, after: null, op: 'delete', query: { filter } }],
1887
+ db: [attachSpanContext({
1888
+ collection,
1889
+ pk: { _id: before._id },
1890
+ before,
1891
+ after: null,
1892
+ op: 'delete',
1893
+ query: { filter },
1894
+ }, spanContext)],
1843
1895
  t: alignedNow()
1844
1896
  }]
1845
1897
  });
@@ -1881,13 +1933,19 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1881
1933
  t0: Date.now(),
1882
1934
  collection: this?.model?.collection?.name || 'unknown',
1883
1935
  op,
1936
+ spanContext: captureSpanContextFromTracer(),
1884
1937
  filter: sanitizeDbValue(this.getFilter?.() ?? this._conditions ?? undefined),
1885
1938
  update: sanitizeDbValue(this.getUpdate?.() ?? this._update ?? undefined),
1886
1939
  projection: sanitizeDbValue(this.projection?.() ?? this._fields ?? undefined),
1887
1940
  options: sanitizeDbValue(this.getOptions?.() ?? this.options ?? undefined),
1888
1941
  };
1889
1942
  } catch {
1890
- (this as any).__repro_qmeta = { t0: Date.now(), collection: 'unknown', op };
1943
+ (this as any).__repro_qmeta = {
1944
+ t0: Date.now(),
1945
+ collection: 'unknown',
1946
+ op,
1947
+ spanContext: captureSpanContextFromTracer(),
1948
+ };
1891
1949
  }
1892
1950
  next();
1893
1951
  });
@@ -1898,6 +1956,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1898
1956
 
1899
1957
  const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
1900
1958
  const resultMeta = summarizeQueryResult(op, res);
1959
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1901
1960
 
1902
1961
  emitDbQuery(cfg, sid, aid, {
1903
1962
  collection: meta.collection,
@@ -1906,6 +1965,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1906
1965
  resultMeta,
1907
1966
  durMs: Date.now() - meta.t0,
1908
1967
  t: alignedNow(),
1968
+ spanContext,
1909
1969
  });
1910
1970
  });
1911
1971
  }
@@ -1920,9 +1980,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1920
1980
  t0: Date.now(),
1921
1981
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1922
1982
  docs: sanitizeDbValue(docs),
1983
+ spanContext: captureSpanContextFromTracer(),
1923
1984
  };
1924
1985
  } catch {
1925
- (this as any).__repro_insert_meta = { t0: Date.now(), collection: 'unknown' };
1986
+ (this as any).__repro_insert_meta = {
1987
+ t0: Date.now(),
1988
+ collection: 'unknown',
1989
+ spanContext: captureSpanContextFromTracer(),
1990
+ };
1926
1991
  }
1927
1992
  next();
1928
1993
  } as any);
@@ -1932,6 +1997,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1932
1997
  if (!sid) return;
1933
1998
  const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
1934
1999
  const resultMeta = Array.isArray(docs) ? { inserted: docs.length } : summarizeQueryResult('insertMany', docs);
2000
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1935
2001
 
1936
2002
  emitDbQuery(cfg, sid, aid, {
1937
2003
  collection: meta.collection,
@@ -1940,6 +2006,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1940
2006
  resultMeta,
1941
2007
  durMs: Date.now() - meta.t0,
1942
2008
  t: alignedNow(),
2009
+ spanContext,
1943
2010
  });
1944
2011
  } as any);
1945
2012
 
@@ -1949,9 +2016,14 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1949
2016
  t0: Date.now(),
1950
2017
  collection: this?.collection?.name || this?.model?.collection?.name || 'unknown',
1951
2018
  ops: sanitizeDbValue(ops),
2019
+ spanContext: captureSpanContextFromTracer(),
1952
2020
  };
1953
2021
  } catch {
1954
- (this as any).__repro_bulk_meta = { t0: Date.now(), collection: 'unknown' };
2022
+ (this as any).__repro_bulk_meta = {
2023
+ t0: Date.now(),
2024
+ collection: 'unknown',
2025
+ spanContext: captureSpanContextFromTracer(),
2026
+ };
1955
2027
  }
1956
2028
  next();
1957
2029
  } as any);
@@ -1962,6 +2034,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1962
2034
  const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
1963
2035
  const bulkResult = summarizeBulkResult(res);
1964
2036
  const resultMeta = { ...bulkResult, result: sanitizeResultForMeta(res?.result ?? res) };
2037
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
1965
2038
 
1966
2039
  emitDbQuery(cfg, sid, aid, {
1967
2040
  collection: meta.collection,
@@ -1970,6 +2043,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1970
2043
  resultMeta,
1971
2044
  durMs: Date.now() - meta.t0,
1972
2045
  t: alignedNow(),
2046
+ spanContext,
1973
2047
  });
1974
2048
  } as any);
1975
2049
 
@@ -1983,10 +2057,16 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1983
2057
  this?._model?.collection?.name ||
1984
2058
  (this?.model && this.model.collection?.name) ||
1985
2059
  'unknown',
2060
+ spanContext: captureSpanContextFromTracer(),
1986
2061
  pipeline: sanitizeDbValue(this.pipeline?.() ?? this._pipeline ?? undefined),
1987
2062
  };
1988
2063
  } catch {
1989
- (this as any).__repro_aggmeta = { t0: Date.now(), collection: 'unknown', pipeline: undefined };
2064
+ (this as any).__repro_aggmeta = {
2065
+ t0: Date.now(),
2066
+ collection: 'unknown',
2067
+ pipeline: undefined,
2068
+ spanContext: captureSpanContextFromTracer(),
2069
+ };
1990
2070
  }
1991
2071
  next();
1992
2072
  });
@@ -1997,6 +2077,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
1997
2077
 
1998
2078
  const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
1999
2079
  const resultMeta = summarizeQueryResult('aggregate', res);
2080
+ const spanContext = meta.spanContext || captureSpanContextFromTracer();
2000
2081
 
2001
2082
  emitDbQuery(cfg, sid, aid, {
2002
2083
  collection: meta.collection,
@@ -2005,6 +2086,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
2005
2086
  resultMeta,
2006
2087
  durMs: Date.now() - meta.t0,
2007
2088
  t: alignedNow(),
2089
+ spanContext,
2008
2090
  });
2009
2091
  });
2010
2092
  };
@@ -2114,26 +2196,19 @@ function dehydrateComplexValue(value: any) {
2114
2196
 
2115
2197
  function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
2116
2198
  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
-
2199
+ const dbEntry = attachSpanContext({
2200
+ collection: payload.collection,
2201
+ op: payload.op,
2202
+ query: payload.query ?? undefined,
2203
+ resultMeta: payload.resultMeta ?? undefined,
2204
+ durMs: payload.durMs ?? undefined,
2205
+ pk: null, before: null, after: null,
2206
+ error: payload.error ?? undefined,
2207
+ }, payload?.spanContext);
2123
2208
  post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
2124
2209
  entries: [{
2125
2210
  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
- }],
2211
+ db: [dbEntry],
2137
2212
  t: payload.t,
2138
2213
  }]
2139
2214
  });
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
  };
@@ -492,24 +475,6 @@ function forkAlsStoreForUnawaited(baseStore) {
492
475
  return cloned;
493
476
  }
494
477
 
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
478
  // ========= Generic call-site shim (used by Babel transform) =========
514
479
  // Decides whether to emit a top-level event based on callee origin tags.
515
480
  // No hardcoded library names or file paths.
@@ -631,15 +596,13 @@ if (!global.__repro_call) {
631
596
  ? (label && label.length ? label : fn.name)
632
597
  : '(anonymous)';
633
598
  const sourceFile = fn[SYM_SRC_FILE];
634
- const receiverForTrace = shouldCaptureReceiver(label, thisArg) ? thisArg : undefined;
635
599
  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,
600
+ file: sourceFile || callFile || null,
601
+ line: sourceFile ? null : (callLine || null),
639
602
  parentSpanId: forcedParentSpanId
640
603
  };
641
604
 
642
- trace.enter(name, meta, { args, receiver: receiverForTrace });
605
+ trace.enter(name, meta, { args });
643
606
  // After entering, snapshot a clean store that captures the parent + this call’s span,
644
607
  // so callbacks invoked during the callee run are isolated per invocation.
645
608
  const snapshotForArgs = cloneStore(als.getStore());
@@ -654,8 +617,7 @@ if (!global.__repro_call) {
654
617
  const exitDetailBase = {
655
618
  returnValue: out,
656
619
  args,
657
- unawaited: shouldForceExit,
658
- receiver: receiverForTrace
620
+ unawaited: shouldForceExit
659
621
  };
660
622
 
661
623
  if (shouldForceExit) markPromiseUnawaited(out);
@@ -697,8 +659,7 @@ if (!global.__repro_call) {
697
659
  args,
698
660
  threw,
699
661
  error,
700
- unawaited: shouldForceExit,
701
- receiver: receiverForTrace
662
+ unawaited: shouldForceExit
702
663
  };
703
664
  runExit(detail);
704
665
  return value;
@@ -718,7 +679,7 @@ if (!global.__repro_call) {
718
679
  runExit(exitDetailBase);
719
680
  return out;
720
681
  } catch (e) {
721
- trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args, receiver: receiverForTrace });
682
+ trace.exit({ fn: name, file: meta.file, line: meta.line }, { threw: true, error: e, args });
722
683
  throw e;
723
684
  } finally {
724
685
  if (pendingMarker) {
@@ -880,6 +841,32 @@ function getCurrentTraceId() {
880
841
  return s && s.traceId || null;
881
842
  }
882
843
 
844
+ function getCurrentSpanContext() {
845
+ try {
846
+ const store = als.getStore();
847
+ if (!store) return null;
848
+
849
+ const stack = Array.isArray(store.__repro_span_stack) ? store.__repro_span_stack : [];
850
+ const top = stack.length ? stack[stack.length - 1] : null;
851
+ const spanId = top && top.id != null ? top.id : null;
852
+ const parentSpanId = top && top.parentId != null
853
+ ? top.parentId
854
+ : (stack.length >= 2 ? (stack[stack.length - 2]?.id ?? null) : null);
855
+ const depth = top && top.depth != null
856
+ ? top.depth
857
+ : (typeof store.depth === 'number'
858
+ ? store.depth
859
+ : (stack.length ? stack.length : null));
860
+
861
+ const traceId = store.traceId || null;
862
+
863
+ if (spanId === null && parentSpanId === null && traceId === null) return null;
864
+ return { traceId, spanId, parentSpanId, depth: depth == null ? null : depth };
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+
883
870
  // ---- Promise propagation fallback: ensure continuations run with the captured store ----
884
871
  let PROMISE_PATCHED = false;
885
872
  function patchPromise() {
@@ -924,6 +911,7 @@ module.exports = {
924
911
  printV8,
925
912
  patchConsole,
926
913
  getCurrentTraceId,
914
+ getCurrentSpanContext,
927
915
  setFunctionLogsEnabled,
928
916
  // export symbols so the require hook can tag function origins
929
917
  SYM_SRC_FILE,