@reproapp/node-sdk 0.0.45 → 0.0.46
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 +1 -0
- package/dist/index.js +34 -3
- package/package.json +2 -2
- package/src/index.ts +41 -3
- package/test/trace-source-exclude.test.js +222 -0
- package/tracer/cjs-hook.js +56 -37
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -332,6 +332,7 @@ function refreshDisabledFunctionTraceRules() {
|
|
|
332
332
|
let disabledFunctionTraceRules = computeDisabledFunctionTraceRules();
|
|
333
333
|
let disabledFunctionTypePatterns = [];
|
|
334
334
|
let disabledTraceFilePatterns = [];
|
|
335
|
+
let excludedInstrumentationFilePatterns = [];
|
|
335
336
|
let __TRACE_LOG_PREF = null;
|
|
336
337
|
let __FULL_VALUE_CAPTURE_ENABLED = true;
|
|
337
338
|
function setInterceptorTracingEnabled(enabled) {
|
|
@@ -415,7 +416,7 @@ function matchesRule(rule, event) {
|
|
|
415
416
|
const wrapperPattern = rule.wrapper ?? rule.wrapperClass ?? rule.className ?? rule.owner;
|
|
416
417
|
if (!matchesPattern(event.wrapperClass, wrapperPattern))
|
|
417
418
|
return false;
|
|
418
|
-
if (!
|
|
419
|
+
if (!matchesTraceFile(event, rule.file))
|
|
419
420
|
return false;
|
|
420
421
|
if (!matchesPattern(event.line == null ? null : String(event.line), rule.line))
|
|
421
422
|
return false;
|
|
@@ -430,9 +431,23 @@ function matchesRule(rule, event) {
|
|
|
430
431
|
return false;
|
|
431
432
|
return true;
|
|
432
433
|
}
|
|
434
|
+
function matchesTraceFile(event, pattern, defaultWhenEmpty = true) {
|
|
435
|
+
if (pattern === undefined || pattern === null)
|
|
436
|
+
return defaultWhenEmpty;
|
|
437
|
+
const candidates = [event.file, event.sourceFile].filter((value) => typeof value === 'string' && value.length > 0);
|
|
438
|
+
if (!candidates.length) {
|
|
439
|
+
return matchesPattern(null, pattern, defaultWhenEmpty);
|
|
440
|
+
}
|
|
441
|
+
return candidates.some((value) => matchesPattern(value, pattern, false));
|
|
442
|
+
}
|
|
433
443
|
function shouldDropTraceEvent(event) {
|
|
434
444
|
if (disabledTraceFilePatterns.length) {
|
|
435
|
-
if (
|
|
445
|
+
if (matchesTraceFile(event, disabledTraceFilePatterns, false)) {
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (excludedInstrumentationFilePatterns.length) {
|
|
450
|
+
if (matchesTraceFile(event, excludedInstrumentationFilePatterns, false)) {
|
|
436
451
|
return true;
|
|
437
452
|
}
|
|
438
453
|
}
|
|
@@ -599,6 +614,9 @@ function initReproTracing(opts) {
|
|
|
599
614
|
if (hasOwn(options, 'disableTraceFiles')) {
|
|
600
615
|
setDisabledTraceFiles(options.disableTraceFiles ?? null);
|
|
601
616
|
}
|
|
617
|
+
if (hasOwn(options, 'exclude')) {
|
|
618
|
+
excludedInstrumentationFilePatterns = normalizePatternArray(options.exclude);
|
|
619
|
+
}
|
|
602
620
|
if (hasOwn(options, 'logFunctionCalls') && typeof options.logFunctionCalls === 'boolean') {
|
|
603
621
|
setReproTraceLogsEnabled(options.logFunctionCalls);
|
|
604
622
|
}
|
|
@@ -611,7 +629,17 @@ function initReproTracing(opts) {
|
|
|
611
629
|
__TRACER__ = tracerPkg;
|
|
612
630
|
applyTraceLogPreference(tracerPkg);
|
|
613
631
|
const { disableFunctionTraces: _disableFunctionTraces, disableFunctionTypes: _disableFunctionTypes, disableTraceFiles: _disableTraceFiles, fullValueCapture: _fullValueCapture, logFunctionCalls: _logFunctionCalls, traceInterceptors: _traceInterceptors, ...rest } = options;
|
|
614
|
-
const
|
|
632
|
+
const defaults = defaultTracerInitOpts();
|
|
633
|
+
const overrides = rest;
|
|
634
|
+
const initOpts = {
|
|
635
|
+
...defaults,
|
|
636
|
+
...overrides,
|
|
637
|
+
include: overrides.include ?? defaults.include,
|
|
638
|
+
exclude: [
|
|
639
|
+
...(defaults.exclude ?? []),
|
|
640
|
+
...(overrides.exclude ?? []),
|
|
641
|
+
],
|
|
642
|
+
};
|
|
615
643
|
tracerPkg.init?.(initOpts);
|
|
616
644
|
tracerPkg.patchHttp?.();
|
|
617
645
|
applyTraceLogPreference(tracerPkg);
|
|
@@ -736,6 +764,7 @@ function traceEventCandidateFromRecord(event) {
|
|
|
736
764
|
fn: event.fn,
|
|
737
765
|
wrapperClass: inferWrapperClassFromFn(event.fn),
|
|
738
766
|
file: event.file ?? null,
|
|
767
|
+
sourceFile: sourceFileForLibrary,
|
|
739
768
|
line: event.line ?? null,
|
|
740
769
|
depth: event.depth,
|
|
741
770
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? event.file),
|
|
@@ -6336,6 +6365,7 @@ function recordKafkaTraceEvent(raw, sink, cfg, maskReq) {
|
|
|
6336
6365
|
fn: raw.fn,
|
|
6337
6366
|
wrapperClass: inferWrapperClassFromFn(raw.fn),
|
|
6338
6367
|
file: raw.file ?? null,
|
|
6368
|
+
sourceFile: sourceFileForLibrary,
|
|
6339
6369
|
line: typeof raw.line === 'number' ? raw.line : null,
|
|
6340
6370
|
depth: typeof raw.depth === 'number' ? raw.depth : undefined,
|
|
6341
6371
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
|
|
@@ -6441,6 +6471,7 @@ function buildPendingKafkaTraceEvent(raw) {
|
|
|
6441
6471
|
fn: raw.fn,
|
|
6442
6472
|
wrapperClass: inferWrapperClassFromFn(raw.fn),
|
|
6443
6473
|
file: raw.file ?? null,
|
|
6474
|
+
sourceFile: sourceFileForLibrary,
|
|
6444
6475
|
line: typeof raw.line === 'number' ? raw.line : null,
|
|
6445
6476
|
depth: typeof raw.depth === 'number' ? raw.depth : undefined,
|
|
6446
6477
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reproapp/node-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"description": "Repro Nest SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"build": "tsc -p tsconfig.json",
|
|
13
13
|
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
|
|
14
14
|
"prepublishOnly": "npm run build",
|
|
15
|
-
"test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/backend-live-mode.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/runtime-service-registration.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-batching.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-service-scope.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js && node test/postgresql-integration.test.js",
|
|
15
|
+
"test": "npm run build && node test/trace-source-exclude.test.js && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/backend-live-mode.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/runtime-service-registration.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-batching.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-service-scope.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js && node test/postgresql-integration.test.js",
|
|
16
16
|
"test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js && node test/mongodb-mongoose-native-suppression.testcontainers.test.js && node test/postgresql-integrations.testcontainers.test.js"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -341,6 +341,7 @@ export type TraceEventForFilter = {
|
|
|
341
341
|
fn?: string;
|
|
342
342
|
wrapperClass?: string | null;
|
|
343
343
|
file?: string | null;
|
|
344
|
+
sourceFile?: string | null;
|
|
344
345
|
line?: number | null;
|
|
345
346
|
depth?: number;
|
|
346
347
|
library?: string | null;
|
|
@@ -606,6 +607,7 @@ function refreshDisabledFunctionTraceRules() {
|
|
|
606
607
|
let disabledFunctionTraceRules: DisableFunctionTraceConfig[] = computeDisabledFunctionTraceRules();
|
|
607
608
|
let disabledFunctionTypePatterns: Array<string | number | RegExp> = [];
|
|
608
609
|
let disabledTraceFilePatterns: Array<string | number | RegExp> = [];
|
|
610
|
+
let excludedInstrumentationFilePatterns: Array<string | number | RegExp> = [];
|
|
609
611
|
let __TRACE_LOG_PREF: boolean | null = null;
|
|
610
612
|
let __FULL_VALUE_CAPTURE_ENABLED = true;
|
|
611
613
|
|
|
@@ -684,7 +686,7 @@ function matchesRule(rule: DisableFunctionTraceRule, event: TraceEventForFilter)
|
|
|
684
686
|
const wrapperPattern = rule.wrapper ?? rule.wrapperClass ?? rule.className ?? rule.owner;
|
|
685
687
|
if (!matchesPattern(event.wrapperClass, wrapperPattern)) return false;
|
|
686
688
|
|
|
687
|
-
if (!
|
|
689
|
+
if (!matchesTraceFile(event, rule.file)) return false;
|
|
688
690
|
if (!matchesPattern(event.line == null ? null : String(event.line), rule.line)) return false;
|
|
689
691
|
|
|
690
692
|
const libPattern = rule.lib ?? rule.library;
|
|
@@ -699,9 +701,29 @@ function matchesRule(rule: DisableFunctionTraceRule, event: TraceEventForFilter)
|
|
|
699
701
|
return true;
|
|
700
702
|
}
|
|
701
703
|
|
|
704
|
+
function matchesTraceFile(
|
|
705
|
+
event: Pick<TraceEventForFilter, 'file' | 'sourceFile'>,
|
|
706
|
+
pattern?: TraceRulePattern,
|
|
707
|
+
defaultWhenEmpty: boolean = true,
|
|
708
|
+
): boolean {
|
|
709
|
+
if (pattern === undefined || pattern === null) return defaultWhenEmpty;
|
|
710
|
+
const candidates = [event.file, event.sourceFile].filter(
|
|
711
|
+
(value): value is string => typeof value === 'string' && value.length > 0,
|
|
712
|
+
);
|
|
713
|
+
if (!candidates.length) {
|
|
714
|
+
return matchesPattern(null, pattern, defaultWhenEmpty);
|
|
715
|
+
}
|
|
716
|
+
return candidates.some((value) => matchesPattern(value, pattern, false));
|
|
717
|
+
}
|
|
718
|
+
|
|
702
719
|
function shouldDropTraceEvent(event: TraceEventForFilter): boolean {
|
|
703
720
|
if (disabledTraceFilePatterns.length) {
|
|
704
|
-
if (
|
|
721
|
+
if (matchesTraceFile(event, disabledTraceFilePatterns, false)) {
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (excludedInstrumentationFilePatterns.length) {
|
|
726
|
+
if (matchesTraceFile(event, excludedInstrumentationFilePatterns, false)) {
|
|
705
727
|
return true;
|
|
706
728
|
}
|
|
707
729
|
}
|
|
@@ -948,6 +970,9 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
|
|
|
948
970
|
if (hasOwn(options, 'disableTraceFiles')) {
|
|
949
971
|
setDisabledTraceFiles(options.disableTraceFiles ?? null);
|
|
950
972
|
}
|
|
973
|
+
if (hasOwn(options, 'exclude')) {
|
|
974
|
+
excludedInstrumentationFilePatterns = normalizePatternArray(options.exclude);
|
|
975
|
+
}
|
|
951
976
|
if (hasOwn(options, 'logFunctionCalls') && typeof options.logFunctionCalls === 'boolean') {
|
|
952
977
|
setReproTraceLogsEnabled(options.logFunctionCalls);
|
|
953
978
|
}
|
|
@@ -971,7 +996,17 @@ export function initReproTracing(opts?: ReproTracingInitOptions) {
|
|
|
971
996
|
traceInterceptors: _traceInterceptors,
|
|
972
997
|
...rest
|
|
973
998
|
} = options;
|
|
974
|
-
const
|
|
999
|
+
const defaults = defaultTracerInitOpts();
|
|
1000
|
+
const overrides = rest as TracerInitOpts;
|
|
1001
|
+
const initOpts = {
|
|
1002
|
+
...defaults,
|
|
1003
|
+
...overrides,
|
|
1004
|
+
include: overrides.include ?? defaults.include,
|
|
1005
|
+
exclude: [
|
|
1006
|
+
...(defaults.exclude ?? []),
|
|
1007
|
+
...(overrides.exclude ?? []),
|
|
1008
|
+
],
|
|
1009
|
+
};
|
|
975
1010
|
tracerPkg.init?.(initOpts);
|
|
976
1011
|
tracerPkg.patchHttp?.();
|
|
977
1012
|
applyTraceLogPreference(tracerPkg);
|
|
@@ -1094,6 +1129,7 @@ function traceEventCandidateFromRecord(event: PendingTraceEventRecord): TraceEve
|
|
|
1094
1129
|
fn: event.fn,
|
|
1095
1130
|
wrapperClass: inferWrapperClassFromFn(event.fn),
|
|
1096
1131
|
file: event.file ?? null,
|
|
1132
|
+
sourceFile: sourceFileForLibrary,
|
|
1097
1133
|
line: event.line ?? null,
|
|
1098
1134
|
depth: event.depth,
|
|
1099
1135
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? event.file),
|
|
@@ -7595,6 +7631,7 @@ function recordKafkaTraceEvent(
|
|
|
7595
7631
|
fn: raw.fn,
|
|
7596
7632
|
wrapperClass: inferWrapperClassFromFn(raw.fn),
|
|
7597
7633
|
file: raw.file ?? null,
|
|
7634
|
+
sourceFile: sourceFileForLibrary,
|
|
7598
7635
|
line: typeof raw.line === 'number' ? raw.line : null,
|
|
7599
7636
|
depth: typeof raw.depth === 'number' ? raw.depth : undefined,
|
|
7600
7637
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
|
|
@@ -7727,6 +7764,7 @@ function buildPendingKafkaTraceEvent(raw: any): PendingTraceEventRecord | null {
|
|
|
7727
7764
|
fn: raw.fn,
|
|
7728
7765
|
wrapperClass: inferWrapperClassFromFn(raw.fn),
|
|
7729
7766
|
file: raw.file ?? null,
|
|
7767
|
+
sourceFile: sourceFileForLibrary,
|
|
7730
7768
|
line: typeof raw.line === 'number' ? raw.line : null,
|
|
7731
7769
|
depth: typeof raw.depth === 'number' ? raw.depth : undefined,
|
|
7732
7770
|
library: inferLibraryNameFromFile(sourceFileForLibrary ?? raw.file),
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
|
|
4
|
+
process.env.TRACE_IDLE_FLUSH_MS = '25';
|
|
5
|
+
process.env.SESSION_DRAIN_TIMEOUT_MS = '0';
|
|
6
|
+
process.env.REPRO_SDK_BACKGROUND_IDLE_DELAY_MS = '25';
|
|
7
|
+
|
|
8
|
+
const assert = require('node:assert');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const { EventEmitter } = require('node:events');
|
|
12
|
+
const {
|
|
13
|
+
initReproTracing,
|
|
14
|
+
reproMiddleware,
|
|
15
|
+
} = require('../dist');
|
|
16
|
+
|
|
17
|
+
const excludeTenantModule =
|
|
18
|
+
/src[\\/]modules[\\/]core[\\/]database[\\/]modules[\\/]tenant[\\/]tenant-database\.module\.ts$/;
|
|
19
|
+
|
|
20
|
+
function writeFixture(root) {
|
|
21
|
+
const distRoot = path.join(root, 'dist');
|
|
22
|
+
const tenantDistDir = path.join(
|
|
23
|
+
distRoot,
|
|
24
|
+
'modules/core/database/modules/tenant',
|
|
25
|
+
);
|
|
26
|
+
const tenantSourceFile = path.join(
|
|
27
|
+
root,
|
|
28
|
+
'src/modules/core/database/modules/tenant/tenant-database.module.ts',
|
|
29
|
+
);
|
|
30
|
+
const tenantDistFile = path.join(tenantDistDir, 'tenant-database.module.js');
|
|
31
|
+
const tenantMapFile = `${tenantDistFile}.map`;
|
|
32
|
+
const callerFile = path.join(distRoot, 'caller.js');
|
|
33
|
+
|
|
34
|
+
fs.mkdirSync(tenantDistDir, { recursive: true });
|
|
35
|
+
fs.writeFileSync(
|
|
36
|
+
tenantDistFile,
|
|
37
|
+
[
|
|
38
|
+
"'use strict';",
|
|
39
|
+
'function connectTenantDatabase() {',
|
|
40
|
+
" return 'connected';",
|
|
41
|
+
'}',
|
|
42
|
+
'module.exports = { connectTenantDatabase };',
|
|
43
|
+
'//# sourceMappingURL=tenant-database.module.js.map',
|
|
44
|
+
'',
|
|
45
|
+
].join('\n'),
|
|
46
|
+
);
|
|
47
|
+
fs.writeFileSync(
|
|
48
|
+
tenantMapFile,
|
|
49
|
+
JSON.stringify({
|
|
50
|
+
version: 3,
|
|
51
|
+
file: path.basename(tenantDistFile),
|
|
52
|
+
sourceRoot: '',
|
|
53
|
+
sources: [tenantSourceFile],
|
|
54
|
+
names: [],
|
|
55
|
+
mappings: '',
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
fs.writeFileSync(
|
|
59
|
+
callerFile,
|
|
60
|
+
[
|
|
61
|
+
"'use strict';",
|
|
62
|
+
"const tenant = require('./modules/core/database/modules/tenant/tenant-database.module');",
|
|
63
|
+
'function invokeTenantDatabase() {',
|
|
64
|
+
' return tenant.connectTenantDatabase();',
|
|
65
|
+
'}',
|
|
66
|
+
'module.exports = { invokeTenantDatabase, tenant };',
|
|
67
|
+
'',
|
|
68
|
+
].join('\n'),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return { callerFile, tenantSourceFile };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function makeReqRes() {
|
|
75
|
+
const req = new EventEmitter();
|
|
76
|
+
req.method = 'GET';
|
|
77
|
+
req.url = '/source-exclude';
|
|
78
|
+
req.headers = {
|
|
79
|
+
'x-bug-session-id': 'S_trace_source_exclude',
|
|
80
|
+
'x-bug-action-id': 'A_trace_source_exclude',
|
|
81
|
+
'x-bug-request-start': String(Date.now()),
|
|
82
|
+
};
|
|
83
|
+
req.params = {};
|
|
84
|
+
req.query = {};
|
|
85
|
+
|
|
86
|
+
const res = new EventEmitter();
|
|
87
|
+
res.statusCode = 200;
|
|
88
|
+
res.getHeader = () => undefined;
|
|
89
|
+
res.setHeader = () => {};
|
|
90
|
+
res.json = function json(body) {
|
|
91
|
+
this.body = body;
|
|
92
|
+
this.emit('finish');
|
|
93
|
+
return body;
|
|
94
|
+
};
|
|
95
|
+
res.send = res.json;
|
|
96
|
+
res.write = () => true;
|
|
97
|
+
res.end = () => {
|
|
98
|
+
res.emit('finish');
|
|
99
|
+
return true;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return { req, res };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function traceEventsFromPosts(posts) {
|
|
106
|
+
const traces = [];
|
|
107
|
+
const append = (value) => {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
traces.push(...value);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (typeof value !== 'string') return;
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(value);
|
|
115
|
+
if (Array.isArray(parsed)) traces.push(...parsed);
|
|
116
|
+
} catch {}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
for (const post of posts) {
|
|
120
|
+
let payload;
|
|
121
|
+
try {
|
|
122
|
+
payload = JSON.parse(String(post.body || ''));
|
|
123
|
+
} catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const entry of Array.isArray(payload.entries) ? payload.entries : []) {
|
|
127
|
+
append(entry?.trace);
|
|
128
|
+
append(entry?.request?.trace);
|
|
129
|
+
}
|
|
130
|
+
for (const event of Array.isArray(payload.events) ? payload.events : []) {
|
|
131
|
+
append(event?.payload?.trace);
|
|
132
|
+
append(event?.payload?.request?.trace);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return traces;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function main() {
|
|
140
|
+
const fixtureRoot = fs.mkdtempSync(path.join(__dirname, '.tmp-source-exclude-'));
|
|
141
|
+
const posts = [];
|
|
142
|
+
const originalFetch = global.fetch;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const fixture = writeFixture(fixtureRoot);
|
|
146
|
+
const includeFixture = new RegExp(
|
|
147
|
+
`^${fixtureRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
global.fetch = async (url, options = {}) => {
|
|
151
|
+
posts.push({ url: String(url), body: options.body });
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
status: 200,
|
|
155
|
+
json: async () => ({ ok: true }),
|
|
156
|
+
text: async () => '{"ok":true}',
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
initReproTracing({
|
|
161
|
+
include: [includeFixture],
|
|
162
|
+
exclude: [excludeTenantModule],
|
|
163
|
+
logFunctionCalls: false,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const caller = require(fixture.callerFile);
|
|
167
|
+
assert.strictEqual(
|
|
168
|
+
caller.tenant.connectTenantDatabase.__repro_instrumented,
|
|
169
|
+
undefined,
|
|
170
|
+
'source-mapped excluded module must not be instrumented',
|
|
171
|
+
);
|
|
172
|
+
assert.strictEqual(
|
|
173
|
+
caller.invokeTenantDatabase.__repro_instrumented,
|
|
174
|
+
true,
|
|
175
|
+
'non-excluded caller must remain instrumented',
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const middleware = reproMiddleware({
|
|
179
|
+
appId: 'APP_trace_source_exclude',
|
|
180
|
+
tenantId: 'TENANT_trace_source_exclude',
|
|
181
|
+
appSecret: 'secret',
|
|
182
|
+
captureHeaders: false,
|
|
183
|
+
});
|
|
184
|
+
const { req, res } = makeReqRes();
|
|
185
|
+
await new Promise((resolve, reject) => {
|
|
186
|
+
middleware(req, res, () => {
|
|
187
|
+
try {
|
|
188
|
+
assert.strictEqual(caller.invokeTenantDatabase(), 'connected');
|
|
189
|
+
res.json({ ok: true });
|
|
190
|
+
resolve();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
reject(error);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
197
|
+
|
|
198
|
+
const traces = traceEventsFromPosts(posts);
|
|
199
|
+
assert(
|
|
200
|
+
traces.some((event) => event?.fn === 'invokeTenantDatabase'),
|
|
201
|
+
`expected caller trace, received ${JSON.stringify(traces)}`,
|
|
202
|
+
);
|
|
203
|
+
assert(
|
|
204
|
+
!traces.some(
|
|
205
|
+
(event) =>
|
|
206
|
+
event?.fn === 'connectTenantDatabase' ||
|
|
207
|
+
event?.sourceFile === fixture.tenantSourceFile,
|
|
208
|
+
),
|
|
209
|
+
`excluded source leaked into trace payload: ${JSON.stringify(traces)}`,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
console.log('source-mapped tracing exclusion OK');
|
|
213
|
+
} finally {
|
|
214
|
+
global.fetch = originalFetch;
|
|
215
|
+
fs.rmSync(fixtureRoot, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
main().catch((error) => {
|
|
220
|
+
console.error(error);
|
|
221
|
+
process.exitCode = 1;
|
|
222
|
+
});
|
package/tracer/cjs-hook.js
CHANGED
|
@@ -31,16 +31,19 @@ function tagExports(value, filename, seen = new WeakSet(), depth = 0, instrument
|
|
|
31
31
|
|
|
32
32
|
if (typeof value === 'function') {
|
|
33
33
|
try {
|
|
34
|
-
|
|
34
|
+
const existingSourceFile = value[SYM_SRC_FILE];
|
|
35
|
+
const belongsToCurrentModule =
|
|
36
|
+
!existingSourceFile || toPosix(existingSourceFile) === toPosix(filename);
|
|
37
|
+
if (!existingSourceFile) {
|
|
35
38
|
Object.defineProperty(value, SYM_SRC_FILE, { value: filename, configurable: true });
|
|
36
39
|
}
|
|
37
|
-
if (value[SYM_IS_APP] !== isApp) {
|
|
40
|
+
if (belongsToCurrentModule && value[SYM_IS_APP] !== isApp) {
|
|
38
41
|
Object.defineProperty(value, SYM_IS_APP, { value: isApp, configurable: true });
|
|
39
42
|
}
|
|
40
|
-
if (instrumented && value.__repro_instrumented !== true) {
|
|
43
|
+
if (instrumented && belongsToCurrentModule && value.__repro_instrumented !== true) {
|
|
41
44
|
Object.defineProperty(value, '__repro_instrumented', { value: true, configurable: true });
|
|
42
45
|
}
|
|
43
|
-
if (instrumented && value[SYM_BODY_TRACED] !== true) {
|
|
46
|
+
if (instrumented && belongsToCurrentModule && value[SYM_BODY_TRACED] !== true) {
|
|
44
47
|
Object.defineProperty(value, SYM_BODY_TRACED, { value: true, configurable: true });
|
|
45
48
|
}
|
|
46
49
|
} catch {}
|
|
@@ -82,10 +85,24 @@ function installCJS({ include, exclude, parserPlugins } = {}) {
|
|
|
82
85
|
: [ new RegExp('^' + escapeRx(CWD + '/')) ];
|
|
83
86
|
const exc = Array.isArray(exclude) ? exclude : [];
|
|
84
87
|
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
const matchesAny = (patterns, file) => {
|
|
89
|
+
const normalized = toPosix(file);
|
|
90
|
+
return patterns.some((rx) => {
|
|
91
|
+
try {
|
|
92
|
+
rx.lastIndex = 0;
|
|
93
|
+
const matched = rx.test(normalized);
|
|
94
|
+
rx.lastIndex = 0;
|
|
95
|
+
return matched;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const shouldHandle = (physicalFile, sourceFile) => {
|
|
103
|
+
const candidates = [physicalFile, sourceFile].filter(Boolean);
|
|
104
|
+
if (candidates.some(file => matchesAny(exc, file))) return false;
|
|
105
|
+
return candidates.some(file => matchesAny(inc, file));
|
|
89
106
|
};
|
|
90
107
|
|
|
91
108
|
// ---- Global hook: intercept *all* compiles, regardless of how .ts was compiled ----
|
|
@@ -100,8 +117,7 @@ function installCJS({ include, exclude, parserPlugins } = {}) {
|
|
|
100
117
|
if (transformedFiles.has(filename)) {
|
|
101
118
|
// Already transformed once; avoid double-transforming code that may already include __repro_call wrappers.
|
|
102
119
|
out = code;
|
|
103
|
-
} else if (
|
|
104
|
-
try { process.stderr.write(`[trace-debug] compile: ${filename}\n`); } catch {}
|
|
120
|
+
} else if (isAppFile(filename)) {
|
|
105
121
|
const sourceInfo = getSourceInfo(code, filename);
|
|
106
122
|
if (sourceInfo?.metaFilename) {
|
|
107
123
|
metaFilename = sourceInfo.metaFilename;
|
|
@@ -109,35 +125,38 @@ function installCJS({ include, exclude, parserPlugins } = {}) {
|
|
|
109
125
|
if (typeof sourceInfo?.mapOriginalPosition === 'function') {
|
|
110
126
|
mapOriginalPosition = sourceInfo.mapOriginalPosition;
|
|
111
127
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
sourceMaps: 'inline',
|
|
118
|
-
parserOpts: {
|
|
128
|
+
if (shouldHandle(filename, metaFilename)) {
|
|
129
|
+
try { process.stderr.write(`[trace-debug] compile: ${filename}\n`); } catch {}
|
|
130
|
+
// Transform the already-compiled JS (keeps Nest’s decorator metadata intact).
|
|
131
|
+
const res = babel.transformSync(code, {
|
|
132
|
+
filename,
|
|
119
133
|
sourceType: 'unambiguous',
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
retainLines: true,
|
|
135
|
+
sourceMaps: 'inline',
|
|
136
|
+
parserOpts: {
|
|
137
|
+
sourceType: 'unambiguous',
|
|
138
|
+
// We’re parsing JS here; TS was compiled by ts-node/Nest.
|
|
139
|
+
plugins: parserPlugins || [
|
|
140
|
+
'jsx', 'classProperties', 'classPrivateProperties',
|
|
141
|
+
'classPrivateMethods', 'dynamicImport', 'topLevelAwait',
|
|
142
|
+
'optionalChaining', 'nullishCoalescingOperator',
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
// only the wrap plugin; do NOT run TS transform here
|
|
146
|
+
plugins: [
|
|
147
|
+
[ makeWrap(metaFilename, {
|
|
148
|
+
mode: 'all',
|
|
149
|
+
wrapGettersSetters: false,
|
|
150
|
+
skipAnonymous: false,
|
|
151
|
+
mapOriginalPosition,
|
|
152
|
+
}) ],
|
|
125
153
|
],
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
skipAnonymous: false,
|
|
133
|
-
mapOriginalPosition,
|
|
134
|
-
}) ],
|
|
135
|
-
],
|
|
136
|
-
compact: false,
|
|
137
|
-
comments: true,
|
|
138
|
-
});
|
|
139
|
-
out = res?.code || code;
|
|
140
|
-
wasInstrumented = !!res?.code;
|
|
154
|
+
compact: false,
|
|
155
|
+
comments: true,
|
|
156
|
+
});
|
|
157
|
+
out = res?.code || code;
|
|
158
|
+
wasInstrumented = !!res?.code;
|
|
159
|
+
}
|
|
141
160
|
transformedFiles.add(filename);
|
|
142
161
|
}
|
|
143
162
|
} catch (err) {
|