@reproapp/node-sdk 0.0.17 → 0.0.18
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reproapp/node-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Repro Nest SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,7 +12,8 @@
|
|
|
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-manifest-priority.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/ingest-queue-retention.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/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js"
|
|
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-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/ingest-queue-retention.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/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js",
|
|
16
|
+
"test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js"
|
|
16
17
|
},
|
|
17
18
|
"peerDependencies": {
|
|
18
19
|
"express": "^5.1.0",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
|
|
3
|
+
const { trace, SYM_BODY_TRACED } = require('../tracer/runtime');
|
|
4
|
+
|
|
5
|
+
function assertSyncThrowsOnceWithNoActiveTrace() {
|
|
6
|
+
let calls = 0;
|
|
7
|
+
function throwsOnce() {
|
|
8
|
+
calls += 1;
|
|
9
|
+
throw new Error('app failure');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
assert.throws(
|
|
13
|
+
() => global.__repro_call(throwsOnce, null, [], __filename, 10, 'throwsOnce', false),
|
|
14
|
+
/app failure/,
|
|
15
|
+
);
|
|
16
|
+
assert.strictEqual(calls, 1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function assertSyncThrowsOnceWithActiveTrace() {
|
|
20
|
+
let calls = 0;
|
|
21
|
+
function throwsOnce() {
|
|
22
|
+
calls += 1;
|
|
23
|
+
throw new Error('traced app failure');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
assert.throws(
|
|
27
|
+
() => trace.withTrace('runtime-no-double-invoke', () => (
|
|
28
|
+
global.__repro_call(throwsOnce, null, [], __filename, 25, 'throwsOnce', false)
|
|
29
|
+
)),
|
|
30
|
+
/traced app failure/,
|
|
31
|
+
);
|
|
32
|
+
assert.strictEqual(calls, 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertBodyTracedThrowsOnceWithActiveTrace() {
|
|
36
|
+
let calls = 0;
|
|
37
|
+
function bodyTracedThrowsOnce() {
|
|
38
|
+
calls += 1;
|
|
39
|
+
throw new Error('body traced app failure');
|
|
40
|
+
}
|
|
41
|
+
Object.defineProperty(bodyTracedThrowsOnce, SYM_BODY_TRACED, {
|
|
42
|
+
value: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
assert.throws(
|
|
47
|
+
() => trace.withTrace('runtime-no-double-invoke-body', () => (
|
|
48
|
+
global.__repro_call(bodyTracedThrowsOnce, null, [], __filename, 45, 'bodyTracedThrowsOnce', false)
|
|
49
|
+
)),
|
|
50
|
+
/body traced app failure/,
|
|
51
|
+
);
|
|
52
|
+
assert.strictEqual(calls, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
assertSyncThrowsOnceWithNoActiveTrace();
|
|
57
|
+
assertSyncThrowsOnceWithActiveTrace();
|
|
58
|
+
assertBodyTracedThrowsOnceWithActiveTrace();
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.log('runtime no double invoke OK');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main().catch((error) => {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.error(error);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const mongoose = require('mongoose');
|
|
3
|
+
const { GenericContainer, Wait } = require('testcontainers');
|
|
4
|
+
|
|
5
|
+
const { trace } = require('../tracer/runtime');
|
|
6
|
+
|
|
7
|
+
async function startMongo() {
|
|
8
|
+
return new GenericContainer('mongo:7')
|
|
9
|
+
.withExposedPorts(27017)
|
|
10
|
+
.withTmpFs({ '/data/db': 'rw,noexec,nosuid,size=256m' })
|
|
11
|
+
.withCommand(['mongod', '--bind_ip_all', '--setParameter', 'diagnosticDataCollectionEnabled=false'])
|
|
12
|
+
.withWaitStrategy(Wait.forListeningPorts())
|
|
13
|
+
.start();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
let container;
|
|
18
|
+
let connection;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
container = await startMongo();
|
|
22
|
+
const uri = `mongodb://${container.getHost()}:${container.getMappedPort(27017)}/repro_node_sdk_test`;
|
|
23
|
+
connection = await mongoose.createConnection(uri).asPromise();
|
|
24
|
+
|
|
25
|
+
const SideEffect = connection.model('SideEffect', new mongoose.Schema({
|
|
26
|
+
name: { type: String, required: true },
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
let calls = 0;
|
|
30
|
+
async function writeThenThrow(name) {
|
|
31
|
+
calls += 1;
|
|
32
|
+
await SideEffect.create({ name });
|
|
33
|
+
throw new Error('intentional app failure');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await assert.rejects(
|
|
37
|
+
() => trace.withTrace('runtime-no-double-invoke-container', () => (
|
|
38
|
+
global.__repro_call(
|
|
39
|
+
writeThenThrow,
|
|
40
|
+
null,
|
|
41
|
+
['single-write'],
|
|
42
|
+
__filename,
|
|
43
|
+
35,
|
|
44
|
+
'writeThenThrow',
|
|
45
|
+
false,
|
|
46
|
+
)
|
|
47
|
+
)),
|
|
48
|
+
/intentional app failure/,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
assert.strictEqual(calls, 1);
|
|
52
|
+
assert.strictEqual(await SideEffect.countDocuments({ name: 'single-write' }), 1);
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.log('runtime no double invoke testcontainers OK');
|
|
56
|
+
} finally {
|
|
57
|
+
if (connection) {
|
|
58
|
+
await connection.close().catch(() => {});
|
|
59
|
+
}
|
|
60
|
+
if (container) {
|
|
61
|
+
await container.stop().catch(() => {});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch((error) => {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.error(error);
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
});
|
package/tracer/runtime.js
CHANGED
|
@@ -505,9 +505,14 @@ function forkAlsStoreForUnawaited(baseStore) {
|
|
|
505
505
|
if (!global.__repro_call) {
|
|
506
506
|
Object.defineProperty(global, '__repro_call', {
|
|
507
507
|
value: function __repro_call(fn, thisArg, args, callFile, callLine, label, isUnawaitedCall) {
|
|
508
|
+
let didInvokeTarget = false;
|
|
509
|
+
const invokeTarget = (targetThis, targetArgs) => {
|
|
510
|
+
didInvokeTarget = true;
|
|
511
|
+
return fn.apply(targetThis, targetArgs);
|
|
512
|
+
};
|
|
508
513
|
try {
|
|
509
514
|
if (typeof fn !== 'function' || fn[SYM_SKIP_WRAP]) {
|
|
510
|
-
return
|
|
515
|
+
return invokeTarget(thisArg, args);
|
|
511
516
|
}
|
|
512
517
|
|
|
513
518
|
const currentStore = als.getStore();
|
|
@@ -516,13 +521,9 @@ if (!global.__repro_call) {
|
|
|
516
521
|
// If no tracing context is active, bail out quickly to avoid touching app semantics
|
|
517
522
|
// during module initialization or other untracked code paths.
|
|
518
523
|
if (!currentStore) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
return out;
|
|
523
|
-
} catch {
|
|
524
|
-
return fn ? fn.apply(thisArg, args) : undefined;
|
|
525
|
-
}
|
|
524
|
+
const out = invokeTarget(thisArg, args);
|
|
525
|
+
if (isUnawaitedCall && isThenable(out)) markPromiseUnawaited(out);
|
|
526
|
+
return out;
|
|
526
527
|
}
|
|
527
528
|
|
|
528
529
|
const sourceFile = fn[SYM_SRC_FILE];
|
|
@@ -643,7 +644,7 @@ if (!global.__repro_call) {
|
|
|
643
644
|
const shouldWrap = !isAsyncLocalContextSetter(fn, thisArg);
|
|
644
645
|
const argsForCall = shouldWrap ? wrapArgsWithStore(args, snapshotForArgs) : args;
|
|
645
646
|
try {
|
|
646
|
-
const out =
|
|
647
|
+
const out = invokeTarget(thisArg, argsForCall);
|
|
647
648
|
|
|
648
649
|
const isThenableValue = isThenable(out);
|
|
649
650
|
const isQuery = isMongooseQuery(out);
|
|
@@ -755,10 +756,10 @@ if (!global.__repro_call) {
|
|
|
755
756
|
let out;
|
|
756
757
|
if (isolatedStore) {
|
|
757
758
|
als.run(isolatedStore, () => {
|
|
758
|
-
out =
|
|
759
|
+
out = invokeTarget(thisArg, argsForCall);
|
|
759
760
|
});
|
|
760
761
|
} else {
|
|
761
|
-
out =
|
|
762
|
+
out = invokeTarget(thisArg, argsForCall);
|
|
762
763
|
}
|
|
763
764
|
if (isUnawaitedCall && isThenable(out)) markPromiseUnawaited(out);
|
|
764
765
|
return out;
|
|
@@ -775,7 +776,10 @@ if (!global.__repro_call) {
|
|
|
775
776
|
}
|
|
776
777
|
return invokeWithTrace(forcedParentSpanId, callStoreForArgs);
|
|
777
778
|
});
|
|
778
|
-
} catch {
|
|
779
|
+
} catch (error) {
|
|
780
|
+
if (didInvokeTarget) {
|
|
781
|
+
throw error;
|
|
782
|
+
}
|
|
779
783
|
return fn ? fn.apply(thisArg, args) : undefined;
|
|
780
784
|
}
|
|
781
785
|
},
|