@reclaimprotocol/attestor-core 4.0.2 → 4.0.3
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/lib/utils/atomic-operations.d.ts +24 -0
- package/lib/utils/atomic-operations.js +65 -0
- package/lib/utils/connection-state-machine.d.ts +43 -0
- package/lib/utils/connection-state-machine.js +129 -0
- package/lib/utils/resource-monitor.d.ts +61 -0
- package/lib/utils/resource-monitor.js +107 -0
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic operations utility for preventing race conditions.
|
|
3
|
+
* Provides mutex-like locks using Promise-based mutual exclusion.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AtomicOperations {
|
|
6
|
+
private static locks;
|
|
7
|
+
/**
|
|
8
|
+
* Execute a function with a named lock to prevent concurrent execution.
|
|
9
|
+
* @param lockName Unique identifier for the lock
|
|
10
|
+
* @param fn Function to execute atomically
|
|
11
|
+
* @returns Promise that resolves with the function's return value
|
|
12
|
+
*/
|
|
13
|
+
static withLock<T>(lockName: string, fn: () => Promise<T>): Promise<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a lock is currently held.
|
|
16
|
+
* @param lockName Name of the lock to check
|
|
17
|
+
* @returns True if the lock is currently held
|
|
18
|
+
*/
|
|
19
|
+
static isLocked(lockName: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Clear all locks (for testing/cleanup).
|
|
22
|
+
*/
|
|
23
|
+
static clearAllLocks(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Atomic operations utility for preventing race conditions.
|
|
4
|
+
* Provides mutex-like locks using Promise-based mutual exclusion.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AtomicOperations = void 0;
|
|
8
|
+
class AtomicOperations {
|
|
9
|
+
/**
|
|
10
|
+
* Execute a function with a named lock to prevent concurrent execution.
|
|
11
|
+
* @param lockName Unique identifier for the lock
|
|
12
|
+
* @param fn Function to execute atomically
|
|
13
|
+
* @returns Promise that resolves with the function's return value
|
|
14
|
+
*/
|
|
15
|
+
static async withLock(lockName, fn) {
|
|
16
|
+
// Wait for any existing lock to complete
|
|
17
|
+
const existingLock = this.locks.get(lockName);
|
|
18
|
+
if (existingLock) {
|
|
19
|
+
await existingLock.catch(() => {
|
|
20
|
+
// Ignore errors from previous lock holders
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
// Create a new lock
|
|
24
|
+
let resolve;
|
|
25
|
+
let reject;
|
|
26
|
+
const lockPromise = new Promise((res, rej) => {
|
|
27
|
+
resolve = res;
|
|
28
|
+
reject = rej;
|
|
29
|
+
});
|
|
30
|
+
this.locks.set(lockName, lockPromise);
|
|
31
|
+
try {
|
|
32
|
+
// Execute the function
|
|
33
|
+
const result = await fn();
|
|
34
|
+
resolve();
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
reject(error);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
// Clean up the lock if we're still the current holder
|
|
43
|
+
if (this.locks.get(lockName) === lockPromise) {
|
|
44
|
+
this.locks.delete(lockName);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a lock is currently held.
|
|
50
|
+
* @param lockName Name of the lock to check
|
|
51
|
+
* @returns True if the lock is currently held
|
|
52
|
+
*/
|
|
53
|
+
static isLocked(lockName) {
|
|
54
|
+
return this.locks.has(lockName);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Clear all locks (for testing/cleanup).
|
|
58
|
+
*/
|
|
59
|
+
static clearAllLocks() {
|
|
60
|
+
this.locks.clear();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.AtomicOperations = AtomicOperations;
|
|
64
|
+
AtomicOperations.locks = new Map();
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRvbWljLW9wZXJhdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvYXRvbWljLW9wZXJhdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7R0FHRzs7O0FBRUgsTUFBYSxnQkFBZ0I7SUFHNUI7Ozs7O09BS0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBSSxRQUFnQixFQUFFLEVBQW9CO1FBQzlELHlDQUF5QztRQUN6QyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQTtRQUM3QyxJQUFJLFlBQVksRUFBRSxDQUFDO1lBQ2xCLE1BQU0sWUFBWSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7Z0JBQzdCLDJDQUEyQztZQUM1QyxDQUFDLENBQUMsQ0FBQTtRQUNILENBQUM7UUFFRCxvQkFBb0I7UUFDcEIsSUFBSSxPQUFtQixDQUFBO1FBQ3ZCLElBQUksTUFBNEIsQ0FBQTtRQUNoQyxNQUFNLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRTtZQUNsRCxPQUFPLEdBQUcsR0FBRyxDQUFBO1lBQ2IsTUFBTSxHQUFHLEdBQUcsQ0FBQTtRQUNiLENBQUMsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLFdBQVcsQ0FBQyxDQUFBO1FBRXJDLElBQUksQ0FBQztZQUNKLHVCQUF1QjtZQUN2QixNQUFNLE1BQU0sR0FBRyxNQUFNLEVBQUUsRUFBRSxDQUFBO1lBQ3pCLE9BQVEsRUFBRSxDQUFBO1lBQ1YsT0FBTyxNQUFNLENBQUE7UUFDZCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNoQixNQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7WUFDZCxNQUFNLEtBQUssQ0FBQTtRQUNaLENBQUM7Z0JBQVMsQ0FBQztZQUNWLHNEQUFzRDtZQUN0RCxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxLQUFLLFdBQVcsRUFBRSxDQUFDO2dCQUM5QyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQTtZQUM1QixDQUFDO1FBQ0YsQ0FBQztJQUNGLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFnQjtRQUMvQixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQ2hDLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxhQUFhO1FBQ25CLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUE7SUFDbkIsQ0FBQzs7QUExREYsNENBMkRDO0FBMURlLHNCQUFLLEdBQUcsSUFBSSxHQUFHLEVBQXlCLENBQUEifQ==
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection state machine for managing atomic state transitions.
|
|
3
|
+
* Prevents race conditions in connection state management.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum ConnectionState {
|
|
6
|
+
CONNECTING = "connecting",
|
|
7
|
+
CONNECTED = "connected",
|
|
8
|
+
CLOSING = "closing",
|
|
9
|
+
CLOSED = "closed"
|
|
10
|
+
}
|
|
11
|
+
type StateChangeListener = (oldState: ConnectionState, newState: ConnectionState) => void;
|
|
12
|
+
export declare class ConnectionStateMachine {
|
|
13
|
+
private currentState;
|
|
14
|
+
private listeners;
|
|
15
|
+
private transitionLock;
|
|
16
|
+
constructor(initialState?: ConnectionState);
|
|
17
|
+
/**
|
|
18
|
+
* Get current state.
|
|
19
|
+
*/
|
|
20
|
+
getState(): ConnectionState;
|
|
21
|
+
/**
|
|
22
|
+
* Check if the connection is in any of the specified states.
|
|
23
|
+
*/
|
|
24
|
+
isInState(...states: ConnectionState[]): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Register a state change listener.
|
|
27
|
+
*/
|
|
28
|
+
onStateChange(listener: StateChangeListener): () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Attempt to transition from allowed states to a new state.
|
|
31
|
+
* @param fromStates Allowed states to transition from
|
|
32
|
+
* @param toState Target state
|
|
33
|
+
* @param action Optional action to execute during transition
|
|
34
|
+
* @returns Promise that resolves to true if transition succeeded
|
|
35
|
+
*/
|
|
36
|
+
transition(fromStates: ConnectionState | ConnectionState[], toState: ConnectionState, action?: () => Promise<void>): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Force a state change without checking allowed transitions.
|
|
39
|
+
* Use with caution - mainly for error recovery.
|
|
40
|
+
*/
|
|
41
|
+
forceState(newState: ConnectionState): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Connection state machine for managing atomic state transitions.
|
|
4
|
+
* Prevents race conditions in connection state management.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ConnectionStateMachine = exports.ConnectionState = void 0;
|
|
8
|
+
var ConnectionState;
|
|
9
|
+
(function (ConnectionState) {
|
|
10
|
+
ConnectionState["CONNECTING"] = "connecting";
|
|
11
|
+
ConnectionState["CONNECTED"] = "connected";
|
|
12
|
+
ConnectionState["CLOSING"] = "closing";
|
|
13
|
+
ConnectionState["CLOSED"] = "closed";
|
|
14
|
+
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
|
|
15
|
+
class ConnectionStateMachine {
|
|
16
|
+
constructor(initialState = ConnectionState.CONNECTING) {
|
|
17
|
+
this.listeners = [];
|
|
18
|
+
this.transitionLock = null;
|
|
19
|
+
this.currentState = initialState;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get current state.
|
|
23
|
+
*/
|
|
24
|
+
getState() {
|
|
25
|
+
return this.currentState;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if the connection is in any of the specified states.
|
|
29
|
+
*/
|
|
30
|
+
isInState(...states) {
|
|
31
|
+
return states.includes(this.currentState);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Register a state change listener.
|
|
35
|
+
*/
|
|
36
|
+
onStateChange(listener) {
|
|
37
|
+
this.listeners.push(listener);
|
|
38
|
+
return () => {
|
|
39
|
+
const index = this.listeners.indexOf(listener);
|
|
40
|
+
if (index >= 0) {
|
|
41
|
+
this.listeners.splice(index, 1);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Attempt to transition from allowed states to a new state.
|
|
47
|
+
* @param fromStates Allowed states to transition from
|
|
48
|
+
* @param toState Target state
|
|
49
|
+
* @param action Optional action to execute during transition
|
|
50
|
+
* @returns Promise that resolves to true if transition succeeded
|
|
51
|
+
*/
|
|
52
|
+
async transition(fromStates, toState, action) {
|
|
53
|
+
// Wait for any existing transition to complete
|
|
54
|
+
if (this.transitionLock) {
|
|
55
|
+
await this.transitionLock.catch(() => {
|
|
56
|
+
// Ignore errors from previous transitions
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Create a new transition lock
|
|
60
|
+
let resolve;
|
|
61
|
+
let reject;
|
|
62
|
+
this.transitionLock = new Promise((res, rej) => {
|
|
63
|
+
resolve = res;
|
|
64
|
+
reject = rej;
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
const allowedStates = Array.isArray(fromStates) ? fromStates : [fromStates];
|
|
68
|
+
// Check if transition is allowed
|
|
69
|
+
if (!allowedStates.includes(this.currentState)) {
|
|
70
|
+
resolve();
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// If already in target state, no need to transition
|
|
74
|
+
if (this.currentState === toState) {
|
|
75
|
+
resolve();
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
const oldState = this.currentState;
|
|
79
|
+
// Execute action if provided
|
|
80
|
+
if (action) {
|
|
81
|
+
await action();
|
|
82
|
+
}
|
|
83
|
+
// Update state
|
|
84
|
+
this.currentState = toState;
|
|
85
|
+
// Notify listeners
|
|
86
|
+
for (const listener of this.listeners) {
|
|
87
|
+
try {
|
|
88
|
+
listener(oldState, toState);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// Don't let listener errors break the state machine
|
|
92
|
+
console.error('Error in state change listener:', error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
resolve();
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
reject(error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
// Clear the transition lock
|
|
104
|
+
this.transitionLock = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Force a state change without checking allowed transitions.
|
|
109
|
+
* Use with caution - mainly for error recovery.
|
|
110
|
+
*/
|
|
111
|
+
async forceState(newState) {
|
|
112
|
+
if (this.transitionLock) {
|
|
113
|
+
await this.transitionLock.catch(() => { });
|
|
114
|
+
}
|
|
115
|
+
const oldState = this.currentState;
|
|
116
|
+
this.currentState = newState;
|
|
117
|
+
// Notify listeners
|
|
118
|
+
for (const listener of this.listeners) {
|
|
119
|
+
try {
|
|
120
|
+
listener(oldState, newState);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('Error in state change listener:', error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.ConnectionStateMachine = ConnectionStateMachine;
|
|
129
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi1zdGF0ZS1tYWNoaW5lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2Nvbm5lY3Rpb24tc3RhdGUtbWFjaGluZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztHQUdHOzs7QUFFSCxJQUFZLGVBS1g7QUFMRCxXQUFZLGVBQWU7SUFDMUIsNENBQXlCLENBQUE7SUFDekIsMENBQXVCLENBQUE7SUFDdkIsc0NBQW1CLENBQUE7SUFDbkIsb0NBQWlCLENBQUE7QUFDbEIsQ0FBQyxFQUxXLGVBQWUsK0JBQWYsZUFBZSxRQUsxQjtBQUlELE1BQWEsc0JBQXNCO0lBS2xDLFlBQVksZUFBZ0MsZUFBZSxDQUFDLFVBQVU7UUFIOUQsY0FBUyxHQUEwQixFQUFFLENBQUE7UUFDckMsbUJBQWMsR0FBeUIsSUFBSSxDQUFBO1FBR2xELElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFBO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNILFFBQVE7UUFDUCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUE7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLEdBQUcsTUFBeUI7UUFDckMsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUMxQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxhQUFhLENBQUMsUUFBNkI7UUFDMUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUE7UUFDN0IsT0FBTyxHQUFHLEVBQUU7WUFDWCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtZQUM5QyxJQUFJLEtBQUssSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDaEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFBO1lBQ2hDLENBQUM7UUFD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
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource monitor for tracking resources and preventing leaks.
|
|
3
|
+
* Provides debugging capabilities and resource management.
|
|
4
|
+
*/
|
|
5
|
+
import type { Logger } from '../types';
|
|
6
|
+
export interface ResourceInfo {
|
|
7
|
+
id: string;
|
|
8
|
+
type: string;
|
|
9
|
+
createdAt: Date;
|
|
10
|
+
metadata?: any;
|
|
11
|
+
}
|
|
12
|
+
export declare class ResourceMonitor {
|
|
13
|
+
private static instance;
|
|
14
|
+
private resources;
|
|
15
|
+
private logger;
|
|
16
|
+
private constructor();
|
|
17
|
+
/**
|
|
18
|
+
* Get or create the singleton ResourceMonitor instance.
|
|
19
|
+
*/
|
|
20
|
+
static getInstance(logger: Logger): ResourceMonitor;
|
|
21
|
+
/**
|
|
22
|
+
* Track a new resource.
|
|
23
|
+
*/
|
|
24
|
+
trackResource(id: string, type: string, metadata?: any): void;
|
|
25
|
+
/**
|
|
26
|
+
* Untrack a resource.
|
|
27
|
+
*/
|
|
28
|
+
untrackResource(id: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a resource is tracked.
|
|
31
|
+
*/
|
|
32
|
+
isTracked(id: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get information about a specific resource.
|
|
35
|
+
*/
|
|
36
|
+
getResource(id: string): ResourceInfo | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Get all tracked resources.
|
|
39
|
+
*/
|
|
40
|
+
getAllResources(): ResourceInfo[];
|
|
41
|
+
/**
|
|
42
|
+
* Get resources by type.
|
|
43
|
+
*/
|
|
44
|
+
getResourcesByType(type: string): ResourceInfo[];
|
|
45
|
+
/**
|
|
46
|
+
* Get resources older than a specific age.
|
|
47
|
+
*/
|
|
48
|
+
getResourcesOlderThan(ageMs: number): ResourceInfo[];
|
|
49
|
+
/**
|
|
50
|
+
* Log resource statistics.
|
|
51
|
+
*/
|
|
52
|
+
logStats(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Clear all tracked resources (for testing/cleanup).
|
|
55
|
+
*/
|
|
56
|
+
clear(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Reset the singleton instance (for testing).
|
|
59
|
+
*/
|
|
60
|
+
static reset(): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resource monitor for tracking resources and preventing leaks.
|
|
4
|
+
* Provides debugging capabilities and resource management.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ResourceMonitor = void 0;
|
|
8
|
+
class ResourceMonitor {
|
|
9
|
+
constructor(logger) {
|
|
10
|
+
this.resources = new Map();
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get or create the singleton ResourceMonitor instance.
|
|
15
|
+
*/
|
|
16
|
+
static getInstance(logger) {
|
|
17
|
+
if (!ResourceMonitor.instance) {
|
|
18
|
+
ResourceMonitor.instance = new ResourceMonitor(logger);
|
|
19
|
+
}
|
|
20
|
+
return ResourceMonitor.instance;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Track a new resource.
|
|
24
|
+
*/
|
|
25
|
+
trackResource(id, type, metadata) {
|
|
26
|
+
const resource = {
|
|
27
|
+
id,
|
|
28
|
+
type,
|
|
29
|
+
createdAt: new Date(),
|
|
30
|
+
metadata
|
|
31
|
+
};
|
|
32
|
+
this.resources.set(id, resource);
|
|
33
|
+
this.logger.trace({ id, type, metadata }, 'Resource tracked');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Untrack a resource.
|
|
37
|
+
*/
|
|
38
|
+
untrackResource(id) {
|
|
39
|
+
const resource = this.resources.get(id);
|
|
40
|
+
if (resource) {
|
|
41
|
+
this.resources.delete(id);
|
|
42
|
+
this.logger.trace({ id, type: resource.type }, 'Resource untracked');
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a resource is tracked.
|
|
49
|
+
*/
|
|
50
|
+
isTracked(id) {
|
|
51
|
+
return this.resources.has(id);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get information about a specific resource.
|
|
55
|
+
*/
|
|
56
|
+
getResource(id) {
|
|
57
|
+
return this.resources.get(id);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get all tracked resources.
|
|
61
|
+
*/
|
|
62
|
+
getAllResources() {
|
|
63
|
+
return Array.from(this.resources.values());
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get resources by type.
|
|
67
|
+
*/
|
|
68
|
+
getResourcesByType(type) {
|
|
69
|
+
return Array.from(this.resources.values()).filter(r => r.type === type);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get resources older than a specific age.
|
|
73
|
+
*/
|
|
74
|
+
getResourcesOlderThan(ageMs) {
|
|
75
|
+
const cutoff = new Date(Date.now() - ageMs);
|
|
76
|
+
return Array.from(this.resources.values()).filter(r => r.createdAt < cutoff);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Log resource statistics.
|
|
80
|
+
*/
|
|
81
|
+
logStats() {
|
|
82
|
+
const byType = new Map();
|
|
83
|
+
for (const resource of this.resources.values()) {
|
|
84
|
+
byType.set(resource.type, (byType.get(resource.type) || 0) + 1);
|
|
85
|
+
}
|
|
86
|
+
const stats = Object.fromEntries(byType);
|
|
87
|
+
this.logger.info({
|
|
88
|
+
totalResources: this.resources.size,
|
|
89
|
+
byType: stats
|
|
90
|
+
}, 'Resource statistics');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Clear all tracked resources (for testing/cleanup).
|
|
94
|
+
*/
|
|
95
|
+
clear() {
|
|
96
|
+
this.resources.clear();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Reset the singleton instance (for testing).
|
|
100
|
+
*/
|
|
101
|
+
static reset() {
|
|
102
|
+
ResourceMonitor.instance = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.ResourceMonitor = ResourceMonitor;
|
|
106
|
+
ResourceMonitor.instance = null;
|
|
107
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb3VyY2UtbW9uaXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9yZXNvdXJjZS1tb25pdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O0dBR0c7OztBQVdILE1BQWEsZUFBZTtJQUszQixZQUFvQixNQUFjO1FBSDFCLGNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQTtRQUlsRCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtJQUNyQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQWM7UUFDaEMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUMvQixlQUFlLENBQUMsUUFBUSxHQUFHLElBQUksZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQ3ZELENBQUM7UUFDRCxPQUFPLGVBQWUsQ0FBQyxRQUFRLENBQUE7SUFDaEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsYUFBYSxDQUFDLEVBQVUsRUFBRSxJQUFZLEVBQUUsUUFBYztRQUNyRCxNQUFNLFFBQVEsR0FBaUI7WUFDOUIsRUFBRTtZQUNGLElBQUk7WUFDSixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDckIsUUFBUTtTQUNSLENBQUE7UUFFRCxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDaEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLGtCQUFrQixDQUFDLENBQUE7SUFDOUQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsZUFBZSxDQUFDLEVBQVU7UUFDekIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDdkMsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxT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
|