@olane/o-node 0.7.45 → 0.7.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.
@@ -13,6 +13,10 @@ export declare class oNodeConnection extends oConnection {
13
13
  constructor(config: oNodeConnectionConfig);
14
14
  get remotePeerId(): import("@olane/o-config").PeerId;
15
15
  get remoteAddr(): import("@olane/o-config").Multiaddr;
16
+ /**
17
+ * Get the connection configuration for compatibility checking.
18
+ */
19
+ get connectionConfig(): oNodeConnectionConfig;
16
20
  supportsAddress(address: oNodeAddress): boolean;
17
21
  getOrCreateStream(): Promise<oNodeConnectionStream>;
18
22
  createStream(): Promise<oNodeConnectionStream>;
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,qBAAa,eAAgB,SAAQ,WAAW;IAKlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAJrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAEV,MAAM,EAAE,qBAAqB;IAO5D,IAAI,YAAY,qCAEf;IAED,IAAI,UAAU,wCAEb;IAED,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAMzC,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAanD,YAAY,IAAI,OAAO,CAAC,qBAAqB,CAAC;IA6BpD,IAAI,OAAO,IAAI,qBAAqB,EAAE,CAcrC;IAEK,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA6C/C,YAAY,CAAC,MAAM,EAAE,qBAAqB;IAI1C,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
1
+ {"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,qBAAa,eAAgB,SAAQ,WAAW;IAKlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAJrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAEV,MAAM,EAAE,qBAAqB;IAQ5D,IAAI,YAAY,qCAEf;IAED,IAAI,UAAU,wCAEb;IAED;;OAEG;IACH,IAAI,gBAAgB,IAAI,qBAAqB,CAE5C;IAED,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAMzC,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAkBnD,YAAY,IAAI,OAAO,CAAC,qBAAqB,CAAC;IA6BpD,IAAI,OAAO,IAAI,qBAAqB,EAAE,CAcrC;IAEK,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA6C/C,YAAY,CAAC,MAAM,EAAE,qBAAqB;IAI1C,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
@@ -8,6 +8,7 @@ export class oNodeConnection extends oConnection {
8
8
  this.p2pConnection = config.p2pConnection;
9
9
  this.streamHandler = new StreamHandler(this.logger);
10
10
  this.reusePolicy = config.reusePolicy ?? 'none';
11
+ console.log('oNodeConnection constructor', this.reusePolicy);
11
12
  }
12
13
  get remotePeerId() {
13
14
  return this.p2pConnection.remotePeer;
@@ -15,16 +16,27 @@ export class oNodeConnection extends oConnection {
15
16
  get remoteAddr() {
16
17
  return this.p2pConnection.remoteAddr;
17
18
  }
19
+ /**
20
+ * Get the connection configuration for compatibility checking.
21
+ */
22
+ get connectionConfig() {
23
+ return this.config;
24
+ }
18
25
  supportsAddress(address) {
19
26
  return address.libp2pTransports.some((transport) => {
20
27
  return transport.toString() === this.remoteAddr.toString();
21
28
  });
22
29
  }
23
30
  async getOrCreateStream() {
24
- if (this.reusePolicy === 'reuse' && this.streams.length > 0) {
25
- this.logger.debug('Returning reuse stream: ', this.streams[0].p2pStream.protocol);
31
+ console.log('getOrCreateStream', this.reusePolicy);
32
+ if (this.reusePolicy === 'reuse') {
33
+ this.logger.debug('Reusing stream...');
26
34
  // search for streams that allow re-use
27
- return this.streams[0];
35
+ if (this.streams.length > 0) {
36
+ this.logger.debug('Returning reuse stream: ', this.streams[0].p2pStream.protocol);
37
+ // search for streams that allow re-use
38
+ return this.streams[0];
39
+ }
28
40
  }
29
41
  return this.createStream();
30
42
  }
@@ -9,6 +9,8 @@ export declare class oNodeConnectionManager extends oConnectionManager {
9
9
  private defaultDrainTimeoutMs?;
10
10
  private connectionsByAddress;
11
11
  private pendingDialsByAddress;
12
+ /** Cache of oNodeConnection instances keyed by p2p connection ID */
13
+ private nodeConnectionByConnectionId;
12
14
  constructor(config: oNodeConnectionManagerConfig);
13
15
  /**
14
16
  * Set up listeners to maintain connection cache state
@@ -36,6 +38,17 @@ export declare class oNodeConnectionManager extends oConnectionManager {
36
38
  * @returns The best connection or null if none are suitable
37
39
  */
38
40
  private selectBestConnection;
41
+ /**
42
+ * Get a cached oNodeConnection for the given p2p connection if it exists and is valid.
43
+ * @param p2pConnection - The p2p connection to look up
44
+ * @returns A valid oNodeConnection or null if none found
45
+ */
46
+ private getCachedNodeConnection;
47
+ /**
48
+ * Cache an oNodeConnection by its p2p connection ID for potential reuse.
49
+ * @param conn - The oNodeConnection to cache
50
+ */
51
+ private cacheNodeConnection;
39
52
  getOrCreateConnection(nextHopAddress: oAddress, address: oAddress): Promise<Connection>;
40
53
  private performDial;
41
54
  answer(config: oConnectionConfig & {
@@ -43,7 +56,7 @@ export declare class oNodeConnectionManager extends oConnectionManager {
43
56
  reuse?: boolean;
44
57
  }): Promise<oNodeConnection>;
45
58
  /**
46
- * Connect to a given address, reusing libp2p connections when possible
59
+ * Connect to a given address, reusing oNodeConnection and libp2p connections when possible
47
60
  * @param config - Connection configuration
48
61
  * @returns The connection object
49
62
  */
@@ -68,6 +81,7 @@ export declare class oNodeConnectionManager extends oConnectionManager {
68
81
  cachedAddresses: number;
69
82
  totalCachedConnections: number;
70
83
  pendingDials: number;
84
+ cachedNodeConnections: number;
71
85
  connectionsByPeer: Array<{
72
86
  peerId: string;
73
87
  status: string;
@@ -79,5 +93,10 @@ export declare class oNodeConnectionManager extends oConnectionManager {
79
93
  * @returns Number of connections removed
80
94
  */
81
95
  cleanupStaleConnections(): number;
96
+ /**
97
+ * Clean up all stale oNodeConnections from cache
98
+ * @returns Number of oNodeConnections removed
99
+ */
100
+ cleanupStaleNodeConnections(): number;
82
101
  }
83
102
  //# sourceMappingURL=o-node-connection.manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAQhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IAPzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,oBAAoB,CAAwC;IACpE,OAAO,CAAC,qBAAqB,CACjB;gBAES,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA6DtB,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;YA+ER,WAAW;IAwBnB,MAAM,CACV,MAAM,EAAE,iBAAiB,GAAG;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,eAAe,CAAC;IAwC3B;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IA8CpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;IA0D/D;;;OAGG;IACH,aAAa,IAAI;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,KAAK,CAAC;YACvB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC,CAAC;KACJ;IAyBD;;;OAGG;IACH,uBAAuB,IAAI,MAAM;CAuBlC"}
1
+ {"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAUhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IATzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,oBAAoB,CAAwC;IACpE,OAAO,CAAC,qBAAqB,CAA+C;IAC5E,oEAAoE;IACpE,OAAO,CAAC,4BAA4B,CACxB;gBAES,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAyChC;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA8D5B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAIrB,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;YAgFR,WAAW;IAwBnB,MAAM,CACV,MAAM,EAAE,iBAAiB,GAAG;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,eAAe,CAAC;IA2C3B;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6ClE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IAiDpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;IA6D/D;;;OAGG;IACH,aAAa,IAAI;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,iBAAiB,EAAE,KAAK,CAAC;YACvB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC,CAAC;KACJ;IA6BD;;;OAGG;IACH,uBAAuB,IAAI,MAAM;IA6BjC;;;OAGG;IACH,2BAA2B,IAAI,MAAM;CAgBtC"}
@@ -6,6 +6,8 @@ export class oNodeConnectionManager extends oConnectionManager {
6
6
  this.config = config;
7
7
  this.connectionsByAddress = new Map();
8
8
  this.pendingDialsByAddress = new Map();
9
+ /** Cache of oNodeConnection instances keyed by p2p connection ID */
10
+ this.nodeConnectionByConnectionId = new Map();
9
11
  this.p2pNode = config.p2pNode;
10
12
  this.defaultReadTimeoutMs = config.defaultReadTimeoutMs;
11
13
  this.defaultDrainTimeoutMs = config.defaultDrainTimeoutMs;
@@ -22,19 +24,31 @@ export class oNodeConnectionManager extends oConnectionManager {
22
24
  if (!connection) {
23
25
  return;
24
26
  }
25
- for (const [addressKey, cachedConnections,] of this.connectionsByAddress.entries()) {
26
- const index = cachedConnections.indexOf(connection);
27
- if (index !== -1) {
28
- this.logger.debug('Connection closed, removing from cache for address:', addressKey);
29
- cachedConnections.splice(index, 1);
30
- // Remove the address key entirely if no connections remain
31
- if (cachedConnections.length === 0) {
32
- this.connectionsByAddress.delete(addressKey);
33
- }
34
- else {
35
- this.connectionsByAddress.set(addressKey, cachedConnections);
36
- }
37
- }
27
+ // Clean up libp2p connection cache
28
+ // for (const [
29
+ // addressKey,
30
+ // cachedConnections,
31
+ // ] of this.connectionsByAddress.entries()) {
32
+ // const index = cachedConnections.indexOf(connection);
33
+ // if (index !== -1) {
34
+ // this.logger.debug(
35
+ // 'Connection closed, removing from cache for address:',
36
+ // addressKey,
37
+ // );
38
+ // cachedConnections.splice(index, 1);
39
+ // // Remove the address key entirely if no connections remain
40
+ // if (cachedConnections.length === 0) {
41
+ // this.connectionsByAddress.delete(addressKey);
42
+ // } else {
43
+ // this.connectionsByAddress.set(addressKey, cachedConnections);
44
+ // }
45
+ // }
46
+ // }
47
+ // Clean up oNodeConnection cache by connection ID
48
+ const connectionId = connection.id;
49
+ if (this.nodeConnectionByConnectionId.has(connectionId)) {
50
+ this.logger.debug('Connection closed, removing oNodeConnection for connection ID:', connectionId);
51
+ this.nodeConnectionByConnectionId.delete(connectionId);
38
52
  }
39
53
  });
40
54
  }
@@ -105,8 +119,7 @@ export class oNodeConnectionManager extends oConnectionManager {
105
119
  // Priority 2: Based on reuse context
106
120
  if (reuseContext) {
107
121
  // Prefer inbound connections
108
- const inbound = openConnections.find((c) => c.direction === 'inbound' ||
109
- !c.direction);
122
+ const inbound = openConnections.find((c) => c.direction === 'inbound' || !c.direction);
110
123
  if (inbound) {
111
124
  this.logger.debug('Selected inbound connection (reuse context)');
112
125
  return inbound;
@@ -121,9 +134,32 @@ export class oNodeConnectionManager extends oConnectionManager {
121
134
  }
122
135
  }
123
136
  // Priority 3: Return first open connection
124
- this.logger.debug('Selected first available open connection');
137
+ this.logger.debug('Selected first available open connection', connectionsWithStreams);
125
138
  return openConnections[0];
126
139
  }
140
+ /**
141
+ * Get a cached oNodeConnection for the given p2p connection if it exists and is valid.
142
+ * @param p2pConnection - The p2p connection to look up
143
+ * @returns A valid oNodeConnection or null if none found
144
+ */
145
+ getCachedNodeConnection(p2pConnection) {
146
+ const cached = this.nodeConnectionByConnectionId.get(p2pConnection.id);
147
+ if (cached && cached.p2pConnection?.status === 'open') {
148
+ return cached;
149
+ }
150
+ // Clean up stale entry if connection is no longer open
151
+ if (cached) {
152
+ this.nodeConnectionByConnectionId.delete(p2pConnection.id);
153
+ }
154
+ return null;
155
+ }
156
+ /**
157
+ * Cache an oNodeConnection by its p2p connection ID for potential reuse.
158
+ * @param conn - The oNodeConnection to cache
159
+ */
160
+ cacheNodeConnection(conn) {
161
+ this.nodeConnectionByConnectionId.set(conn.p2pConnection.id, conn);
162
+ }
127
163
  async getOrCreateConnection(nextHopAddress, address) {
128
164
  if (!nextHopAddress) {
129
165
  throw new Error('Invalid address passed');
@@ -137,7 +173,7 @@ export class oNodeConnectionManager extends oConnectionManager {
137
173
  const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
138
174
  const bestConnection = this.selectBestConnection(cachedConnections, false);
139
175
  if (bestConnection) {
140
- this.logger.debug('Reusing cached connection for address:', nextHopAddress?.value);
176
+ this.logger.debug('Reusing cached connection for address:', nextHopAddress?.value, bestConnection.id);
141
177
  return bestConnection;
142
178
  }
143
179
  // Clean up stale connections if they exist but are not open
@@ -210,6 +246,8 @@ export class oNodeConnectionManager extends oConnectionManager {
210
246
  requestHandler: config.requestHandler ?? undefined,
211
247
  reusePolicy: reuse ? 'reuse' : 'none',
212
248
  });
249
+ // Cache the new connection by its p2p connection ID
250
+ this.cacheNodeConnection(connection);
213
251
  const addressKey = this.getAddressKey(nextHopAddress);
214
252
  if (addressKey) {
215
253
  const connections = this.connectionsByAddress.get(addressKey) || [];
@@ -225,13 +263,21 @@ export class oNodeConnectionManager extends oConnectionManager {
225
263
  return connection;
226
264
  }
227
265
  /**
228
- * Connect to a given address, reusing libp2p connections when possible
266
+ * Connect to a given address, reusing oNodeConnection and libp2p connections when possible
229
267
  * @param config - Connection configuration
230
268
  * @returns The connection object
231
269
  */
232
270
  async connect(config) {
233
271
  const { address, nextHopAddress, callerAddress, readTimeoutMs, drainTimeoutMs, } = config;
272
+ // First get or create the underlying p2p connection
234
273
  const p2pConnection = await this.getOrCreateConnection(nextHopAddress, address);
274
+ // Check for existing valid oNodeConnection for this p2p connection
275
+ const existingConnection = this.getCachedNodeConnection(p2pConnection);
276
+ if (existingConnection) {
277
+ this.logger.debug('Reusing cached oNodeConnection for connection ID:', p2pConnection.id);
278
+ return existingConnection;
279
+ }
280
+ // No valid cached connection, create new one
235
281
  const connection = new oNodeConnection({
236
282
  nextHopAddress: nextHopAddress,
237
283
  address: address,
@@ -244,6 +290,8 @@ export class oNodeConnectionManager extends oConnectionManager {
244
290
  runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
245
291
  requestHandler: config.requestHandler ?? undefined,
246
292
  });
293
+ // Cache the new connection by its p2p connection ID
294
+ this.cacheNodeConnection(connection);
247
295
  return connection;
248
296
  }
249
297
  /**
@@ -342,7 +390,7 @@ export class oNodeConnectionManager extends oConnectionManager {
342
390
  */
343
391
  getCacheStats() {
344
392
  const allConnections = [];
345
- for (const [addressKey, connections] of this.connectionsByAddress.entries()) {
393
+ for (const [addressKey, connections,] of this.connectionsByAddress.entries()) {
346
394
  for (const conn of connections) {
347
395
  allConnections.push({
348
396
  peerId: conn.remotePeer?.toString() ?? 'unknown',
@@ -355,6 +403,7 @@ export class oNodeConnectionManager extends oConnectionManager {
355
403
  cachedAddresses: this.connectionsByAddress.size,
356
404
  totalCachedConnections: allConnections.length,
357
405
  pendingDials: this.pendingDialsByAddress.size,
406
+ cachedNodeConnections: this.nodeConnectionByConnectionId.size,
358
407
  connectionsByPeer: allConnections,
359
408
  };
360
409
  }
@@ -364,7 +413,7 @@ export class oNodeConnectionManager extends oConnectionManager {
364
413
  */
365
414
  cleanupStaleConnections() {
366
415
  let removed = 0;
367
- for (const [addressKey, connections] of this.connectionsByAddress.entries()) {
416
+ for (const [addressKey, connections,] of this.connectionsByAddress.entries()) {
368
417
  const openConnections = connections.filter((conn) => conn.status === 'open');
369
418
  const staleCount = connections.length - openConnections.length;
370
419
  if (staleCount > 0) {
@@ -384,4 +433,21 @@ export class oNodeConnectionManager extends oConnectionManager {
384
433
  }
385
434
  return removed;
386
435
  }
436
+ /**
437
+ * Clean up all stale oNodeConnections from cache
438
+ * @returns Number of oNodeConnections removed
439
+ */
440
+ cleanupStaleNodeConnections() {
441
+ let removed = 0;
442
+ for (const [connectionId, conn,] of this.nodeConnectionByConnectionId.entries()) {
443
+ if (conn.p2pConnection?.status !== 'open') {
444
+ this.nodeConnectionByConnectionId.delete(connectionId);
445
+ removed++;
446
+ }
447
+ }
448
+ if (removed > 0) {
449
+ this.logger.debug(`Cleaned up ${removed} stale oNodeConnections`);
450
+ }
451
+ return removed;
452
+ }
387
453
  }
@@ -0,0 +1,2 @@
1
+ export * from './synchronized.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './synchronized.js';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Method decorator that automatically acquires and releases a lock
3
+ * when the decorated method is called. Ensures only one invocation
4
+ * of the method executes at a time.
5
+ *
6
+ * @param lockName - Optional custom lock name. If not provided,
7
+ * defaults to "ClassName:methodName"
8
+ * @param timeoutMs - Optional timeout for lock acquisition in milliseconds
9
+ *
10
+ * @example
11
+ * class MyNode extends oNode {
12
+ * protected lockManager = new LockManager();
13
+ *
14
+ * @Synchronized('register:leader')
15
+ * async registerLeader(): Promise<void> {
16
+ * // This method is automatically synchronized
17
+ * }
18
+ * }
19
+ */
20
+ export declare function Synchronized(lockName?: string, timeoutMs?: number): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
21
+ //# sourceMappingURL=synchronized.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronized.d.ts","sourceRoot":"","sources":["../../../src/decorators/synchronized.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,YAEtD,GAAG,eACE,MAAM,cACP,kBAAkB,wBA8BjC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Method decorator that automatically acquires and releases a lock
3
+ * when the decorated method is called. Ensures only one invocation
4
+ * of the method executes at a time.
5
+ *
6
+ * @param lockName - Optional custom lock name. If not provided,
7
+ * defaults to "ClassName:methodName"
8
+ * @param timeoutMs - Optional timeout for lock acquisition in milliseconds
9
+ *
10
+ * @example
11
+ * class MyNode extends oNode {
12
+ * protected lockManager = new LockManager();
13
+ *
14
+ * @Synchronized('register:leader')
15
+ * async registerLeader(): Promise<void> {
16
+ * // This method is automatically synchronized
17
+ * }
18
+ * }
19
+ */
20
+ export function Synchronized(lockName, timeoutMs) {
21
+ return function (target, propertyKey, descriptor) {
22
+ const originalMethod = descriptor.value;
23
+ // Generate lock name if not provided
24
+ const finalLockName = lockName || `${target.constructor.name}:${propertyKey}`;
25
+ descriptor.value = async function (...args) {
26
+ // Ensure lockManager exists on the instance
27
+ const lockManager = this.lockManager;
28
+ if (!lockManager) {
29
+ throw new Error(`LockManager not initialized on instance. ` +
30
+ `Ensure 'protected lockManager = new LockManager()' is declared in ${target.constructor.name}`);
31
+ }
32
+ // Execute the method with automatic lock management
33
+ return lockManager.withLock(finalLockName, async () => {
34
+ return originalMethod.apply(this, args);
35
+ }, timeoutMs);
36
+ };
37
+ return descriptor;
38
+ };
39
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './o-node.js';
2
2
  export * from './utils/index.js';
3
+ export * from './decorators/index.js';
3
4
  export * from './o-node.hierarchy-manager.js';
4
5
  export * from './interfaces/o-node.config.js';
5
6
  export * from './connection/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oCAAoC,CAAC;AACnD,cAAc,mBAAmB,CAAC;AAClC,cAAc,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oCAAoC,CAAC;AACnD,cAAc,mBAAmB,CAAC;AAClC,cAAc,kCAAkC,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './o-node.js';
2
2
  export * from './utils/index.js';
3
+ export * from './decorators/index.js';
3
4
  export * from './o-node.hierarchy-manager.js';
4
5
  export * from './interfaces/o-node.config.js';
5
6
  export * from './connection/index.js';
@@ -11,6 +11,7 @@ import { oToolBase } from '@olane/o-tool';
11
11
  import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
12
12
  import { oNodeConnectionConfig } from './connection/index.js';
13
13
  import { oReconnectionManager } from './managers/o-reconnection.manager.js';
14
+ import { LockManager } from './utils/lock-manager.js';
14
15
  export declare class oNode extends oToolBase {
15
16
  peerId: PeerId;
16
17
  p2pNode: Libp2p;
@@ -23,6 +24,7 @@ export declare class oNode extends oToolBase {
23
24
  protected didRegister: boolean;
24
25
  protected hooksStartFinished: any[];
25
26
  protected hooksInitFinished: any[];
27
+ protected lockManager: LockManager;
26
28
  constructor(config: oNodeConfig);
27
29
  get leader(): oNodeAddress | null;
28
30
  get networkConfig(): Libp2pConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,12 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
1
10
  import { createNode, defaultLibp2pConfig, KEEP_ALIVE, } from '@olane/o-config';
2
11
  import { v4 as uuidv4 } from 'uuid';
3
12
  import { oNodeRouter } from './router/o-node.router.js';
@@ -11,12 +20,15 @@ import { oMethodResolver, oToolBase } from '@olane/o-tool';
11
20
  import { oLeaderResolverFallback } from './router/index.js';
12
21
  import { oNodeNotificationManager } from './o-node.notification-manager.js';
13
22
  import { oReconnectionManager } from './managers/o-reconnection.manager.js';
23
+ import { LockManager } from './utils/lock-manager.js';
24
+ import { Synchronized } from './decorators/synchronized.js';
14
25
  export class oNode extends oToolBase {
15
26
  constructor(config) {
16
27
  super(config);
17
28
  this.didRegister = false;
18
29
  this.hooksStartFinished = [];
19
30
  this.hooksInitFinished = [];
31
+ this.lockManager = new LockManager();
20
32
  this.config = config;
21
33
  }
22
34
  get leader() {
@@ -522,6 +534,8 @@ export class oNode extends oToolBase {
522
534
  resetState() {
523
535
  // Reset registration flag
524
536
  this.didRegister = false;
537
+ // Clear all locks
538
+ this.lockManager.clearAll();
525
539
  // Clear peer references
526
540
  this.peerId = undefined;
527
541
  this.p2pNode = undefined;
@@ -638,3 +652,15 @@ export class oNode extends oToolBase {
638
652
  }
639
653
  }
640
654
  }
655
+ __decorate([
656
+ Synchronized('register:parent'),
657
+ __metadata("design:type", Function),
658
+ __metadata("design:paramtypes", []),
659
+ __metadata("design:returntype", Promise)
660
+ ], oNode.prototype, "registerParent", null);
661
+ __decorate([
662
+ Synchronized('register:leader'),
663
+ __metadata("design:type", Function),
664
+ __metadata("design:paramtypes", []),
665
+ __metadata("design:returntype", Promise)
666
+ ], oNode.prototype, "registerLeader", null);
@@ -1 +1 @@
1
- {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAoB,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAMrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IAC/C,OAAO,CAAC,aAAa,CAAiB;IAEhC,mBAAmB,CAAC,OAAO,EAAE,QAAQ;IAgBrC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAoBhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,IAAI,CAAC;IAIV,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IAwCV,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CAgC5D"}
1
+ {"version":3,"file":"o-node.tool.d.ts","sourceRoot":"","sources":["../../src/o-node.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAoB,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;;AAMrD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAkB;IAC/C,OAAO,CAAC,aAAa,CAAiB;IAEhC,mBAAmB,CAAC,OAAO,EAAE,QAAQ;IAsBrC,cAAc,CAAC,OAAO,EAAE,QAAQ;IAoBhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,IAAI,CAAC;IAIV,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IAyCV,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CAiC5D"}
@@ -13,6 +13,7 @@ import { ConnectionUtils } from './utils/connection.utils.js';
13
13
  export class oNodeTool extends oTool(oServerNode) {
14
14
  async handleProtocolReuse(address) {
15
15
  const reuseProtocol = address.protocol + '/reuse';
16
+ this.logger.debug('Handling protocol reuse: ' + reuseProtocol);
16
17
  const protocols = this.p2pNode.getProtocols();
17
18
  if (protocols.find((p) => p === reuseProtocol)) {
18
19
  // already handling
@@ -21,10 +22,11 @@ export class oNodeTool extends oTool(oServerNode) {
21
22
  const maxOutboundsStreams = process.env.MAX_OUTBOUND_STREAMS
22
23
  ? parseInt(process.env.MAX_OUTBOUND_STREAMS)
23
24
  : 1000;
24
- await this.p2pNode.handle(reuseProtocol, this.handleStream.bind(this), {
25
+ await this.p2pNode.handle(reuseProtocol, this.handleStreamReuse.bind(this), {
25
26
  maxInboundStreams: 10000,
26
27
  maxOutboundStreams: maxOutboundsStreams,
27
28
  });
29
+ this.logger.debug('Handled protocol reuse: ' + reuseProtocol);
28
30
  }
29
31
  async handleProtocol(address) {
30
32
  const protocols = this.p2pNode.getProtocols();
@@ -72,6 +74,7 @@ export class oNodeTool extends oTool(oServerNode) {
72
74
  callerAddress: this.address,
73
75
  p2pConnection: connection,
74
76
  reuse,
77
+ // requestHandler: this.execute.bind(this), TODO: do we need this?
75
78
  });
76
79
  // Use StreamHandler for consistent stream handling
77
80
  // This follows libp2p v3 best practices for length-prefixed streaming
@@ -108,6 +111,7 @@ export class oNodeTool extends oTool(oServerNode) {
108
111
  }));
109
112
  }
110
113
  // create downward direction connection
114
+ this.logger.debug('Pinging child to confirm access');
111
115
  await this.useChild(childAddress, {
112
116
  method: 'ping',
113
117
  params: {},
@@ -1,4 +1,5 @@
1
1
  export * from './stream.utils.js';
2
2
  export * from './network.utils.js';
3
3
  export * from './connection.utils.js';
4
+ export * from './lock-manager.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export * from './stream.utils.js';
2
2
  export * from './network.utils.js';
3
3
  export * from './connection.utils.js';
4
+ export * from './lock-manager.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Promise-based lock manager for handling concurrent access to critical sections
3
+ * Supports named locks for fine-grained concurrency control
4
+ */
5
+ export declare class LockManager {
6
+ private locks;
7
+ private lockStates;
8
+ private resolvers;
9
+ /**
10
+ * Acquire a named lock, waiting if necessary
11
+ * @param lockName - Unique identifier for the lock
12
+ * @param timeoutMs - Optional timeout in milliseconds
13
+ * @throws Error if timeout is reached
14
+ */
15
+ acquire(lockName: string, timeoutMs?: number): Promise<void>;
16
+ /**
17
+ * Release a named lock
18
+ * @param lockName - Unique identifier for the lock to release
19
+ */
20
+ release(lockName: string): void;
21
+ /**
22
+ * Execute a function with automatic lock acquisition/release
23
+ * @param lockName - Unique identifier for the lock
24
+ * @param fn - Async function to execute while holding the lock
25
+ * @param timeoutMs - Optional timeout for lock acquisition
26
+ * @returns The result of the function execution
27
+ */
28
+ withLock<T>(lockName: string, fn: () => Promise<T>, timeoutMs?: number): Promise<T>;
29
+ /**
30
+ * Check if a lock is currently held
31
+ * @param lockName - Unique identifier for the lock
32
+ * @returns true if the lock is currently held
33
+ */
34
+ isLocked(lockName: string): boolean;
35
+ /**
36
+ * Clear all locks (for teardown/reset)
37
+ * Resolves any pending waiters
38
+ */
39
+ clearAll(): void;
40
+ /**
41
+ * Get statistics about current lock states (useful for debugging)
42
+ * @returns Object containing lock statistics
43
+ */
44
+ getStats(): {
45
+ totalLocks: number;
46
+ activeLocks: number;
47
+ lockNames: string[];
48
+ };
49
+ }
50
+ //# sourceMappingURL=lock-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-manager.d.ts","sourceRoot":"","sources":["../../../src/utils/lock-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAyC;IACtD,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,SAAS,CAAsC;IAEvD;;;;;OAKG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BlE;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAS/B;;;;;;OAMG;IACG,QAAQ,CAAC,CAAC,EACd,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC;IASb;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAInC;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAUhB;;;OAGG;IACH,QAAQ,IAAI;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB;CAWF"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Promise-based lock manager for handling concurrent access to critical sections
3
+ * Supports named locks for fine-grained concurrency control
4
+ */
5
+ export class LockManager {
6
+ constructor() {
7
+ this.locks = new Map();
8
+ this.lockStates = new Map();
9
+ this.resolvers = new Map();
10
+ }
11
+ /**
12
+ * Acquire a named lock, waiting if necessary
13
+ * @param lockName - Unique identifier for the lock
14
+ * @param timeoutMs - Optional timeout in milliseconds
15
+ * @throws Error if timeout is reached
16
+ */
17
+ async acquire(lockName, timeoutMs) {
18
+ // Wait for any existing lock to be released
19
+ while (this.lockStates.get(lockName)) {
20
+ const currentLock = this.locks.get(lockName);
21
+ if (currentLock) {
22
+ if (timeoutMs) {
23
+ await Promise.race([
24
+ currentLock,
25
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Lock timeout: ${lockName}`)), timeoutMs)),
26
+ ]);
27
+ }
28
+ else {
29
+ await currentLock;
30
+ }
31
+ }
32
+ }
33
+ // Acquire the lock
34
+ this.lockStates.set(lockName, true);
35
+ const lockPromise = new Promise((resolve) => {
36
+ this.resolvers.set(lockName, resolve);
37
+ });
38
+ this.locks.set(lockName, lockPromise);
39
+ }
40
+ /**
41
+ * Release a named lock
42
+ * @param lockName - Unique identifier for the lock to release
43
+ */
44
+ release(lockName) {
45
+ const resolver = this.resolvers.get(lockName);
46
+ if (resolver) {
47
+ resolver();
48
+ this.resolvers.delete(lockName);
49
+ }
50
+ this.lockStates.set(lockName, false);
51
+ }
52
+ /**
53
+ * Execute a function with automatic lock acquisition/release
54
+ * @param lockName - Unique identifier for the lock
55
+ * @param fn - Async function to execute while holding the lock
56
+ * @param timeoutMs - Optional timeout for lock acquisition
57
+ * @returns The result of the function execution
58
+ */
59
+ async withLock(lockName, fn, timeoutMs) {
60
+ await this.acquire(lockName, timeoutMs);
61
+ try {
62
+ return await fn();
63
+ }
64
+ finally {
65
+ this.release(lockName);
66
+ }
67
+ }
68
+ /**
69
+ * Check if a lock is currently held
70
+ * @param lockName - Unique identifier for the lock
71
+ * @returns true if the lock is currently held
72
+ */
73
+ isLocked(lockName) {
74
+ return this.lockStates.get(lockName) ?? false;
75
+ }
76
+ /**
77
+ * Clear all locks (for teardown/reset)
78
+ * Resolves any pending waiters
79
+ */
80
+ clearAll() {
81
+ // Resolve any pending waiters
82
+ for (const lockName of this.lockStates.keys()) {
83
+ this.release(lockName);
84
+ }
85
+ this.locks.clear();
86
+ this.lockStates.clear();
87
+ this.resolvers.clear();
88
+ }
89
+ /**
90
+ * Get statistics about current lock states (useful for debugging)
91
+ * @returns Object containing lock statistics
92
+ */
93
+ getStats() {
94
+ const activeLocks = Array.from(this.lockStates.entries()).filter(([_, isLocked]) => isLocked);
95
+ return {
96
+ totalLocks: this.lockStates.size,
97
+ activeLocks: activeLocks.length,
98
+ lockNames: activeLocks.map(([name]) => name),
99
+ };
100
+ }
101
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-node",
3
- "version": "0.7.45",
3
+ "version": "0.7.46",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -40,7 +40,7 @@
40
40
  "devDependencies": {
41
41
  "@eslint/eslintrc": "^3.3.1",
42
42
  "@eslint/js": "^9.29.0",
43
- "@olane/o-test": "0.7.45",
43
+ "@olane/o-test": "0.7.46",
44
44
  "@tsconfig/node20": "^20.1.6",
45
45
  "@types/jest": "^30.0.0",
46
46
  "@types/json5": "^2.2.0",
@@ -60,13 +60,13 @@
60
60
  "typescript": "5.4.5"
61
61
  },
62
62
  "dependencies": {
63
- "@olane/o-config": "0.7.45",
64
- "@olane/o-core": "0.7.45",
65
- "@olane/o-protocol": "0.7.45",
66
- "@olane/o-tool": "0.7.45",
63
+ "@olane/o-config": "0.7.46",
64
+ "@olane/o-core": "0.7.46",
65
+ "@olane/o-protocol": "0.7.46",
66
+ "@olane/o-tool": "0.7.46",
67
67
  "debug": "^4.4.1",
68
68
  "dotenv": "^16.5.0",
69
69
  "json5": "^2.2.3"
70
70
  },
71
- "gitHead": "d336869dfa3105b453d2249ae388870088b17343"
71
+ "gitHead": "48c45f37c6e2e1ee626ce1fb0f0de175ca2db3b6"
72
72
  }