@senzops/apm-node 1.2.7 → 1.2.8
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.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/register.js +1 -1
- package/dist/register.js.map +1 -1
- package/dist/register.mjs +1 -1
- package/dist/register.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/context.ts +71 -9
package/package.json
CHANGED
package/src/core/context.ts
CHANGED
|
@@ -5,17 +5,41 @@ interface IStorage<T> {
|
|
|
5
5
|
getStore(): T | undefined;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Async-safe fallback when AsyncLocalStorage is unavailable.
|
|
10
|
+
*
|
|
11
|
+
* Not concurrency-safe across truly parallel requests in a single isolate,
|
|
12
|
+
* but correct for sequential and async/await patterns (e.g. Cloudflare Workers
|
|
13
|
+
* where each request gets its own execution context).
|
|
14
|
+
*/
|
|
8
15
|
class NaiveStorage<T> implements IStorage<T> {
|
|
9
16
|
private store: T | undefined;
|
|
10
17
|
|
|
11
18
|
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R {
|
|
12
19
|
const prev = this.store;
|
|
13
20
|
this.store = store;
|
|
21
|
+
|
|
22
|
+
let result: R;
|
|
14
23
|
try {
|
|
15
|
-
|
|
16
|
-
}
|
|
24
|
+
result = callback(...args);
|
|
25
|
+
} catch (err) {
|
|
17
26
|
this.store = prev;
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// If the callback returned a thenable (async handler), defer the restore
|
|
31
|
+
// until the promise settles so Context.current() works across awaits.
|
|
32
|
+
if (result != null && typeof (result as any).then === 'function') {
|
|
33
|
+
const promise = (result as any).then(
|
|
34
|
+
(val: any) => { this.store = prev; return val; },
|
|
35
|
+
(err: any) => { this.store = prev; throw err; }
|
|
36
|
+
);
|
|
37
|
+
return promise as R;
|
|
18
38
|
}
|
|
39
|
+
|
|
40
|
+
// Sync callback — restore immediately.
|
|
41
|
+
this.store = prev;
|
|
42
|
+
return result;
|
|
19
43
|
}
|
|
20
44
|
|
|
21
45
|
getStore(): T | undefined {
|
|
@@ -23,29 +47,67 @@ class NaiveStorage<T> implements IStorage<T> {
|
|
|
23
47
|
}
|
|
24
48
|
}
|
|
25
49
|
|
|
26
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the best available async context storage.
|
|
52
|
+
*
|
|
53
|
+
* Uses lazy re-resolution: if the initial attempt (at module evaluation time)
|
|
54
|
+
* falls back to NaiveStorage, subsequent calls to resolveStorage() will retry.
|
|
55
|
+
* This handles runtimes where AsyncLocalStorage becomes available after module
|
|
56
|
+
* init (e.g. some Workers configurations).
|
|
57
|
+
*/
|
|
58
|
+
const tryResolveALS = <T>(): IStorage<T> | null => {
|
|
59
|
+
// 1. Check globalThis (Cloudflare Workers nodejs_compat_v2, Bun, Deno)
|
|
27
60
|
if (typeof globalThis !== 'undefined' && (globalThis as any).AsyncLocalStorage) {
|
|
28
61
|
return new (globalThis as any).AsyncLocalStorage();
|
|
29
62
|
}
|
|
30
63
|
|
|
64
|
+
// 2. Node.js CJS require
|
|
31
65
|
try {
|
|
32
66
|
if (typeof require !== 'undefined') {
|
|
33
|
-
const
|
|
34
|
-
if (AsyncLocalStorage) return new AsyncLocalStorage();
|
|
67
|
+
const mod = require('node:async_hooks');
|
|
68
|
+
if (mod?.AsyncLocalStorage) return new mod.AsyncLocalStorage();
|
|
35
69
|
}
|
|
36
70
|
} catch {}
|
|
37
71
|
|
|
38
72
|
try {
|
|
39
73
|
if (typeof require !== 'undefined') {
|
|
40
|
-
const
|
|
41
|
-
if (AsyncLocalStorage) return new AsyncLocalStorage();
|
|
74
|
+
const mod = require('async_hooks');
|
|
75
|
+
if (mod?.AsyncLocalStorage) return new mod.AsyncLocalStorage();
|
|
42
76
|
}
|
|
43
77
|
} catch {}
|
|
44
78
|
|
|
45
|
-
return
|
|
79
|
+
return null;
|
|
46
80
|
};
|
|
47
81
|
|
|
48
|
-
|
|
82
|
+
class LazyStorage<T> implements IStorage<T> {
|
|
83
|
+
private inner: IStorage<T>;
|
|
84
|
+
private resolved = false;
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
this.inner = tryResolveALS<T>() || new NaiveStorage<T>();
|
|
88
|
+
this.resolved = !(this.inner instanceof NaiveStorage);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private ensureResolved() {
|
|
92
|
+
if (this.resolved) return;
|
|
93
|
+
const als = tryResolveALS<T>();
|
|
94
|
+
if (als) {
|
|
95
|
+
this.inner = als;
|
|
96
|
+
this.resolved = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R {
|
|
101
|
+
this.ensureResolved();
|
|
102
|
+
return this.inner.run(store, callback, ...args);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getStore(): T | undefined {
|
|
106
|
+
return this.inner.getStore();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const storage = new LazyStorage<ActiveTrace>();
|
|
49
111
|
|
|
50
112
|
export const Context = {
|
|
51
113
|
run: <T>(trace: ActiveTrace, fn: () => T): T => {
|