@tier0/opcua-client 0.4.0 → 0.4.1

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/README.md CHANGED
@@ -190,7 +190,7 @@ Node-RED Alpine image, the pinned open62541 v1.4.16 source archive, and its
190
190
  not contact npm or GitHub and does not invoke a compiler:
191
191
 
192
192
  ```bash
193
- npm install ./tier0-opcua-client-0.4.0.tgz --offline
193
+ npm install ./tier0-opcua-client-0.4.1.tgz --offline
194
194
  ```
195
195
 
196
196
  The loader uses the bundled native addon directly. Set
@@ -126,6 +126,16 @@ module.exports = function registerConnectionNode(RED) {
126
126
  function OpcUaConnectionNode(config) {
127
127
  RED.nodes.createNode(this, config);
128
128
  const node = this;
129
+ const dependentCleanups = new Set();
130
+
131
+ function runDependentCleanup(record) {
132
+ if (!record.promise) {
133
+ record.promise = Promise.resolve()
134
+ .then(record.cleanup)
135
+ .finally(() => dependentCleanups.delete(record));
136
+ }
137
+ return record.promise;
138
+ }
129
139
 
130
140
  const options = clientOptions(config, node.credentials);
131
141
  const registration = connectionRegistry.register(options);
@@ -138,6 +148,14 @@ module.exports = function registerConnectionNode(RED) {
138
148
  ...node.client.getDiagnostics(),
139
149
  connectionGovernance: node.getConnectionGovernance(),
140
150
  });
151
+ node.registerDependentCleanup = (cleanup) => {
152
+ if (typeof cleanup !== 'function') {
153
+ throw new TypeError('OPC UA dependent cleanup must be a function');
154
+ }
155
+ const record = { cleanup, promise: null };
156
+ dependentCleanups.add(record);
157
+ return () => runDependentCleanup(record);
158
+ };
141
159
 
142
160
  if (registration.duplicate && config.allowParallelSession !== true) {
143
161
  node.warn(
@@ -161,6 +179,14 @@ module.exports = function registerConnectionNode(RED) {
161
179
  node.client.off('fault', onFault);
162
180
  node.client.off('state', onState);
163
181
  registration.unregister();
182
+ const cleanupResults = await Promise.allSettled(
183
+ [...dependentCleanups].map(runDependentCleanup)
184
+ );
185
+ for (const result of cleanupResults) {
186
+ if (result.status === 'rejected') {
187
+ node.warn(`OPC UA dependent cleanup failed: ${result.reason?.message || result.reason}`);
188
+ }
189
+ }
164
190
  try {
165
191
  await node.client.disconnect();
166
192
  } catch (error) {
@@ -552,15 +552,23 @@ module.exports = function registerSubscribeNode(RED) {
552
552
  }
553
553
  });
554
554
 
555
- node.on('close', async (done) => {
555
+ async function cleanupSubscription() {
556
556
  node.closing = true;
557
557
  if (node.reconnectTimer) clearTimeout(node.reconnectTimer);
558
+ node.reconnectTimer = null;
558
559
  discardPendingChanges();
559
560
  node.connection?.client.off('fault', onFault);
560
561
  node.connection?.client.off('state', onState);
561
562
  node.desired.clear();
563
+ await runSerialized(() => clearSubscriptions());
564
+ }
565
+
566
+ const runCleanup = node.connection?.registerDependentCleanup?.(cleanupSubscription) ||
567
+ cleanupSubscription;
568
+
569
+ node.on('close', async (done) => {
562
570
  try {
563
- await runSerialized(() => clearSubscriptions());
571
+ await runCleanup();
564
572
  } catch (error) {
565
573
  node.warn(`OPC UA subscription cleanup failed: ${error.message}`);
566
574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tier0/opcua-client",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "High-scale OPC UA client nodes for Node-RED powered by open62541",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {