node-opcua-alias-name-server 2.176.0

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.
Files changed (51) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +361 -0
  3. package/dist/add_alias.d.ts +73 -0
  4. package/dist/add_alias.js +262 -0
  5. package/dist/add_alias.js.map +1 -0
  6. package/dist/address_space_alias_store.d.ts +130 -0
  7. package/dist/address_space_alias_store.js +360 -0
  8. package/dist/address_space_alias_store.js.map +1 -0
  9. package/dist/alias_hierarchy.d.ts +45 -0
  10. package/dist/alias_hierarchy.js +135 -0
  11. package/dist/alias_hierarchy.js.map +1 -0
  12. package/dist/alias_index.d.ts +60 -0
  13. package/dist/alias_index.js +119 -0
  14. package/dist/alias_index.js.map +1 -0
  15. package/dist/alias_name_archive.d.ts +45 -0
  16. package/dist/alias_name_archive.js +75 -0
  17. package/dist/alias_name_archive.js.map +1 -0
  18. package/dist/bind_alias_category.d.ts +173 -0
  19. package/dist/bind_alias_category.js +417 -0
  20. package/dist/bind_alias_category.js.map +1 -0
  21. package/dist/bind_configuration_methods.d.ts +44 -0
  22. package/dist/bind_configuration_methods.js +175 -0
  23. package/dist/bind_configuration_methods.js.map +1 -0
  24. package/dist/bind_find_alias.d.ts +61 -0
  25. package/dist/bind_find_alias.js +227 -0
  26. package/dist/bind_find_alias.js.map +1 -0
  27. package/dist/index.d.ts +18 -0
  28. package/dist/index.js +64 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/install_alias_names.d.ts +229 -0
  31. package/dist/install_alias_names.js +146 -0
  32. package/dist/install_alias_names.js.map +1 -0
  33. package/dist/last_change.d.ts +87 -0
  34. package/dist/last_change.js +174 -0
  35. package/dist/last_change.js.map +1 -0
  36. package/dist/well_known.d.ts +88 -0
  37. package/dist/well_known.js +93 -0
  38. package/dist/well_known.js.map +1 -0
  39. package/package.json +54 -0
  40. package/source/add_alias.ts +318 -0
  41. package/source/address_space_alias_store.ts +417 -0
  42. package/source/alias_hierarchy.ts +141 -0
  43. package/source/alias_index.ts +127 -0
  44. package/source/alias_name_archive.ts +84 -0
  45. package/source/bind_alias_category.ts +546 -0
  46. package/source/bind_configuration_methods.ts +219 -0
  47. package/source/bind_find_alias.ts +290 -0
  48. package/source/index.ts +74 -0
  49. package/source/install_alias_names.ts +342 -0
  50. package/source/last_change.ts +201 -0
  51. package/source/well_known.ts +101 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022-2024 Sterfive SAS - 833264583 RCS ORLEANS - France (https://www.sterfive.com)
4
+
5
+ Copyright (c) 2014-2022 Etienne Rossignon
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
8
+ this software and associated documentation files (the "Software"), to deal in
9
+ the Software without restriction, including without limitation the rights to
10
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
+ the Software, and to permit persons to whom the Software is furnished to do so,
12
+ subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,361 @@
1
+ # node-opcua-alias-name-server
2
+
3
+ Server-side OPC UA **AliasNames** (OPC 10000-17).
4
+
5
+ These packages let a Server publish **its own** AliasNames and let a Client resolve
6
+ them. They do **not** aggregate AliasNames collected from other Servers: Annex B
7
+ (aggregating Server) and Annex C (GDS) of OPC 10000-17, and the Annex D PubSub change
8
+ notification, are out of scope. Anything that requires knowing about more than one
9
+ Server is not implemented here.
10
+
11
+ see http://node-opcua.github.io/
12
+
13
+ ## Why this exists
14
+
15
+ Every node-opcua Server that loads the standard nodeset already exposes the `Aliases`,
16
+ `TagVariables` and `Topics` Objects, each carrying a MANDATORY `FindAlias` Method that
17
+ is bound to nothing. A conformance tester therefore sees the SDK advertise the
18
+ AliasName feature and then fail its only required Method. This package binds them.
19
+
20
+ Part 17 is, in effect, DNS for an address space: a Client asks `FindAlias("TI101")` and
21
+ gets back the `ExpandedNodeId` values that name resolves to, instead of being configured
22
+ with a raw NodeId. It is the standard bridge between ISA-5.1 style plant tag names and
23
+ explicitly modelled OPC UA Nodes.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install node-opcua-alias-name-server
29
+ ```
30
+
31
+ ## Zero configuration
32
+
33
+ If your NodeSet2.xml already models `AliasNameType` instances, one call is the whole
34
+ integration:
35
+
36
+ ```ts
37
+ import { installAliasNames } from "node-opcua-alias-name-server";
38
+
39
+ await server.initialize();
40
+ await installAliasNames(server); // also declares the ALIAS capability
41
+ await server.start();
42
+ ```
43
+
44
+ **Call it between `initialize()` and `start()`.** The address space exists from
45
+ `initialize()` onwards, and `start()` performs the mDNS/LDS registration that reads the
46
+ capability list — so installing afterwards binds the Methods correctly but registers the
47
+ Server without `ALIAS`.
48
+
49
+ `FindAlias` now answers correctly on `Aliases`, `TagVariables`, `Topics` and every
50
+ vendor subcategory nested below them. The default store reads the address space
51
+ directly, so the model *is* the database — there is nothing to keep in sync.
52
+
53
+ Calling `installAliasNames` twice is a no-op, not a double binding.
54
+
55
+ There is also an address-space-level form, for tests and tools that have no Server:
56
+
57
+ ```ts
58
+ import { installAliasNamesOnAddressSpace } from "node-opcua-alias-name-server";
59
+ await installAliasNamesOnAddressSpace(addressSpace);
60
+ ```
61
+
62
+ ## Declaring aliases from code
63
+
64
+ For a Server whose address space is built programmatically:
65
+
66
+ ```ts
67
+ import { addAlias, removeAlias, WellKnownCategories } from "node-opcua-alias-name-server";
68
+
69
+ addAlias(addressSpace, WellKnownCategories.TagVariables, "TI101", temperatureVariable);
70
+ removeAlias(addressSpace, WellKnownCategories.TagVariables, "TI101");
71
+ ```
72
+
73
+ `addAlias` enforces the rules of clause 6.2 so you cannot build a non-conformant alias:
74
+ the BrowseName's string part equals the DisplayName with an empty locale and no other
75
+ locale, and at least one `AliasFor` Reference exists. It also enforces the category
76
+ restrictions where the target is local and therefore checkable — `TagVariables` accepts
77
+ only Variables (clause 9.3), `Topics` only `PublishedDataSetType` instances or subtypes
78
+ (clause 9.4).
79
+
80
+ **There is deliberately no rename.** Clause 6.2 makes the BrowseName immutable: "If an
81
+ AliasName is to be changed, it shall be a deletion of the old AliasName and the addition
82
+ of the new AliasName", which yields a new NodeId. That is what lets an aggregating Server
83
+ notice the change, so a rename API would quietly break aggregation downstream.
84
+
85
+ Adding the same name twice adds a target to the existing alias rather than creating a
86
+ second node; an exact duplicate of (name, target) is ignored.
87
+
88
+ ## Options
89
+
90
+ | Option | Default | Meaning |
91
+ |---|---|---|
92
+ | `store` | `AddressSpaceAliasStore` | Where aliases come from. Inject your own to back them with a database or an existing tag dictionary. |
93
+ | `maxResults` | `1000` | Beyond this a call answers `Bad_ResponseTooLarge` (clause 6.3.2 Table 4). |
94
+ | `verbose` | `true` | Also bind `FindAliasVerbose` (clause 6.3.3). |
95
+ | `comparator` | insertion order | Result ordering (clause 6.3.2, "best match first"). |
96
+ | `isReadAllowed` | allow all | `(context, categoryNodeId) => boolean \| Promise<boolean>`, consulted per category. |
97
+ | `isWriteAllowed` | **deny all** | Same shape, for the configuration Methods. |
98
+ | `likeOptions` | case sensitive | Passed to the `Like` matcher. |
99
+ | `additionalCategoryRoots` | — | Categories modelled outside the `Aliases` hierarchy. |
100
+ | `categoryProvider` | `defaultCategoryProvider()` | Replace category discovery entirely; may be async. |
101
+ | `advertiseCapability` | `true` | Declare `ALIAS` in the Server's `capabilitiesForMDNS` (OPC 10000-12 Annex D). |
102
+ | `configurationMethods` | `false` | Expose `AddAliasesToCategory` / `DeleteAliasesFromCategory` (CU 5874). |
103
+ | `persistencePath` | — | File backing the persisted `LastChange` (clause 6.3.1). |
104
+ | `lastChangeOnAllCategories` | `true` | Add a `LastChange` Property to every category, not only the root. |
105
+
106
+ **Set `persistencePath` on any Server that Clients cache against.** Without it every
107
+ restart resets `LastChange` to zero, and clause 6.3.1 requires a Client seeing a value
108
+ older than its cache to *clear that cache* — so an unpersisted Server silently orders
109
+ every connected Client to discard a still-valid cache on every restart.
110
+
111
+ ## Extending it
112
+
113
+ Everything installation does is available piecewise, so an advanced Server — a GDS, an
114
+ aggregating Server, a vendor Server with per-customer categories — does not have to
115
+ re-implement a private half.
116
+
117
+ ### Categories created at runtime
118
+
119
+ `installAliasNames` binds what exists when it runs, and is a no-op if called again. A
120
+ category created afterwards would otherwise have an unbound MANDATORY `FindAlias` — the
121
+ exact defect this package removes, reappearing at runtime. Use `addAliasCategory`, which
122
+ creates *and* binds:
123
+
124
+ ```ts
125
+ import { addAliasCategory, WellKnownCategories } from "node-opcua-alias-name-server";
126
+
127
+ const wells = addAliasCategory(addressSpace, WellKnownCategories.TagVariables, "Wells");
128
+ // FindAlias and FindAliasVerbose are already bound, with the options
129
+ // installAliasNames was given
130
+ ```
131
+
132
+ For a category built by hand, bind it with the options installation used:
133
+
134
+ ```ts
135
+ import { bindAliasCategory, getInstalledAliasNames } from "node-opcua-alias-name-server";
136
+
137
+ const installed = getInstalledAliasNames(addressSpace)!;
138
+ bindAliasCategory(addressSpace, myCategory, installed.bindingOptions);
139
+ ```
140
+
141
+ `installAliasNamesOnAddressSpace` calls `bindAliasCategory` in its own loop, so there is
142
+ exactly one binding path and a late category cannot diverge from an installed one. This
143
+ matters most for `FindAliasVerbose`, whose clone-with-reserved-NodeId logic cannot
144
+ sensibly be hand-rolled.
145
+
146
+ ### Dynamic category sets
147
+
148
+ `additionalCategoryRoots` only covers roots known at install time. When the set is
149
+ genuinely dynamic, replace discovery:
150
+
151
+ ```ts
152
+ import { defaultCategoryProvider } from "node-opcua-alias-name-server";
153
+
154
+ await installAliasNames(server, {
155
+ categoryProvider: async (addressSpace) => [
156
+ ...(await defaultCategoryProvider()(addressSpace)),
157
+ ...(await myTenantCategories(addressSpace))
158
+ ]
159
+ });
160
+ ```
161
+
162
+ `additionalCategoryRoots` is expressed through `defaultCategoryProvider`, so the two
163
+ compose rather than being parallel mechanisms.
164
+
165
+ ### Per-category access control
166
+
167
+ `isReadAllowed` receives the category the Method was called on and may return a Promise,
168
+ so "may this user see *this* customer's category" is expressible, and a permission lookup
169
+ may hit a database:
170
+
171
+ ```ts
172
+ await installAliasNames(server, {
173
+ isReadAllowed: async (context, categoryNodeId) => tenantOf(context) === ownerOf(categoryNodeId)
174
+ });
175
+ ```
176
+
177
+ The gate is consulted **per category**, each at most once per call, and the outcome
178
+ differs by how the category was reached:
179
+
180
+ | Situation | Result |
181
+ |---|---|
182
+ | Direct call on a denied category | `Bad_UserAccessDenied` — nothing left to filter |
183
+ | Denied category reached by a recursive search | Omitted; the call still returns `Good` |
184
+
185
+ Absence is the only answer that discloses nothing: an error, or a count that changed,
186
+ would confirm the category exists. `FindAliasVerbose` filters at the same point as
187
+ `FindAlias`, so it cannot leak an `AliasNameCategoryId` or a `ServerUri` for a category
188
+ the plain form would have hidden.
189
+
190
+ `Bad_ResponseTooLarge` is still possible for a gated caller, since the cap is applied to
191
+ the raw scan to keep truncation detectable — but it names no category.
192
+
193
+ OPC 10000-17 defines no security model at all: four `Bad_UserAccessDenied` rows, no
194
+ Security clause, no Roles, and no `RolePermissions` on any Part 17 node in the standard
195
+ nodeset. Every Server has to supply its own rule, which is why this hook exists rather
196
+ than a built-in policy.
197
+
198
+ ### Removing a category
199
+
200
+ The specification does not say what happens to a category's contents, so the rule is
201
+ explicit:
202
+
203
+ ```ts
204
+ removeAliasCategory(addressSpace, tenantCategory); // re-parent (default)
205
+ removeAliasCategory(addressSpace, tenantCategory, { orphans: "cascade" }); // delete with it
206
+ ```
207
+
208
+ `reparent` moves the aliases and subcategories to the parent first, so an alias keeps its
209
+ NodeId and a Client that resolved it keeps resolving it — clause 6.2 makes a NodeId change
210
+ mean "this is a different alias". `cascade` is right when the category itself is being
211
+ retired. The three well-known categories cannot be removed; clause 9 requires them.
212
+
213
+ ### Result ordering
214
+
215
+ Clause 6.3.2 requires "what it recommends as the best match first", and says the criteria
216
+ are Server specific. The examples it gives — the ServerStatus of the Server holding the
217
+ Node, load balancing — only mean anything once more than one Server is involved. A Server
218
+ publishing its own aliases has no basis to prefer one of its own Nodes over another, so
219
+ the default preserves discovery order: deterministic, and therefore stable across calls.
220
+ Supply `comparator` when your Server does have a basis.
221
+
222
+ ### Denial-of-service bounds
223
+
224
+ `FindAlias` is remotely callable, usually by an anonymous session, so both of its inputs
225
+ are bounded:
226
+
227
+ - **The search pattern.** Parsing allocates one element per character, and the transport
228
+ accepts a String up to 16 MB, so patterns over 2048 characters are refused with
229
+ `Bad_InvalidArgument` before anything is allocated. See the cost table in
230
+ `node-opcua-like-matcher`. Adjust with `likeOptions.maxPatternLength`.
231
+ - **The result set.** `maxResults` bounds the *work*, not just the response: the store
232
+ stops collecting one entry past the cap rather than walking the whole hierarchy and
233
+ discarding it. The cap is applied to the raw entries before they are merged by name,
234
+ because merging can reduce the count and would otherwise report a truncated scan as a
235
+ complete answer. A merge that trips the cap is therefore `Bad_ResponseTooLarge` even
236
+ though the merged list would have been short — conservative, and consistent with
237
+ "try new filter and repeat find".
238
+
239
+ ### Category discovery
240
+
241
+ Categories are found by walking down from `Aliases`, which is where clause 9.1 puts them:
242
+ vendors "are free to add additional instances of AliasNameCategoryType under this
243
+ hierarchy". A category modelled anywhere else is not discovered — the address space keeps
244
+ no inverse `HasTypeDefinition` reference, so there is nothing to sweep. Name such a
245
+ category in `additionalCategoryRoots`, otherwise its MANDATORY `FindAlias` stays unbound.
246
+
247
+ ### `FindAliasVerbose` NodeIds
248
+
249
+ The shipped `Opc.Ua.NodeSet2.xml` declares `FindAliasVerbose` on `AliasNameCategoryType`
250
+ but instantiates it on none of the three well-known categories. Upstream nonetheless
251
+ reserves fixed NodeIds for those instances (`i=24054`, `i=24063`, `i=24072`), so
252
+ installation uses them rather than server-assigned ones and an aggregating Server sees
253
+ the NodeId it expects. Vendor subcategories get a server-assigned NodeId, as they must.
254
+
255
+ ## The `ALIAS` capability is declared for you
256
+
257
+ `installAliasNames` adds `ALIAS` to the Server's `capabilitiesForMDNS`
258
+ (OPC 10000-12 Annex D Table D.1). Declaring the capability and installing the feature are
259
+ the same decision, so they happen together — leaving it to each caller to remember means
260
+ it will sometimes be forgotten, and a Server that omits it is simply never discovered by
261
+ anything looking for alias-capable Servers, with nothing reporting the failure.
262
+
263
+ It is idempotent, case-insensitive, and replaces node-opcua's `NA` placeholder rather
264
+ than producing the meaningless `["NA", "ALIAS"]`:
265
+
266
+ | Before | After |
267
+ |---|---|
268
+ | `[]` | `["ALIAS"]` |
269
+ | `["NA"]` | `["ALIAS"]` |
270
+ | `["DA", "HD"]` | `["DA", "HD", "ALIAS"]` |
271
+ | `["Alias"]` | `["Alias"]` — already declared |
272
+
273
+ The normative identifier is `ALIAS`; Part 17's prose writes it `Alias`, and Part 12
274
+ Annex D is the normative source.
275
+
276
+ Pass `advertiseCapability: false` if the Server manages its own capability list, or use
277
+ the helper directly:
278
+
279
+ ```ts
280
+ import { advertiseAliasCapability } from "node-opcua-alias-name-server";
281
+ advertiseAliasCapability(server.capabilitiesForMDNS);
282
+ ```
283
+
284
+ ## `LastChange` (clause 6.3.1)
285
+
286
+ A **`VersionTime`: a UInt32 count of seconds since 2000-01-01T00:00:00Z**, not a
287
+ `DateTime` — which is what every other "last changed" Property in the SDK is, and so the
288
+ easiest thing here to get wrong.
289
+
290
+ All three clause 6.3.1 triggers move it: an alias added or deleted, a category added or
291
+ deleted, and an alias's referenced Nodes changing. Nested categories roll up — a change
292
+ deep in the hierarchy moves every ancestor to the root — and the rollup is applied when
293
+ the change happens, not computed on read, so the Property a Client subscribes to actually
294
+ carries the value.
295
+
296
+ ```ts
297
+ await installAliasNames(server, { persistencePath: "./aliases-lastchange.json" });
298
+ ```
299
+
300
+ The archive is small JSON — a version and a map of category NodeId to VersionTime — and
301
+ is written atomically. A corrupt or future-versioned archive is **reported**, not silently
302
+ treated as "start from zero", because that is the same cache-clearing bug persistence
303
+ exists to prevent.
304
+
305
+ Two things worth designing around:
306
+
307
+ - **Resolution is one second.** Two changes inside the same second are indistinguishable.
308
+ A Client should treat an *equal* `LastChange` as "re-browse to be sure"; only a value
309
+ *older* than the cached one carries clause 6.3.1's "clear the cache" meaning.
310
+ - **Category NodeIds must be stable**, since the archive keys on them.
311
+ `addAliasCategory` therefore derives a string NodeId from the category's path
312
+ (`ns=1;s=Aliases/TagVariables/Unit200`) rather than taking the next free numeric id,
313
+ which would shift whenever an unrelated Node happened to be created first.
314
+
315
+ ## The configuration Methods (clauses 6.3.4, 6.3.5)
316
+
317
+ Off by default. Turning them on exposes `AddAliasesToCategory` and
318
+ `DeleteAliasesFromCategory` on every category — but **every call is denied until
319
+ `isWriteAllowed` says otherwise**:
320
+
321
+ ```ts
322
+ await installAliasNames(server, {
323
+ configurationMethods: true,
324
+ isWriteAllowed: async (context, categoryNodeId) => isEngineer(context)
325
+ });
326
+ ```
327
+
328
+ Read and write are gated independently, and only one of them is safe to open by default:
329
+ `isReadAllowed` allows everyone, `isWriteAllowed` denies everyone.
330
+
331
+ Both Methods report **per item**. The call succeeds and an `ErrorCodes` array parallel to
332
+ `AliasNames` says what happened to each, so one bad entry does not fail the batch. Only
333
+ the argument errors of Tables 11 and 15 — mismatched array sizes, an empty call, a denied
334
+ caller — fail the call itself.
335
+
336
+ `AddAliasesToCategory` follows Table 10: `Bad_NodeIdUnknown` for a missing local target,
337
+ `Bad_NotSupported` for a remote target unless `allowRemoteTargets` is set on the store,
338
+ and `Uncertain_ReferenceOutOfServer` for a remote target when it is — the clause is
339
+ explicit that the uncertain code applies *whether or not* a check was performed, and this
340
+ Server does not check, since that would mean being a Client of the other Server. An exact
341
+ duplicate of (AliasName, target, target Server) is `Good` and ignored, whether already
342
+ stored or repeated within the same call. A null `TargetReferenceType` defaults to
343
+ `AliasFor`, and the `ServerIndex` inside an incoming `ExpandedNodeId` is ignored —
344
+ `TargetServers` is authoritative (Table 9).
345
+
346
+ `DeleteAliasesFromCategory` follows Table 14: `Bad_NotFound` when the name is not there,
347
+ `Bad_InvalidState` when it is not owned by this Server. An entry with no target removes
348
+ every target of that name, removal is all-or-nothing per name, and removing the last
349
+ target removes the `AliasNameType` Object, since clause 7.2 gives it at least one
350
+ ReferencedNode.
351
+
352
+ ## What is not here yet
353
+
354
+ - **Aggregation across Servers** (Annexes B, C) and the **Annex D PubSub change
355
+ notification** — out of scope by design, not pending.
356
+ - **UACTT has not been run.** The sample Server in `node-opcua-alias-name-test` is ready
357
+ for it.
358
+
359
+ ## License
360
+
361
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @module node-opcua-alias-name-server
3
+ *
4
+ * Creating and removing `AliasNameType` instances from code, for Servers whose
5
+ * address space is built programmatically rather than loaded from a NodeSet2.
6
+ */
7
+ import type { BaseNode, IAddressSpace, UAObject } from "node-opcua-address-space-base";
8
+ import { type NodeId } from "node-opcua-nodeid";
9
+ /** Raised when an alias would break a rule of OPC 10000-17. */
10
+ export declare class AliasNameError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ export interface AddAliasOptions {
14
+ /**
15
+ * ReferenceType linking the AliasName to its target. Defaults to `AliasFor`
16
+ * (clause 8.2); a subtype is allowed.
17
+ */
18
+ referenceType?: NodeId | string;
19
+ /**
20
+ * Namespace index for the AliasName's BrowseName. Defaults to the Server's
21
+ * own namespace. Clause 6.2 allows namespace 1 or a vendor namespace, and
22
+ * requires Clients to ignore it when comparing.
23
+ */
24
+ namespaceIndex?: number;
25
+ /**
26
+ * Accept a target this Server cannot resolve.
27
+ *
28
+ * For a Node on **another** Server, which by definition is not in this
29
+ * address space: the existence check and the clause 9.3 / 9.4 category
30
+ * restrictions are both skipped, because neither can be evaluated without
31
+ * being a Client of that Server. Used by `AddAliasesToCategory` when
32
+ * `TargetServers` names a remote Server (clause 6.3.4).
33
+ */
34
+ allowUnresolvedTarget?: boolean;
35
+ }
36
+ /**
37
+ * Add an `AliasNameType` instance under `category`, naming `target`.
38
+ *
39
+ * Enforces clause 6.2:
40
+ *
41
+ * - the string part of the BrowseName equals the DisplayName, with an empty
42
+ * locale and no other locale;
43
+ * - the Object has at least one `AliasFor` Reference;
44
+ * - the BrowseName is immutable once created. There is deliberately no rename:
45
+ * "If an AliasName is to be changed, it shall be a deletion of the old
46
+ * AliasName and the addition of the new AliasName", which yields a new NodeId
47
+ * and is what lets an aggregating Server notice the change.
48
+ *
49
+ * and the category restrictions of clauses 9.3 and 9.4, where the target is
50
+ * local and therefore checkable.
51
+ *
52
+ * Adding the same (name, target) twice returns the existing node rather than
53
+ * creating a duplicate.
54
+ */
55
+ export declare function addAlias(addressSpace: IAddressSpace, category: UAObject | NodeId, aliasName: string, target: BaseNode | NodeId, options?: AddAliasOptions): UAObject;
56
+ /**
57
+ * Remove one target from an alias, or the whole alias when no target is given.
58
+ *
59
+ * Removing the last target deletes the `AliasNameType` node: clause 7.2 says the
60
+ * ReferencedNodes array always has at least one entry, so an alias naming
61
+ * nothing cannot be represented.
62
+ *
63
+ * @returns true when something was removed.
64
+ */
65
+ export declare function removeAlias(addressSpace: IAddressSpace, category: UAObject | NodeId, aliasName: string, target?: BaseNode | NodeId): boolean;
66
+ /**
67
+ * The `AliasNameType` instance with this name Organized by `category`, if any.
68
+ *
69
+ * Compares the string part only, ignoring the namespace, as clause 6.2 requires.
70
+ * Backed by a per-category index — see {@link lookupAlias} for why a plain scan
71
+ * was not good enough.
72
+ */
73
+ export declare function findAlias(addressSpace: IAddressSpace, category: UAObject, aliasName: string): UAObject | null;