node-opcua-uanodeset-rdf 2.182.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.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Collecting an `RdfModel` over a session.
3
+ *
4
+ * This is what makes a server exportable when its nodeset file is not available, which for a
5
+ * vendor's own companion specification is often the only way to get the model into a graph store
6
+ * at all.
7
+ *
8
+ * It is not the equal of `addressSpaceToRdfModel`, and the differences are not bugs to be fixed
9
+ * later. OPC UA has no service that lists the nodes of a namespace, so the model is whatever a
10
+ * crawl from Root reaches; `SymbolicName` is NodeSet2 metadata rather than an attribute, so it is
11
+ * simply gone; and `NamespaceMetadataType` carries no list of required models, so the ontology
12
+ * node has no `owl:imports` unless the caller supplies one. "What a session cannot tell you" in
13
+ * `VOCABULARY.md` is the full list.
14
+ */
15
+ import { type IBasicSessionBrowseAsyncMultiple, type IBasicSessionBrowseNextAsyncMultiple, type IBasicSessionReadAsyncMultiple } from "node-opcua-pseudo-session";
16
+ import type { RdfModel } from "./model.js";
17
+ /**
18
+ * what collecting a model asks of a session, written out rather than named.
19
+ *
20
+ * Three services and no more: browse, follow the continuation point a browse hands back, and read
21
+ * attributes. Anything satisfying that will do, and both things that do are useful here: a
22
+ * `ClientSession` against a real server, and a `PseudoSession` over an address space. Spelling the
23
+ * intersection out is also what keeps `create`, `close`, subscriptions and the rest of a session's
24
+ * surface out of the contract, so a caller can pass something far smaller than a session.
25
+ */
26
+ export type RdfSession = IBasicSessionBrowseAsyncMultiple & IBasicSessionBrowseNextAsyncMultiple & IBasicSessionReadAsyncMultiple;
27
+ /** the model a namespace of a server describes, as far as a session can see it */
28
+ export declare function sessionToRdfModel(session: RdfSession, modelUri?: string): Promise<RdfModel>;
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ /**
3
+ * Collecting an `RdfModel` over a session.
4
+ *
5
+ * This is what makes a server exportable when its nodeset file is not available, which for a
6
+ * vendor's own companion specification is often the only way to get the model into a graph store
7
+ * at all.
8
+ *
9
+ * It is not the equal of `addressSpaceToRdfModel`, and the differences are not bugs to be fixed
10
+ * later. OPC UA has no service that lists the nodes of a namespace, so the model is whatever a
11
+ * crawl from Root reaches; `SymbolicName` is NodeSet2 metadata rather than an attribute, so it is
12
+ * simply gone; and `NamespaceMetadataType` carries no list of required models, so the ontology
13
+ * node has no `owl:imports` unless the caller supplies one. "What a session cannot tell you" in
14
+ * `VOCABULARY.md` is the full list.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.sessionToRdfModel = sessionToRdfModel;
18
+ const node_opcua_data_model_1 = require("node-opcua-data-model");
19
+ const node_opcua_nodeid_1 = require("node-opcua-nodeid");
20
+ const node_opcua_pseudo_session_1 = require("node-opcua-pseudo-session");
21
+ const node_opcua_service_browse_1 = require("node-opcua-service-browse");
22
+ /** `Root`, where a crawl has to start, since nothing lists the nodes of a namespace */
23
+ const ROOT = (0, node_opcua_nodeid_1.resolveNodeId)("RootFolder");
24
+ /** `Server_NamespaceArray` */
25
+ const NAMESPACE_ARRAY = (0, node_opcua_nodeid_1.resolveNodeId)("Server_NamespaceArray");
26
+ /** `Server_Namespaces`, the folder of `NamespaceMetadataType` objects */
27
+ const NAMESPACES_FOLDER = (0, node_opcua_nodeid_1.coerceNodeId)("i=11715");
28
+ /** `References`, so one browse per node covers every relation in both directions */
29
+ const REFERENCES = (0, node_opcua_nodeid_1.resolveNodeId)("References");
30
+ /** BrowseName, NodeClass and TypeDefinition of each target, which is all the mapper needs to name it */
31
+ const RESULT_MASK = 0x3f;
32
+ /**
33
+ * how many nodes to read attributes for at once.
34
+ *
35
+ * `browseAll` reads the server's own browse limits, but nothing here reads `MaxNodesPerRead`:
36
+ * honouring a server's operation limits belongs in a client that specialises in it, not in a
37
+ * bare exporter. This is a figure every server in practice accepts.
38
+ */
39
+ const READ_CHUNK = 100;
40
+ function stubOf(reference) {
41
+ return {
42
+ nodeId: reference.nodeId,
43
+ nodeClass: reference.nodeClass,
44
+ browseName: reference.browseName,
45
+ references: []
46
+ };
47
+ }
48
+ /** the namespace array, which is the only thing that turns a uri into the index NodeIds use */
49
+ async function readNamespaceArray(session) {
50
+ const [dataValue] = await session.read([{ nodeId: NAMESPACE_ARRAY, attributeId: node_opcua_data_model_1.AttributeIds.Value }]);
51
+ const value = dataValue?.value?.value;
52
+ if (!value?.length) {
53
+ throw new Error("the server does not expose Server_NamespaceArray, so no namespace can be named");
54
+ }
55
+ return [...value];
56
+ }
57
+ /**
58
+ * every node a crawl from Root reaches, with the references of those in the target namespace.
59
+ *
60
+ * References are kept only for the target namespace because they are all the mapper reads: for
61
+ * any other node it asks for a name. That also bounds memory on a server whose base namespace is
62
+ * far larger than the model being exported.
63
+ */
64
+ async function crawl(session, targetIndex) {
65
+ const known = new Map();
66
+ const nodes = [];
67
+ const rootReference = { nodeId: ROOT, nodeClass: node_opcua_data_model_1.NodeClass.Object, browseName: new node_opcua_data_model_1.QualifiedName({ name: "Root" }) };
68
+ known.set(ROOT.toString(), { ...rootReference, references: [] });
69
+ let frontier = [ROOT];
70
+ const visited = new Set([ROOT.toString()]);
71
+ while (frontier.length) {
72
+ const results = await (0, node_opcua_pseudo_session_1.browseAll)(session, frontier.map((nodeId) => ({
73
+ nodeId,
74
+ browseDirection: node_opcua_service_browse_1.BrowseDirection.Both,
75
+ referenceTypeId: REFERENCES,
76
+ includeSubtypes: true,
77
+ nodeClassMask: 0,
78
+ resultMask: RESULT_MASK
79
+ })));
80
+ const next = [];
81
+ for (let index = 0; index < frontier.length; index++) {
82
+ const from = frontier[index];
83
+ const references = results[index]?.references ?? [];
84
+ const owner = known.get(from.toString());
85
+ for (const reference of references) {
86
+ const key = reference.nodeId.toString();
87
+ if (!known.has(key))
88
+ known.set(key, stubOf(reference));
89
+ if (owner && from.namespace === targetIndex) {
90
+ owner.references.push({
91
+ referenceTypeId: reference.referenceTypeId,
92
+ isForward: reference.isForward,
93
+ targetId: reference.nodeId
94
+ });
95
+ }
96
+ if (!visited.has(key) && reference.isForward) {
97
+ visited.add(key);
98
+ next.push(reference.nodeId);
99
+ }
100
+ }
101
+ if (owner && from.namespace === targetIndex) {
102
+ nodes.push(owner);
103
+ // a node's own type definition is the target of its forward HasTypeDefinition
104
+ // edge. ReferenceDescription.typeDefinition is the *target's* type, not this
105
+ // node's, and reading it as this node's silently retypes half the model.
106
+ const typeDefinition = references.find((reference) => reference.isForward && isHasTypeDefinition(reference))?.nodeId;
107
+ if (typeDefinition)
108
+ owner.typeDefinition = typeDefinition;
109
+ const supertype = references.find((reference) => !reference.isForward && isHasSubtype(reference));
110
+ if (supertype)
111
+ owner.subtypeOf = supertype.nodeId;
112
+ }
113
+ }
114
+ frontier = next;
115
+ }
116
+ return { nodes, known };
117
+ }
118
+ const HAS_SUBTYPE = (0, node_opcua_nodeid_1.resolveNodeId)("HasSubtype");
119
+ const HAS_TYPE_DEFINITION = (0, node_opcua_nodeid_1.resolveNodeId)("HasTypeDefinition");
120
+ const isHasSubtype = (reference) => reference.referenceTypeId.toString() === HAS_SUBTYPE.toString();
121
+ const isHasTypeDefinition = (reference) => reference.referenceTypeId.toString() === HAS_TYPE_DEFINITION.toString();
122
+ /**
123
+ * the reference-type hierarchy, which `isHierarchical` walks and a stub does not carry.
124
+ *
125
+ * Reference types are few, and almost all of them belong to the base namespace, so this is a
126
+ * handful of browses however large the model is.
127
+ */
128
+ async function fillReferenceTypeChains(session, known) {
129
+ const pending = () => [...known.values()].filter((node) => node.nodeClass === node_opcua_data_model_1.NodeClass.ReferenceType && node.subtypeOf === undefined);
130
+ let batch = pending();
131
+ const asked = new Set();
132
+ while (batch.length) {
133
+ const fresh = batch.filter((node) => !asked.has(node.nodeId.toString()));
134
+ if (!fresh.length)
135
+ return;
136
+ for (const node of fresh)
137
+ asked.add(node.nodeId.toString());
138
+ const results = await (0, node_opcua_pseudo_session_1.browseAll)(session, fresh.map((node) => ({
139
+ nodeId: node.nodeId,
140
+ browseDirection: node_opcua_service_browse_1.BrowseDirection.Inverse,
141
+ referenceTypeId: HAS_SUBTYPE,
142
+ includeSubtypes: false,
143
+ nodeClassMask: 0,
144
+ resultMask: RESULT_MASK
145
+ })));
146
+ for (let index = 0; index < fresh.length; index++) {
147
+ const reference = results[index]?.references?.[0];
148
+ if (!reference)
149
+ continue;
150
+ fresh[index].subtypeOf = reference.nodeId;
151
+ const key = reference.nodeId.toString();
152
+ if (!known.has(key))
153
+ known.set(key, stubOf(reference));
154
+ }
155
+ batch = pending();
156
+ }
157
+ }
158
+ /** the attributes a node carries that no browse reports */
159
+ async function readAttributes(session, nodes) {
160
+ const wanted = [];
161
+ for (const node of nodes) {
162
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.Description });
163
+ if (node.nodeClass === node_opcua_data_model_1.NodeClass.Variable || node.nodeClass === node_opcua_data_model_1.NodeClass.VariableType) {
164
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.DataType });
165
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.ValueRank });
166
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.ArrayDimensions });
167
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.Value });
168
+ }
169
+ if (node.nodeClass === node_opcua_data_model_1.NodeClass.ReferenceType) {
170
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.Symmetric });
171
+ wanted.push({ node, attributeId: node_opcua_data_model_1.AttributeIds.InverseName });
172
+ }
173
+ }
174
+ for (let start = 0; start < wanted.length; start += READ_CHUNK) {
175
+ const chunk = wanted.slice(start, start + READ_CHUNK);
176
+ const dataValues = await session.read(chunk.map(({ node, attributeId }) => ({ nodeId: node.nodeId, attributeId })));
177
+ for (let index = 0; index < chunk.length; index++) {
178
+ const dataValue = dataValues[index];
179
+ if (!dataValue?.statusCode.isGood())
180
+ continue;
181
+ const { node, attributeId } = chunk[index];
182
+ const value = dataValue.value?.value;
183
+ switch (attributeId) {
184
+ case node_opcua_data_model_1.AttributeIds.Description:
185
+ if (value instanceof node_opcua_data_model_1.LocalizedText && value.text)
186
+ node.description = value;
187
+ break;
188
+ case node_opcua_data_model_1.AttributeIds.DataType:
189
+ if (value)
190
+ node.dataType = value;
191
+ break;
192
+ case node_opcua_data_model_1.AttributeIds.ValueRank:
193
+ if (typeof value === "number")
194
+ node.valueRank = value;
195
+ break;
196
+ case node_opcua_data_model_1.AttributeIds.ArrayDimensions:
197
+ if (value)
198
+ node.arrayDimensions = Array.from(value);
199
+ break;
200
+ case node_opcua_data_model_1.AttributeIds.Value:
201
+ if (dataValue.value)
202
+ node.value = dataValue.value;
203
+ break;
204
+ case node_opcua_data_model_1.AttributeIds.Symmetric:
205
+ node.symmetric = !!value;
206
+ break;
207
+ case node_opcua_data_model_1.AttributeIds.InverseName:
208
+ if (value instanceof node_opcua_data_model_1.LocalizedText && value.text)
209
+ node.inverseName = value;
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+ }
215
+ }
216
+ }
217
+ /**
218
+ * the version and publication date a `NamespaceMetadataType` object states.
219
+ *
220
+ * There is no `RequiredModels` here to read: the type does not have one. That is why a
221
+ * session-sourced ontology node carries no `owl:imports`.
222
+ */
223
+ async function readNamespaceMetadata(session, namespaceUri) {
224
+ try {
225
+ const folder = await (0, node_opcua_pseudo_session_1.browseAll)(session, {
226
+ nodeId: NAMESPACES_FOLDER,
227
+ browseDirection: node_opcua_service_browse_1.BrowseDirection.Forward,
228
+ referenceTypeId: REFERENCES,
229
+ includeSubtypes: true,
230
+ nodeClassMask: 0,
231
+ resultMask: RESULT_MASK
232
+ });
233
+ const entry = folder.references?.find((reference) => reference.browseName.name === namespaceUri);
234
+ if (!entry)
235
+ return {};
236
+ const properties = await (0, node_opcua_pseudo_session_1.browseAll)(session, {
237
+ nodeId: entry.nodeId,
238
+ browseDirection: node_opcua_service_browse_1.BrowseDirection.Forward,
239
+ referenceTypeId: REFERENCES,
240
+ includeSubtypes: true,
241
+ nodeClassMask: 0,
242
+ resultMask: RESULT_MASK
243
+ });
244
+ const find = (name) => properties.references?.find((reference) => reference.browseName.name === name)?.nodeId;
245
+ const versionId = find("NamespaceVersion");
246
+ const publicationId = find("NamespacePublicationDate");
247
+ if (!versionId && !publicationId)
248
+ return {};
249
+ const dataValues = await session.read([versionId, publicationId]
250
+ .filter((nodeId) => !!nodeId)
251
+ .map((nodeId) => ({ nodeId, attributeId: node_opcua_data_model_1.AttributeIds.Value })));
252
+ const out = {};
253
+ let cursor = 0;
254
+ if (versionId) {
255
+ const value = dataValues[cursor++]?.value?.value;
256
+ if (typeof value === "string" && value)
257
+ out.version = value;
258
+ }
259
+ if (publicationId) {
260
+ const value = dataValues[cursor]?.value?.value;
261
+ if (value instanceof Date)
262
+ out.publicationDate = value;
263
+ }
264
+ return out;
265
+ }
266
+ catch {
267
+ // a server without Server/Namespaces states no version, which is not an error for an export
268
+ return {};
269
+ }
270
+ }
271
+ /** the model a namespace of a server describes, as far as a session can see it */
272
+ async function sessionToRdfModel(session, modelUri) {
273
+ const namespaceUris = await readNamespaceArray(session);
274
+ const targetIndex = modelUri ? namespaceUris.indexOf(modelUri) : namespaceUris.length - 1;
275
+ if (targetIndex < 0) {
276
+ throw new Error(`the server holds no namespace ${modelUri}`);
277
+ }
278
+ const namespaceUri = namespaceUris[targetIndex];
279
+ const { nodes, known } = await crawl(session, targetIndex);
280
+ await fillReferenceTypeChains(session, known);
281
+ await readAttributes(session, nodes);
282
+ const metadata = await readNamespaceMetadata(session, namespaceUri);
283
+ // a crawl reaches nodes in the order the hierarchy happens to lay them out, which is not an
284
+ // order anyone chose. Sorting makes two exports of the same server comparable.
285
+ nodes.sort((a, b) => a.nodeId.toString().localeCompare(b.nodeId.toString()));
286
+ return {
287
+ namespaceUris,
288
+ target: { index: targetIndex, namespaceUri, version: metadata.version, publicationDate: metadata.publicationDate },
289
+ nodes,
290
+ resolve: (nodeId) => known.get(nodeId.toString())
291
+ };
292
+ }
293
+ //# sourceMappingURL=from_session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"from_session.js","sourceRoot":"","sources":["../source/from_session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * An OPC UA information model as JSON-LD, for loading into a SPARQL store.
3
+ *
4
+ * This is unlike the Annex I formats in every way that matters, which is why it lives in a
5
+ * package of its own.
6
+ *
7
+ * It is **not in the specification**. OPC 10000-6 Annex I runs I.1 to I.24 and has no RDF
8
+ * section. The vocabulary belongs to the reference implementation, whose `rdf_prototype.md` no
9
+ * longer describes what it emits, so its output is the specification. What we reverse-engineered
10
+ * is written down in `VOCABULARY.md` beside this file.
11
+ *
12
+ * It is **one-way**. There is no reader, so none of the checks that make the Annex I formats
13
+ * trustworthy apply: no round trip, no fixpoint, no digest equivalence. A diff against the
14
+ * reference implementation is the only oracle, which is why matching it exactly matters more here
15
+ * than for a format that can be checked against itself.
16
+ *
17
+ * There are two ways in, and each has an entry point of its own so that neither drags in what
18
+ * only the other needs:
19
+ *
20
+ * - `node-opcua-uanodeset-rdf/address-space` is the complete source, and the one to prefer where
21
+ * the model was loaded from its NodeSet2 document.
22
+ * - `node-opcua-uanodeset-rdf/session` works against a server whose nodeset file you do not have,
23
+ * which is the only way to get a vendor's model into a graph store at all. It never mentions
24
+ * `node-opcua-address-space`, so a client application does not load it.
25
+ *
26
+ * This module is both, for callers that want both.
27
+ */
28
+ export * from "./address_space.js";
29
+ export { type RdfSession, sessionToRdfModel } from "./from_session.js";
30
+ export { sessionToJsonLd, sessionToJsonLdText } from "./session.js";
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * An OPC UA information model as JSON-LD, for loading into a SPARQL store.
4
+ *
5
+ * This is unlike the Annex I formats in every way that matters, which is why it lives in a
6
+ * package of its own.
7
+ *
8
+ * It is **not in the specification**. OPC 10000-6 Annex I runs I.1 to I.24 and has no RDF
9
+ * section. The vocabulary belongs to the reference implementation, whose `rdf_prototype.md` no
10
+ * longer describes what it emits, so its output is the specification. What we reverse-engineered
11
+ * is written down in `VOCABULARY.md` beside this file.
12
+ *
13
+ * It is **one-way**. There is no reader, so none of the checks that make the Annex I formats
14
+ * trustworthy apply: no round trip, no fixpoint, no digest equivalence. A diff against the
15
+ * reference implementation is the only oracle, which is why matching it exactly matters more here
16
+ * than for a format that can be checked against itself.
17
+ *
18
+ * There are two ways in, and each has an entry point of its own so that neither drags in what
19
+ * only the other needs:
20
+ *
21
+ * - `node-opcua-uanodeset-rdf/address-space` is the complete source, and the one to prefer where
22
+ * the model was loaded from its NodeSet2 document.
23
+ * - `node-opcua-uanodeset-rdf/session` works against a server whose nodeset file you do not have,
24
+ * which is the only way to get a vendor's model into a graph store at all. It never mentions
25
+ * `node-opcua-address-space`, so a client application does not load it.
26
+ *
27
+ * This module is both, for callers that want both.
28
+ */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
41
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.sessionToJsonLdText = exports.sessionToJsonLd = exports.sessionToRdfModel = void 0;
45
+ __exportStar(require("./address_space.js"), exports);
46
+ var from_session_js_1 = require("./from_session.js");
47
+ Object.defineProperty(exports, "sessionToRdfModel", { enumerable: true, get: function () { return from_session_js_1.sessionToRdfModel; } });
48
+ var session_js_1 = require("./session.js");
49
+ Object.defineProperty(exports, "sessionToJsonLd", { enumerable: true, get: function () { return session_js_1.sessionToJsonLd; } });
50
+ Object.defineProperty(exports, "sessionToJsonLdText", { enumerable: true, get: function () { return session_js_1.sessionToJsonLdText; } });
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;;;;;;;;;;;;;;;AAEH,qDAAmC;AACnC,qDAAuE;AAA7C,oHAAA,iBAAiB,OAAA;AAC3C,2CAAoE;AAA3D,6GAAA,eAAe,OAAA;AAAE,iHAAA,mBAAmB,OAAA"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * What the JSON-LD mapper needs to know about a model, and nothing more.
3
+ *
4
+ * The mapper used to read an `IAddressSpace` directly, which tied it to a source that answers
5
+ * synchronously and at random. A session answers asynchronously and in batches, and rewriting the
6
+ * mapper around `await` would have turned four passes over the nodes into thousands of round
7
+ * trips. So collection and mapping are separated instead: a collector fills this structure, the
8
+ * mapper reads it, and the mapper never awaits.
9
+ *
10
+ * The structure is deliberately close to what OPC UA can actually tell you about a node, since
11
+ * one of its two collectors has only Browse and Read to work with.
12
+ */
13
+ import type { LocalizedText, NodeClass, QualifiedName } from "node-opcua-data-model";
14
+ import type { NodeId } from "node-opcua-nodeid";
15
+ import type { Variant } from "node-opcua-variant";
16
+ /** one edge, in the direction the source states it */
17
+ export interface RdfReference {
18
+ referenceTypeId: NodeId;
19
+ isForward: boolean;
20
+ targetId: NodeId;
21
+ }
22
+ /** a model the exporter names in its own right */
23
+ export interface RdfModelReference {
24
+ modelUri: string;
25
+ version?: string;
26
+ publicationDate?: Date;
27
+ }
28
+ /**
29
+ * one node.
30
+ *
31
+ * A node the exporter merely has to *name* (a supertype in another model, say) needs only
32
+ * `nodeId`, `nodeClass` and `browseName`; the rest may be absent, and `references` may be empty.
33
+ * Only the nodes of the exported namespace are filled in completely.
34
+ */
35
+ export interface RdfNode {
36
+ nodeId: NodeId;
37
+ nodeClass: NodeClass;
38
+ browseName: QualifiedName;
39
+ references: RdfReference[];
40
+ description?: LocalizedText;
41
+ /**
42
+ * NodeSet2 metadata, not an OPC UA attribute, so a session-sourced model never carries it.
43
+ * See "What a session cannot tell you" in VOCABULARY.md.
44
+ */
45
+ symbolicName?: string;
46
+ typeDefinition?: NodeId;
47
+ /** Variable and VariableType */
48
+ dataType?: NodeId;
49
+ valueRank?: number;
50
+ arrayDimensions?: number[] | null;
51
+ value?: Variant;
52
+ /** ReferenceType */
53
+ inverseName?: LocalizedText;
54
+ symmetric?: boolean;
55
+ subtypeOf?: NodeId;
56
+ }
57
+ /** the namespace being exported, and what the ontology node says about it */
58
+ export interface RdfTarget {
59
+ index: number;
60
+ namespaceUri: string;
61
+ version?: string;
62
+ publicationDate?: Date;
63
+ requiredModels?: RdfModelReference[];
64
+ }
65
+ /** everything the mapper reads */
66
+ export interface RdfModel {
67
+ /** index to uri, as the source's own namespace array orders it */
68
+ namespaceUris: string[];
69
+ target: RdfTarget;
70
+ /** the exported namespace's nodes, in the order they are written */
71
+ nodes: RdfNode[];
72
+ /**
73
+ * any node the mapper may have to name, including nodes of other models.
74
+ *
75
+ * An address space answers this from itself on demand; a session collector has to have
76
+ * fetched the answer already, which is why the return is a node rather than a promise.
77
+ */
78
+ resolve(nodeId: NodeId): RdfNode | undefined;
79
+ }
package/dist/model.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * What the JSON-LD mapper needs to know about a model, and nothing more.
4
+ *
5
+ * The mapper used to read an `IAddressSpace` directly, which tied it to a source that answers
6
+ * synchronously and at random. A session answers asynchronously and in batches, and rewriting the
7
+ * mapper around `await` would have turned four passes over the nodes into thousands of round
8
+ * trips. So collection and mapping are separated instead: a collector fills this structure, the
9
+ * mapper reads it, and the mapper never awaits.
10
+ *
11
+ * The structure is deliberately close to what OPC UA can actually tell you about a node, since
12
+ * one of its two collectors has only Browse and Read to work with.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../source/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The session entry point: `node-opcua-uanodeset-rdf/session`.
3
+ *
4
+ * Nothing reachable from here mentions `node-opcua-address-space`, at the type level or the
5
+ * runtime one, so a client application can export a server's model without loading the address
6
+ * space machinery it has no other use for. That is the whole reason this is a separate entry
7
+ * point rather than a second function in the root one.
8
+ *
9
+ * What a session cannot tell you is listed in `VOCABULARY.md`; the short version is that OPC UA
10
+ * has no service listing the nodes of a namespace, `SymbolicName` is not an attribute, and
11
+ * `NamespaceMetadataType` states no required models.
12
+ */
13
+ import { type RdfSession } from "./from_session.js";
14
+ import { type JsonLdOptions } from "./to_jsonld.js";
15
+ export { type RdfSession, sessionToRdfModel } from "./from_session.js";
16
+ export type { RdfModel, RdfModelReference, RdfNode, RdfReference, RdfTarget } from "./model.js";
17
+ export { type JsonLdOptions, modelToJsonLd, OPCUA_NAMESPACE, prefixOfNamespace, UARDF } from "./to_jsonld.js";
18
+ /**
19
+ * a namespace of a server as a JSON-LD document.
20
+ *
21
+ * The session may be a real client session, or a `PseudoSession` over an address space. The
22
+ * latter is what makes the two paths comparable: the same model collected two ways, where the
23
+ * difference is exactly what a session cannot carry.
24
+ */
25
+ export declare function sessionToJsonLd(session: RdfSession, options?: JsonLdOptions): Promise<Record<string, unknown>>;
26
+ /** a namespace of a server as a JSON-LD document, serialised */
27
+ export declare function sessionToJsonLdText(session: RdfSession, options?: JsonLdOptions): Promise<string>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ /**
3
+ * The session entry point: `node-opcua-uanodeset-rdf/session`.
4
+ *
5
+ * Nothing reachable from here mentions `node-opcua-address-space`, at the type level or the
6
+ * runtime one, so a client application can export a server's model without loading the address
7
+ * space machinery it has no other use for. That is the whole reason this is a separate entry
8
+ * point rather than a second function in the root one.
9
+ *
10
+ * What a session cannot tell you is listed in `VOCABULARY.md`; the short version is that OPC UA
11
+ * has no service listing the nodes of a namespace, `SymbolicName` is not an attribute, and
12
+ * `NamespaceMetadataType` states no required models.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.UARDF = exports.prefixOfNamespace = exports.OPCUA_NAMESPACE = exports.modelToJsonLd = exports.sessionToRdfModel = void 0;
16
+ exports.sessionToJsonLd = sessionToJsonLd;
17
+ exports.sessionToJsonLdText = sessionToJsonLdText;
18
+ const from_session_js_1 = require("./from_session.js");
19
+ const to_jsonld_js_1 = require("./to_jsonld.js");
20
+ var from_session_js_2 = require("./from_session.js");
21
+ Object.defineProperty(exports, "sessionToRdfModel", { enumerable: true, get: function () { return from_session_js_2.sessionToRdfModel; } });
22
+ var to_jsonld_js_2 = require("./to_jsonld.js");
23
+ Object.defineProperty(exports, "modelToJsonLd", { enumerable: true, get: function () { return to_jsonld_js_2.modelToJsonLd; } });
24
+ Object.defineProperty(exports, "OPCUA_NAMESPACE", { enumerable: true, get: function () { return to_jsonld_js_2.OPCUA_NAMESPACE; } });
25
+ Object.defineProperty(exports, "prefixOfNamespace", { enumerable: true, get: function () { return to_jsonld_js_2.prefixOfNamespace; } });
26
+ Object.defineProperty(exports, "UARDF", { enumerable: true, get: function () { return to_jsonld_js_2.UARDF; } });
27
+ /**
28
+ * a namespace of a server as a JSON-LD document.
29
+ *
30
+ * The session may be a real client session, or a `PseudoSession` over an address space. The
31
+ * latter is what makes the two paths comparable: the same model collected two ways, where the
32
+ * difference is exactly what a session cannot carry.
33
+ */
34
+ async function sessionToJsonLd(session, options = {}) {
35
+ return (0, to_jsonld_js_1.modelToJsonLd)(await (0, from_session_js_1.sessionToRdfModel)(session, options.modelUri), options);
36
+ }
37
+ /** a namespace of a server as a JSON-LD document, serialised */
38
+ async function sessionToJsonLdText(session, options = {}) {
39
+ return `${JSON.stringify(await sessionToJsonLd(session, options), null, 2)}\n`;
40
+ }
41
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../source/session.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAgBH,0CAEC;AAGD,kDAEC;AArBD,uDAAuE;AACvE,iDAAmE;AAEnE,qDAAuE;AAA7C,oHAAA,iBAAiB,OAAA;AAE3C,+CAA8G;AAAjF,6GAAA,aAAa,OAAA;AAAE,+GAAA,eAAe,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAAE,qGAAA,KAAK,OAAA;AAErF;;;;;;GAMG;AACI,KAAK,UAAU,eAAe,CAAC,OAAmB,EAAE,UAAyB,EAAE;IAClF,OAAO,IAAA,4BAAa,EAAC,MAAM,IAAA,mCAAiB,EAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;AACtF,CAAC;AAED,gEAAgE;AACzD,KAAK,UAAU,mBAAmB,CAAC,OAAmB,EAAE,UAAyB,EAAE;IACtF,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AACnF,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The vocabulary, and the mapping from a collected model to the JSON-LD document.
3
+ *
4
+ * Everything here is synchronous and reads only `RdfModel`, so the same code serves a model
5
+ * collected from an address space and one collected over a session. The vocabulary is the
6
+ * reference implementation's; `VOCABULARY.md` beside this file records what reading its output
7
+ * established.
8
+ */
9
+ import type { RdfModel } from "./model.js";
10
+ /** the vocabulary the reference implementation writes its own terms under */
11
+ export declare const UARDF = "http://opcfoundation.org/rdf/uacore#";
12
+ /** the OPC UA base namespace, always abbreviated `opcua` */
13
+ export declare const OPCUA_NAMESPACE = "http://opcfoundation.org/UA/";
14
+ /** the prefix a namespace uri is abbreviated to: its last non-empty segment, lowercased */
15
+ export declare function prefixOfNamespace(uri: string): string;
16
+ export interface JsonLdOptions {
17
+ /** the model to export; the default is the last namespace the address space holds */
18
+ modelUri?: string;
19
+ /**
20
+ * two fields of the ontology node the address space does not retain.
21
+ *
22
+ * They are written in the source document's `Models` element and dropped by the loader, so a
23
+ * caller that wants them in the output supplies them again.
24
+ */
25
+ model?: {
26
+ xmlSchemaUri?: string;
27
+ modelVersion?: string;
28
+ };
29
+ }
30
+ /** a collected model as a JSON-LD document */
31
+ export declare function modelToJsonLd(model: RdfModel, options?: JsonLdOptions): Record<string, unknown>;