Skip to content

fix(deps): update dependency node-opcua to v2.182.2 - #795

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/node-opcua-2.x
Open

fix(deps): update dependency node-opcua to v2.182.2#795
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/node-opcua-2.x

Conversation

@renovate

@renovate renovate Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
node-opcua (source) 2.180.02.182.2 age confidence

Release Notes

node-opcua/node-opcua (node-opcua)

v2.182.2

Compare Source

v2.182.0

Compare Source

node-opcua 2.182.0

Compare: v2.181.0...v2.182.0 · 75 commits · 24 pull requests · includes the 2.181.1 patch

A standards, conformance and correctness release. node-opcua now reads and writes the JSON NodeSet of OPC 10000-6 Annex I, the normative JSON form of an information model, and ships the OPC UA JSON encoding as an MIT package. A second week against the OPC Foundation Compliance Test Tool (CTT) fixed a dozen server defects, most of them in the Publish service: response sizing, subscription priority, keep-alive timing, disabled subscriptions, and a session-hijack hole in CloseSession. The Local Discovery Server now enforces the authentication Part 4 requires of RegisterServer, which is a breaking change for deployments that relied on the old defaults. Four new packages, two new companion nodesets, and a release matrix tool that keeps a project's node-opcua-* pins on one coherent release.

Highlights
  • 📄 OPC 10000-6 Annex I JSON NodeSets (.jsonl, .json, .uanodeset) load out of the box in OPCUAServer, and can be written back. Verified against the OPC Foundation's own tool.
  • 🔓 node-opcua-json: the OPC UA JSON encoding of Part 6 clause 5.4, all four schemes, now MIT-licensed inside the SDK.
  • 🕸️ node-opcua-uanodeset-rdf: export an information model as JSON-LD, from an address space or over a live session, for SPARQL and OWL toolchains.
  • 🧮 npx node-opcua-versions@latest bump moves every node-opcua-* dependency of a project to one coherent release.
  • 📦 Publish responses are sized to the client's maxMessageSize, so a burst of large values no longer tears down the channel with BadTcpMessageTooLarge.
  • 🔐 CloseSession from a foreign SecureChannel is rejected, and the LDS refuses unauthenticated registrations.
  • 🏭 OPC 40700 and OPC 40702 (Surface Technology) join the nodeset catalog, with the instantiate() fix their shared children needed.

⚠️ Behaviour changes to review before upgrading

Local Discovery Server: registration now requires an authenticated channel (breaking). As OPC UA Part 4 §5.5.5 and §5.5.6 require, OPCUADiscoveryServer refuses RegisterServer and RegisterServer2 over a MessageSecurityMode.None channel with BadSecurityModeInsufficient, refuses a serverUri that does not match the ApplicationUri of the caller's certificate with BadServerUriInvalid, and its default certificate manager no longer trusts unknown certificates: an unknown registrant is refused at OpenSecureChannel and its certificate lands in the rejected folder. Move it to trusted/certs to allow the registration; the LDS logs both paths at startup. FindServers, FindServersOnNetwork and GetEndpoints stay available without security. OPCUAServer already registers over SignAndEncrypt, so nothing changes on the server side. Two opt-ins restore the old behaviour: allowUnsecuredRegistration: true and automaticallyAcceptUnknownCertificate: true. #​1635

A client's MaxMessageSize is never raised. A client asking for less than the server's 128 kB floor had its limit raised to it, then refused the messages it received. A client-supplied value is now only ever lowered, and only to what the server can manage. Shipped in 2.181.1. [c4cd90a]

A Publish response fits the negotiated maxMessageSize. Notifications were batched by count alone. Bytes now count alongside, the remainder goes out under moreNotifications, and a single value that cannot fit even alone is delivered with its clientHandle and timestamps but BadResponseTooLarge and no value; the item recovers when its value next fits. Behaviour is unchanged when the client announced no limit. Shipped in 2.181.1. #​1626

A UserNameIdentityToken without the serverNonce answers BadIdentityTokenInvalid, not BadUserAccessDenied. A wrong password still answers BadUserAccessDenied. Subclasses overriding the protected isUserAuthorized and calling back with false keep today's behaviour. Shipped in 2.181.1. #​1625

Publish arbitration follows the specification. Among a session's ready subscriptions the highest priority is served first, then the longest since served; a keep-alive is owed MaxKeepAliveCount cycles after the last data, not one; a disabled subscription keeps sending its keep-alive under its own id. Clients that relied on "whichever timer fires first" will see different ordering. #​1631 #​1637 #​1641

revisedSamplingInterval is never below the advertised MinSupportedSampleRate. A requested 0 on the Value attribute of an exception-based variable now answers 50 ms (the advertised floor) instead of 0. The item stays exception-based, but changes inside one interval are coalesced: the first goes out at once, the latest when the window ends. A server that sets minSupportedSampleRate: 0 keeps the old answer. Event items requesting 0 are still answered 0. #​1633

Server/PublishSubscribe is browsable by every session. Opc.Ua.NodeSet2.xml reserves Browse of the SecurityGroups and KeyPushTargets methods to SecurityKeyServerAdmin, a role no node-opcua user manager assigns, so since 2.180.0 nobody could see them. The new ensureStructureIsBrowsable grants Browse to Anonymous and AuthenticatedUser on 25 nodes under PublishSubscribe; Read, Write and Call stay with the roles the nodeset names. #​1634

A denied Value read carries the requested timestamps. BadUserAccessDenied, BadNotReadable and BadSecurityModeInsufficient results, and a fresh UncertainInitialValue, are stamped with the clock of the denial or of construction. The value stays undisclosed. #​1639

An instance DataType is narrowed to the one its type declares. After a load, a variable whose DataType is a strict supertype of the declaration on its type definition is moved to the declared type, provided its value already fits. On the standard 1.05.07 nodeset plus DI, ADI, AutoID, IA, Machinery, Robotics, MachineVision and GDS exactly one node moves: DI ModelVersion, from String to SemanticVersionString. #​1640

A Variable declaring BaseDataType with no value reads BadWaitingForInitialData, as one omitting the DataType always did, instead of UncertainInitialValue. Two nodes move in the standard nodeset, both NumberInList. [e39919c]

The loader keeps what a nodeset declared. A DateTime <Value>, IsOptionSet, EventNotifier on an ObjectType, ReleaseStatus, AccessRestrictions, Category, Documentation and Extensions were dropped at load; they are carried now. A variable declaring ArrayDimensions="0" no longer reads back "1". #​1636

Precompiled nodeset images use record schema 3. Every image cached from 2.181.0 (imageStore: true) is rebuilt on first use; the catalog images shipped with node-opcua-nodesets are already regenerated. [dc4b1f6]

A re-evaluated CertificateExpirationAlarm stays silent when nothing changed. Servers with push certificate management raised a "certificate is OK" event on the Server object every minute. #​1644

node-opcua-server depends on node-opcua-uanodeset-json and node-opcua-json. A client-only consumer of node-opcua-client pays nothing. [7f1b8c5]


🚀 Features
The JSON NodeSet of OPC 10000-6 Annex I (#​1642, #​1646)

Annex I is normative in Part 6 draft 1.05.08 and specifies three serialisations of a UANodeSet. node-opcua-uanodeset-json reads and writes all three:

extension Annex what it is
.jsonl I.4 one JSON object per line, header first: stream, grep, diff
.json I.1 one document, children nested in their parent
.uanodeset I.3 a TAR.GZ of a manifest and numbered members
  • Nothing to call. node-opcua-server imports the package, so an OPCUAServer loads an Annex I document given its path. Formats are chosen by sniffing the content, not the extension, after inflating, so .jsonl.gz and a misnamed file both load. Import node-opcua-uanodeset-json/jsonl to register one form only.
  • Verified against the OPC Foundation's ua-nodeset-tool on the DI nodeset: reading any form gives the same address-space digest as the NodeSet2 XML (5 923 nodes, 20 754 references); writing is a byte-identical fixpoint; the reference tool reads what node-opcua writes and reports one difference, DesignToolOnly, which node-opcua has never carried in any format.
  • The loader gained a format registry: a NodesetFormat says whether a document is its own, what it depends on and what its records are. XML and the precompiled image go through the same contract, and every format gets a cache sibling. [6646887]
  • Known limit: a nodeset that defines a DataType and uses it as a value in the same document cannot be decoded yet.
The OPC UA JSON encoding, under MIT (#​1642)

node-opcua-json implements Part 6 clause 5.4 for every built-in type, Variant, DataValue and ExtensionObject, in all four schemes: DeprecatedNonReversible and DeprecatedReversible (1.04), Verbose and Compact (1.05). It is the wire form of PubSub over MQTT and AMQP and what a Variant looks like inside an Annex I document. Moved from the PubSub workspace and relicensed; 175 tests came with it.

import { JsonEncodingScheme, opcuaJsonEncodeVariant } from "node-opcua-json";
opcuaJsonEncodeVariant(v, JsonEncodingScheme.Compact, namespaceArray);   // { UaType: 3, Value: 42 }
An information model as JSON-LD (#​1643)

node-opcua-uanodeset-rdf exports a model for a SPARQL store or an OWL toolchain, with the vocabulary of the OPC Foundation's reference tool. Two entry points: over a loaded address space, and node-opcua-uanodeset-rdf/session over Browse and Read of a live server, which never loads node-opcua-address-space and accepts a ClientSession or a PseudoSession. Graph membership matches the reference tool exactly on DI (434 entries); the predicate under which a child is written still differs in places, and the README says so. One-way: there is no reader.

node-opcua-versions: a release is a set, not a number (#​1648)

Only the packages that changed are bumped in a release, so node-opcua 2.181.1 legitimately depended on node-opcua-debug 2.181.0, and nothing on npm related the fifty packages beside the umbrella to a release. The new package carries the release matrix; its version is the release, and it is published last, so its presence on the registry proves the release is complete.

npx node-opcua-versions@latest bump      # every node-opcua-* dependency to the newest release
npx node-opcua-versions@2.182.0 bump     # to that release exactly, up or down
npx node-opcua-versions@latest check     # is every pin part of one release? exit 1 otherwise, --fix repairs
npx node-opcua-versions@latest bump -w   # the whole workspace

expand declares the node-opcua-* peers a Sterfive package needs at your release; --install runs the right package manager once per lockfile. 2.182.0 is the first release with a set.

NodeSet2 XML from records, and the tools split by cost (#​1636, #​1647)
  • recordsToNodeset2XML writes a NodeSet2 document straight from the record form with no address space in the loop. Over the 33 published nodesets, records to XML to records is exact: 19 892 records, zero differences. The UA namespace itself can now be exported and reloaded (5 476 nodes identical); AllowSubTypes is written back; a Definition Name is spelled as the document spelled it; a 64-bit integer is a decimal string, never its low half.
  • opcua-nodeset-image (build, xml, records, info, diff) never builds an address space; the new opcua-nodeset-live (verify, export, equivalence) does, and equivalence shows what an implementation loses when it loads a document and writes it back.
  • The RFC in documentation/rfc/nodeset-ndjson.md withdraws NodeSet-NDJSON as an interchange proposal: Annex I is the normative JSON NodeSet, and the format is documented for what it is, an internal load cache frozen at schema 3. What replaces the proposal is an implementation report on Annex I, with five review comments for the annex.
Nodeset catalog
  • OPC 40700 (Surface Technology General Types) and OPC 40702 (OCT-MSS) join node-opcua-nodesets as nodesets.surfaceTechnologyGeneralTypes and nodesets.surfaceTechnologyOctMss, with their images and the generated node-opcua-nodeset-surface-technology-general-types and node-opcua-nodeset-surface-technology-oct-mss packages. [9e2df41]
Address space
  • instantiate() honours options.references. A child linked to its parent through HasOrderedComponent, HasPhysicalComponent, HasContainedComponent or HasAttachedComponent can be given its parent at construction, so the NodeIdManager names it with its parent chain (ListType_First, not First) and a seeded symbol table matches. No change for callers that do not pass it. #​1645
  • ensureStructureIsBrowsable(node) grants Browse to Anonymous and AuthenticatedUser on a node and its aggregates when the nodeset grants it to neither, leaving every other permission alone. #​1634
Server
  • ServerCapabilities.ConformanceUnits (i=24101) is bound. The call had been commented out since 2022, so serverCapabilities.conformanceUnits never reached the address space. It is read live, so the list can be filled after start. #​1632
  • OPCUADiscoveryServer emits onRegistrationRefused and logs each refusal with the caller's address, security mode and certificate ApplicationUri, as Part 4 asks of a Discovery Server. #​1635
  • trustListUpdated fires on an out-of-band TrustList change (an admin script or a Pull-model client writing into the trusted folder), not only on the TrustList methods. Deliberately not applyChangesCompleted, which implies a rotation and tears down channels. #​1622

⚡ Performance
  • The subscription notification path lost two complexity problems: purging pending notifications on a bulk DeleteMonitoredItems or terminate() is one pass with a Set instead of one pass per item (measured 2 ms to 47 ms superlinear at 2 000 items on a live server), and a notification's encoded size is measured once at capture rather than again at assembly. The tests count passes over the queue, not milliseconds. #​1629
  • Exception-based monitored items coalesce to one notification per reported interval, so a fast writer costs one filter, clone and queue operation per interval instead of per change. #​1633
  • OPCUADiscoveryServer.shutdown() no longer sleeps one second after the endpoints close; endpoint shutdown already waits for the listener. [1c61815]
  • The NodeSet-NDJSON benchmark was corrected to read files outside the timed region and resolve chains from the catalog. Remeasured idle: a cold load from the image is 19 to 31% faster than from XML and a parse 60 to 69%, on documents that differ in size by a factor of twelve. Nothing in the loader got faster; the measurement stopped being wrong. [10166e2] [d82b91c]

🐛 Fixes
Shipped in 2.181.1 (2026-09-05)
  • transport: never raise a MaxMessageSize the client asked for. [c4cd90a]
  • server: size a PublishResponse to the channel, not to a count; BadResponseTooLarge for a value that cannot fit alone. #​1626
  • server: a UserNameIdentityToken without the serverNonce, or a blob that will not decrypt, answers BadIdentityTokenInvalid. #​1625
Server: sessions and Publish (OPC Foundation CTT 1.05.513)
  • server: CloseSession sent from a SecureChannel other than the one that created or activated the session is rejected with BadSessionIdInvalid. Any channel that knew a valid AuthenticationToken could terminate another client's session. #​1630
  • server: Publish requests are arbitrated by priority, then longest-since-served, across every ready subscription of a session; each subscription used to serve itself from the shared queue whenever its own timer fired. #​1631
  • server: a disabled subscription keeps sending its keep-alive under its own id beside its siblings; the arbitration of the previous fix skipped it entirely, so its Publish waited until the client timeout and its held data went out on a stale request when re-enabled (Subscription Basic 014, 044, 046, 048). #​1637
  • server: the flush after a Publish response is not a publishing cycle. It advanced the keep-alive, lifetime and interval counters, so a keep-alive fell due one cycle after data and stole a request a sibling subscription needed: with five subscriptions and five queued requests, one was never answered (Subscription Publish Min 05 003). The late-subscription comparator also returns a sign now. #​1641
  • server: two late subscriptions are served by priority when a Publish arrives; the comparator never returned a negative number, so the subscription created second always won (Subscription Minimum 02 005, 007, 008). [9df5634]
  • server: revisedSamplingInterval honours the advertised MinSupportedSampleRate (Monitor Basic 038); itemToMonitor was assigned after the parameters were parsed, and modify() rewrote a requested 0 before parsing. #​1633
  • server: ServerCapabilities.ConformanceUnits is bound and encodes an empty list as a typed empty array (Base Info Core Structure 2 001). #​1632
  • server: the mandatory methods of Server/PublishSubscribe/SecurityGroups are browsable again (Base Info Core Structure 002). #​1634
  • alarms: a re-evaluated certificate expiration alarm raises no event when active state, severity and message are unchanged (Subscription Minimum 02 020). #​1644
Local Discovery Server
  • lds: RegisterServer and RegisterServer2 require an authenticated channel and a matching serverUri; the default store no longer auto-trusts unknown certificates. See the behaviour changes above. #​1635
  • lds: plain RegisterServer with isOnline: true failed in the mDNS announcer on an empty mdnsServerName. [3ba0ca3]
  • server: RegisterServerManager really falls back to a Sign, then a None, LDS endpoint when no SignAndEncrypt endpoint is offered; the fallback filtered an already-empty list and could never select anything. [08e89a9]
Address space and loader
  • address-space: a child shared by two parents is instantiated once. OPC 40700 declares MachineryOperationMode under both MachineryBuildingBlocks and Monitoring/Status; instantiate() produced two clones, warned "has more than one parent", and created Optional children that were not requested. Same root cause as #​1143. #​1638
  • alarms: ExclusiveLimitAlarmType asks explicitly for the ActiveState texts and transition times it used to receive through the optional-filter bug above. [732b4d2]
  • address-space: a denied Value read carries the requested timestamps (Base Info Core Structure 2 001). #​1639
  • address-space: an instance DataType is narrowed to the one its type declares (Base Info Core Structure 2 001, DI over 1.05). #​1640
  • address-space: a Variable with no usable DataType and no value answers BadWaitingForInitialData whichever way the document said it. [e39919c]
  • address-space: the loader keeps DateTime values, IsOptionSet, ObjectType EventNotifier, ReleaseStatus, AccessRestrictions, Category, Documentation and Extensions; the extension-object array binder no longer writes its length fallback into the node's arrayDimensions. [634e18b] [e6b02e4]
  • address-space: the UA namespace can be exported; the translation table used to remap the exported namespace over its own entry. AllowSubTypes is written back instead of a wrong IsOptional. [90c50d8]
  • address-space: an empty <Description> was written onto every Argument that never had one. [e6b02e4]
  • nodeid: coerceNodeId("nsu=urn:x;s=a;b") reads back: the namespace URI ends at the first semicolon, not the last, so a string identifier may contain any number. toString had always written that form correctly. [8b9a81c]
  • xml2json: an element's namespace prefix reaches the fragment cloner, so an Extension no longer comes back bound to the default namespace with an unused declaration beside it. [02a1f7c]
Other
  • server-configuration: an out-of-band TrustList change is reported as trustListUpdated, so a server waiting to leave NoConfiguration hears about it. #​1622
  • certificate-manager: the doc comments said the store lives at <rootFolder>/<name>; it is rootFolder alone, and two managers sharing a rootFolder share one store and one private key whatever their name. Documentation and two tests, no behaviour change. #​1628
  • build: the API documentation workflow had failed since 2.181.0; the solution-style tsconfigs inherited a root file and TypeDoc stopped walking their references. [04354d3]

🗑️ Deprecations

No API is deprecated in this release. The NodeSet-NDJSON RFC is withdrawn as an interchange proposal; the .ndjson.gz images stay as node-opcua's own load cache, frozen at schema 3 with no cross-version guarantee.


📦 Packages and dependencies
New package What
node-opcua-json the OPC UA JSON encoding, Part 6 clause 5.4, MIT
node-opcua-uanodeset-json the Annex I JSON NodeSet, three serialisations, read and write
node-opcua-uanodeset-rdf one-way JSON-LD export of an information model
node-opcua-versions the release matrix and its CLI (npx node-opcua-versions@latest)
node-opcua-nodeset-surface-technology-general-types, node-opcua-nodeset-surface-technology-oct-mss generated typings for OPC 40700 and OPC 40702

node-opcua-crypto stays at 5.10.1 and node-opcua-pki at 6.22.0. commander 15 is a dependency of the new CLI only.


🧹 Internal
  • The catalog-wide round-trip suites (two minutes) move behind pnpm --filter node-opcua-address-space run test:long, with a job in both GitHub and GitLab CI. [02a1f7c]
  • Root pnpm run lint:ci runs the exact sequence of the CI lint job, so a branch can be checked the way CI checks it before it is pushed. [f921efb]
  • version:lerna no longer wipes and reinstalls the workspace; the release set of node-opcua-versions is staged from the root version lifecycle and pnpm run check:releases fails CI when it is stale. [060f132] [31a7e36]
  • The address-space and discovery test suites assert through should(value) instead of value!.should, which check:shortcircuit now enforces across 3 208 files. [81b522f] [325ccdf] [474cd5d]
  • Stale test certificates in the persistent temp stores are recreated when they no longer match the store key; this is what made DISCO4-J fail in full runs and pass alone. [4c84741]

Pull requests
  • #​1622 fix(server-configuration): relay out-of-band TrustList changes
  • #​1625 fix(server): a UserNameIdentityToken without the serverNonce is invalid
  • #​1626 fix(server): size a PublishResponse to the channel, not to a count
  • #​1628 docs(certificate-manager): rootFolder is the store, name is only a label
  • #​1629 perf(server): remove two complexity problems on the subscription notification path
  • #​1630 fix(server): reject CloseSession sent from a foreign SecureChannel
  • #​1631 fix(server): arbitrate Publish requests by priority, not tick order
  • #​1632 fix(server): bind ServerCapabilities.ConformanceUnits (i=24101)
  • #​1633 fix(server): revisedSamplingInterval is never below the advertised MinSupportedSampleRate
  • #​1634 fix(server): keep PublishSubscribe SecurityGroups methods browsable
  • #​1635 fix(lds): require an authenticated SecureChannel for RegisterServer(2)
  • #​1636 NodeSet-NDJSON: write NodeSet2 XML back from records, and an RFC for the format
  • #​1637 fix(server): keep a disabled subscription alive beside its siblings
  • #​1638 fix(address-space): instantiate a child shared by two parents once (OPC 40700)
  • #​1639 fix(address-space): a denied Value read carries the requested timestamps
  • #​1640 fix(address-space): narrow an instance DataType to the one its type declares
  • #​1641 fix(server): the flush after a Publish response is not a publishing cycle
  • #​1642 feat: read and write the JSON NodeSet of OPC 10000-6 Annex I
  • #​1643 feat(uanodeset-rdf): export an information model as JSON-LD
  • #​1644 fix(alarms): a re-evaluated certificate alarm stays silent when nothing changed
  • #​1645 feat(address-space): instantiate() honours options.references
  • #​1646 docs(json, uanodeset-json): READMEs for the two new packages
  • #​1647 docs(rfc): withdraw the interchange proposal, report on Annex I instead
  • #​1648 feat(versions): node-opcua-versions, the release matrix and its CLI

Full Changelog: node-opcua/node-opcua@v2.181.0...v2.182.0

v2.181.1

Compare Source

v2.181.0

Compare Source

node-opcua 2.181.0 - One small step for the version number, one giant leap for nodeset loading.

Compare: v2.180.0...v2.181.0 · 180 commits · 41 pull requests

A security, conformance and performance release, and a large one. The application private key can now live in an HSM, a cloud KMS or a TPM and never be read by node-opcua. Loading a NodeSet2 file takes well under half the time it did, thanks to precompiled images shipped with node-opcua-nodesets and a rewritten loader. A campaign against the OPC Foundation Compliance Test Tool (CTT) fixed eleven conformance defects in Read, Write, Subscriptions, GetEndpoints and the server capabilities. The published API of node-opcua-address-space has been repaired so that what TypeScript accepts is what exists at run time. Underneath, the whole monorepo was prepared for a future ESM migration without changing the module format: require("node-opcua") works exactly as before.

Highlights
  • 🔐 HSM/KMS-held application keys end to end (secure channels, session signatures, user tokens, push certificate management). File-based keys are unchanged and not deprecated.
  • NodeSet2 loading is 2 to 2.5 times faster by default, with a precompiled image beside every nodeset of the catalog and zero configuration.
  • 🧩 OPCUAServer({ nodesets: [...] }) loads paths, gzip files, URLs and streams in one list; nodeset_filename is deprecated.
  • Eleven CTT conformance fixes, including subscription keep-alive timing, MaxAge, DateTime precision and operation limits.
  • 🔔 subscriptionId_changed client event when a subscription had to be recreated during reconnection (fixes #​1368).
  • 📦 node-opcua-client-browser ships its ESM build again: previous tarballs had no dist-esm folder at all.

⚠️ Behaviour changes to review before upgrading

None of these need code changes for a typical application, but each one is observable.

Server operation limits now have real defaults and are enforced. In 2.180.0 every OperationLimits value defaulted to 0 (not exposed, not enforced). The defaults are now 10 000 for Read, Write, Browse, RegisterNodes, TranslateBrowsePaths and MonitoredItemsPerCall, and 1 000 for MethodCall and the two HistoryRead limits. A request above a limit is answered with BadTooManyOperations. An explicit 0 in serverCapabilities.operationLimits still means "not exposed". [9e06cf0] [63c4eb9] [56ef7ee]

ensureObjectIsSecure keeps the structure browsable by default. The nodes of a secured object are now visible to every session, including Anonymous, and only Read, Write and Call stay restricted to the admin roles on a signed and encrypted channel. This is what the CTT expects of the mandatory Identities of the well-known roles. Pass { hideStructure: true } to keep the previous behaviour. [28a71d1] [a62d2cf]

A subscription's first message goes out after one publishing interval, not immediately. A PublishRequest that arrived before the first tick used to be answered in about 20 ms. Part 4 §5.13.1 requires the first keep-alive after one cycle. Tests that timed the first response as "immediate" will need adjusting. [9b9f8e7]

SetMonitoringMode to the mode an item already has returns Good. It returned BadNothingToDo per item, which the specification reserves for the service-level result of an empty request. [833247c]

OPCUAServer.initialize() rejects when a nodeset fails to load. It used to resolve with a half-loaded address space, and the engine callback was invoked twice. [50c86bf]

Two write status codes corrected. A write allowed by AccessLevel but withheld by UserAccessLevel returns BadUserAccessDenied (was BadWriteNotSupported). Writing Historizing on a variable without an HA Configuration returns BadNotWritable (was BadNotSupported). [a993a71] [b277a1b]

Read semantics aligned with Part 4. A Read with MaxAge refreshes a cached ServerTimestamp older than MaxAge. TimestampsToReturn.Source no longer leaks the server timestamp. A BadDataEncodingInvalid result carries the requested timestamps. [9756c30] [3d505f7] [48668f1]

GetEndpoints serves ApplicationName in the first requested LocaleId instead of a hard-coded en-US. [efc6ccd]

Child accessors are inherited, non-enumerable, and never shadow a member. Object.hasOwn(node, "enabledState") is now false and Object.keys(node) no longer lists children, while "enabledState" in node is true on every node. A child named like an attribute, a method or a field (eventNotifier, namespaceUri, Then) is no longer reachable as node.eventNotifier; use getChildByName. Only a structural reference (component, property, subtype, organizes) makes a dotted child; a node that is only an event source or notifier of its parent is not one. [2db095c] [30feed2]

INamespace gained members. It now extends INamespaceIterable (nodeIterator, objectTypeIterator, objectTypeCount, the same for variable, data and reference types, aliasCount). Code that implements INamespace by hand must add them; code that only consumes the interface is unaffected. [07d3ed4]

A method's ParentNodeId is now translated into the address space's namespace table like every other id of a loaded nodeset; it used to be passed through as the file's string. [4f35505]

dumpXml can no longer be imported from node-opcua. It was declared, exported by no module and undefined at run time. [b22a0dd]

Internal secure-channel plumbing changed. MessageBuilder, ClientSecureChannelLayer, ServerSecureChannelLayer and OPCUAServerEndPoint construction are tagged @internal and now work on key operations only. invalidPrivateKey, MessageBuilderOptions.privateKey, the channel and endpoint getPrivateKey() and the endpoint certificateChain + privateKey options are deleted. Applications configuring security through OPCUAClient, OPCUAServer and OPCUACertificateManager options are not affected. See migrating_to_key_operations.md. [3961b5f]

node-opcua-nodesets grows by about 0.8 MB for the precompiled images. [3811e29]


🚀 Features
Opaque key operations: HSM, KMS and TPM-held keys (#​1587, #​1591)

node-opcua can now run with a private key it can never read. An IKeyOperations provider (node-opcua-crypto 5.10.1, node-opcua-pki 6.22.0) signs and decrypts on behalf of the application, and every path uses it:

  • Certificate bootstrap and renewal, Sign and SignAndEncrypt secure channels (Basic256Sha256 and Aes256_Sha256_RsaPss verified end to end), session signatures, X509 user tokens (UserIdentityInfoX509.keyOperations) and password decryption.
  • An asynchronous OpenSecureChannel pipeline: chunk headers stay synchronous so sequence numbers keep their order, only the crypto is deferred. Local keys ride the provider's synchronous fast path and produce byte-identical output; the transaction suites measure no difference (100.1 s vs 100.4 s).
  • Push certificate management over an opaque key: CreateSigningRequest(regeneratePrivateKey=false) and certificate-only UpdateCertificate work, the certificate being verified against the provider's public key. regeneratePrivateKey=true and a pushed private key are refused with BadNotSupported, since key rotation belongs to the key store.
  • New: OpaqueCertificateKeyPairProvider, ICertificateKeyPairProvider2.getKeyOperations(), computeSignatureAsync, asymmetricDecryptWithKeyOps, CryptoFactory.signParams and decryptParams.
  • Docs: using_hsm_kms_keys.md, migrating_to_key_operations.md, and a runnable sample simple_server_with_hsm_style_key.ts.
  • A check:privatekey ratchet in CI keeps raw getPrivateKey() call sites from creeping back.
Nodeset loading: sources, images, and one server option (#​1609, #​1611, #​1612, #​1617, #​1618)

OPCUAServer takes nodesets. Paths and sources mix in one list and load in dependency order:

const server = new OPCUAServer({
    nodesets: [
        nodesets.standard,                                          // a path, as nodeset_filename took
        nodesetSourceFromGzipFile("plant_model.xml.gz"),            // Node.js build
        nodesetSourceFromUrl("https://models.example.com/plant.xml", { init: { headers } })
    ],
    nodesetLoaderOptions: { yieldEveryBytes: 1024 * 1024 }
});
  • nodesetSourceFromGzipFile(path), nodesetSourceFromUrl(url, { init?, gzip? }) and nodesetSourceFromStream(name, open) are lazy and reusable; the URL helper rejects the load with the HTTP status on a response that is not ok. nodesetLoaderOptions carries yieldEveryBytes, imageStore, permissions and accessRestrictions.
  • A nodeset loads after the models an earlier call loaded. generateAddressSpace(server.engine.addressSpace, [companionSource]) after server.initialize() used to fail with Cannot find namespace for http://opcfoundation.org/UA/. A model already in the address space now satisfies the requirement when its version is high enough; a lower version is an error naming both versions.
  • A nodeset loads from any source, parsed as the chunks arrive: a string, UTF-8 bytes, a Node.js Readable, a web ReadableStream, an async generator, or a function opening one. yieldEveryBytes (default 8 MiB) lets the event loop turn while a large model loads, so a running server keeps answering. A source that fails half-way rejects the load with the source named.
  • Precompiled nodeset images. An image is the records of a NodeSet2 document as gzip-compressed JSON Lines (.ndjson.gz) with the SHA-256 of its source in the trailer. node-opcua-nodesets ships one beside every nodeset of its catalog, and the Node.js generateAddressSpace replays <name>.ndjson.gz when its digest matches <name>.xml. A missing, stale or truncated image silently falls back to the XML. imageStore: false disables it; imageStore: true adds a per-user cache for your own nodesets (NODE_OPCUA_NODESET_IMAGE_DIR or ~/.cache/node-opcua/nodeset-images, atomic writes, oldest-first eviction).
  • opcua-nodeset-image, a new CLI in node-opcua-address-space: build, verify, info and export. nodesetToImage(source) does the same from code and needs neither the file's dependencies nor an address space.
  • A namespace exports to records and to an image, not only to XML: namespace.toNodesetRecords(), namespace.toNodesetImage(). The round trip is the identity, and toNodeset2XML output is byte-identical to 2.180.0 (golden test). The loader now reads UAView elements.
  • addressSpace.registerChildAccessorNames(names) gives runtime-created browse names the same shared accessor loaded names get.
Client
  • subscriptionId_changed event on ClientSubscription, carrying the new and previous ids, emitted when the server could not transfer the subscription during reconnection and the client rebuilt it. Documented in creating_a_client_typescript.md. Fixes #​1368. #​1619
Address space: the published API now matches the runtime (#​1603, #​1604, #​1606, #​1607)
  • main and types of node-opcua-address-space named different modules. Twelve names type-checked and were undefined at run time (CloneHelper, EventNotifierFlags, exploreNode, getContextMaxMessageSize, ...), and fifty-two existed that TypeScript refused. Both entries now point at one module, and a check-entry-points gate keeps them aligned across all 117 packages.
  • One AddressSpace, not two. A stub class shadowed the real one, so AddressSpace.create() through the types entry returned an object with no methods, and assigning AddressSpace.historizerFactory reached a static nothing read. AddressSpace is now an interface plus a const; the implementation is AddressSpaceImpl, internal.
  • The documented way to customise an alarm works. ConditionInfo has a constructor, and calculateConditionInfo is on the published alarm interface. _calculateConditionInfo stays, deprecated, and an override of it still wins.
  • Helpers downstream code already used are published with doc comments: the promoteTo* family, dumpToBSD, VariableHistorian, NodeIdManager, addElement, removeElement, bindExtObjArrayNode, createExtObjArrayNode, resolveReferenceNode, resolveReferenceType, adjustNamespaceArray, makeAttributeEventName.
  • Namespace iteration is public: nodeIterator, objectTypeIterator and friends on INamespace, so no more casts to a hand-written copy of the internals.
  • *Impl classes no longer appear in the generated documentation; every implementation states the interface it satisfies instead of casting to it.
Server
  • ensureObjectIsSecure(node, { hideStructure }), see the behaviour changes above. [a62d2cf]
  • Real defaults for every operation limit the server enforces, see above. [9e06cf0]
  • makeNowDataValue() in node-opcua-data-value builds a DataValue stamped with the current clock, for results made up at read time. [3d505f7]
Conformance-testing address space (#​1610, #​1623)
  • A new Objects/CTT folder mirrors the ~230 NodeId settings of the CTT server project (s=CTT/<setting path>), so a project file can be generated by browsing instead of maintained by hand: Variant and Image scalars, Variant arrays, Structure variables, AnalogItemType arrays, the five ArrayItemTypes with their axis properties, and the HA-profile scalars, arrays and access-right variables with in-memory history. Unresolved settings drop from 107 to 35.
  • The alarm input nodes the CTT writes to, each a writable scalar with an alarm instantiated on it, under one AlarmSource that is an event source of the Server object.
  • The ExtensionObject variables take a concrete structure from the caller through a new optional extensionObject option.
Tooling
  • node-opcua-convert-nodeset-to-javascript escapes as $name exactly the child names the runtime cannot expose on the parent class; the 29 generated node-opcua-nodeset-* packages are unchanged. [3e1a96d]

⚡ Performance

All figures are the maintainer's, best of N on one machine, before and after within the same pull request.

Scenario Before After PR
Standard nodeset, XML path, cold load 254 ms 207 ms #​1609
Standard nodeset, heap retained after load 17.1 MB 14.6 MB #​1609
findReferencesEx calls during a standard load 22 201 1 852 #​1609
Standard nodeset, image replay vs XML on the same run 149 ms (XML) 82 ms (image) #​1611
Standard nodeset from image, node construction phase 72 ms 37 ms #​1612
Default generateAddressSpace path vs XML, standard 151 ms (XML) 94 ms (default) #​1617
Default path vs XML, six-file companion chain 194 ms (XML) 117 ms (default) #​1617
raiseEvent with 20 fields 225 µs 75 µs [9e782d0]
One TranslateBrowsePath step on a 5 000-child folder ~5 ms ~3 µs [ac1deb3]
readValue of a variable holding one million strings 156 ms 3 ms [9e782d0]
Parsing Opc.Ua.NodeSet2.xml (xml2json alone) 166 ms 150 ms #​1609

How it was done, in short:

  • Child accessors are shared getters on the prototype instead of an own property installed on every node at the end of each load, which cost four reference scans per node, 12% of the load time and 7% of the heap. getComponentByName, getPropertyByName, getMethodByName and getFolderElementByName read the same child index.
  • Reference lookups stop rescanning: findReferencesEx is memoized on nodes with more than eight references, references are indexed under a packed integer rather than two NodeId.toString() calls, no back reference is allocated for a reference the file declares from both ends, and the image records which few references need one so the end-of-load sweep is skipped on replay.
  • Nodes are cheaper to build: the private record is a symbol-keyed property rather than a WeakMap entry, display names and descriptions become LocalizedText on first read, and the loader no longer binds a getter and setter pair on every variable.
  • Initial values take a shorter path: no clone, comparison, change event or parent touch for a variable nobody has seen yet.
  • An image is inflated once through zlib off the main thread, its header and trailer read from its two ends, and the sibling check compares the XML length before hashing.
  • raiseEvent fills a layout computed once per event type; Variant.clone copies arrays of immutable element types by slicing.
  • xml2json: direct handler calls instead of EventEmitter, one regex step per tag name, a chunk no longer costs the square of its unread tail.

🐛 Fixes
Conformance (OPC Foundation CTT, Nano Embedded Device profile) (#​1613, #​1623)
  • server: a Read with MaxAge refreshes a ServerTimestamp older than MaxAge (Attribute Read 006, 018, 023). [9756c30]
  • data-value: TimestampsToReturn.Source drops the server timestamp (Attribute Read 007). [3d505f7]
  • address-space: BadDataEncodingInvalid carries the read timestamps (Attribute Read 037). [48668f1]
  • date-time: a DateTime keeps its 100 ns ticks through encode and decode, as the picoseconds property; a value written at full OPC UA precision now reads back equal (Attribute Write Values 003). [500511a]
  • address-space: a secured object stays browsable by every session (Base Info Core Structure 001). [28a71d1]
  • server: GetEndpoints honours the requested LocaleId (Discovery Get Endpoints 002). [efc6ccd]
  • server: an

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot force-pushed the renovate/node-opcua-2.x branch from 6e38cf0 to 48f653c Compare September 5, 2026 22:28
@renovate renovate Bot changed the title fix(deps): update dependency node-opcua to v2.181.0 fix(deps): update dependency node-opcua to v2.181.1 Sep 5, 2026
@renovate
renovate Bot force-pushed the renovate/node-opcua-2.x branch from 48f653c to 1f348ad Compare September 8, 2026 14:39
@renovate renovate Bot changed the title fix(deps): update dependency node-opcua to v2.181.1 fix(deps): update dependency node-opcua to v2.182.0 Sep 8, 2026
@renovate
renovate Bot force-pushed the renovate/node-opcua-2.x branch from 1f348ad to b4a010a Compare September 9, 2026 17:52
@renovate renovate Bot changed the title fix(deps): update dependency node-opcua to v2.182.0 fix(deps): update dependency node-opcua to v2.182.2 Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants