node-opcua-client-browser 2.177.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.
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,42 @@
1
+ # node-opcua-client-browser
2
+
3
+ OPC UA client SDK for modern browsers, using the **WebSocket transport mapping**
4
+ defined in OPC UA Part 6, §7.5.
5
+
6
+ > **Status — scaffold only.**
7
+ > This initial drop establishes the package layout, build toolchain, and
8
+ > Playwright E2E harness. The WebSocket transport, the `createBrowserClient`
9
+ > helper, and the prebuilt browser bundle arrive in follow-up PRs. Consult
10
+ > `openspec/changes/add-browser-client-wsopcua/` for the canonical scope and
11
+ > roadmap.
12
+
13
+ ## Planned scope (upcoming PRs)
14
+
15
+ - Endpoint URL schemes: `opc.ws://`, `opc.wss://`, `ws://`, `wss://`
16
+ - Security policies: `None`, `Basic256Sha256` (SignAndEncrypt)
17
+ - User identity tokens: Anonymous, UserName/Password (password encrypted under
18
+ `Basic256Sha256` when applicable)
19
+ - Services: Read / Write / CreateSubscription / CreateMonitoredItems / Publish
20
+
21
+ ## What is in this scaffold PR
22
+
23
+ - Package skeleton (`package.json`, `tsconfig*.json`).
24
+ - An empty `source/index.ts` that just exports a `VERSION` string; subsequent
25
+ PRs add the actual transport implementation.
26
+ - A Playwright test harness that builds a minimal test page with esbuild,
27
+ serves it over a local HTTP server, and loads it headless in Chromium.
28
+ - One smoke spec (`test/e2e/smoke.spec.ts`) that asserts the test page loads
29
+ and the `node-opcua-client-browser` module evaluates. Later PRs replace
30
+ this with full OPC UA Read / Write / Subscribe flows over `opc.ws` and
31
+ `opc.wss` against a demo server.
32
+
33
+ ## Running the smoke test
34
+
35
+ ```bash
36
+ pnpm exec playwright install chromium # first time only
37
+ pnpm test:e2e # runs the smoke spec headless
38
+ ```
39
+
40
+ ## License
41
+
42
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @module node-opcua-client-browser
3
+ *
4
+ * `ClientWS_transport` — OPC UA WebSocket client transport (Part 6 §7.5).
5
+ *
6
+ * Subclasses `ClientTCP_transport` and reuses its HEL/ACK + chunk-reassembly
7
+ * machinery unchanged. The only swap is the socket-creation step: instead of
8
+ * opening a TCP connection, we open a `WebSocket` and hand a
9
+ * {@link WsSocketAdapter} to `TCP_transport._install_socket`.
10
+ *
11
+ * OPC UA WebSocket framing:
12
+ * - One UACP chunk per outgoing binary WebSocket frame (handled naturally —
13
+ * each `write(chunk)` call invokes `ws.send(chunk)` once).
14
+ * - Incoming bytes are fed into `TCP_transport`'s packet-assembler, which
15
+ * re-combines split chunks.
16
+ * - Subprotocol `opcua+uacp` is requested on the handshake. If the server
17
+ * echoes it back we record that; if not (common with a `websockify`
18
+ * bridge) we continue without failing — browsers still accept the
19
+ * connection provided the server's bytes are valid UACP.
20
+ */
21
+ import type { ErrorCallback } from "node-opcua-status-code";
22
+ import { ClientTransportBase, type IClientTransportFactory, type TransportSettingsOptions } from "node-opcua-transport";
23
+ import { type WebSocketLike } from "./ws_socket_adapter";
24
+ /** WebSocket subprotocol name defined by OPC UA Part 6 §7.5 */
25
+ export declare const OPCUA_UACP_SUBPROTOCOL = "opcua+uacp";
26
+ /**
27
+ * A tiny WebSocket-constructor type. In the browser this is `globalThis.WebSocket`;
28
+ * in Node-side unit tests this is the `ws` package's `default` export. Either works.
29
+ */
30
+ export type WebSocketConstructor = new (url: string, protocols?: string | string[]) => WebSocketLike;
31
+ /** Options specific to the browser WebSocket transport. */
32
+ export interface ClientWSTransportOptions extends TransportSettingsOptions {
33
+ /**
34
+ * The WebSocket constructor to use. Defaults to `globalThis.WebSocket`.
35
+ * Override this from Node-side unit tests to inject the `ws` package.
36
+ */
37
+ webSocketCtor?: WebSocketConstructor;
38
+ /**
39
+ * When `true`, `connect()` fails if the peer does not echo back the
40
+ * `opcua+uacp` subprotocol on the WebSocket opening handshake. Defaults to
41
+ * `false` because `websockify` bridges do not negotiate subprotocols; the
42
+ * OPC UA bytes that follow will fail the HEL/ACK step anyway if the peer
43
+ * isn't actually speaking UACP.
44
+ */
45
+ strictSubprotocol?: boolean;
46
+ }
47
+ /**
48
+ * Parsed endpoint URL, normalised for the browser `WebSocket` constructor.
49
+ */
50
+ export interface ParsedWsEndpoint {
51
+ /** The URL to pass to `new WebSocket(url, …)` — always starts with `ws://` or `wss://`. */
52
+ wsUrl: string;
53
+ /** `true` if the original URL used `opc.wss://` or `wss://`. */
54
+ secure: boolean;
55
+ }
56
+ /**
57
+ * Parse and normalise an OPC UA WebSocket endpoint URL. Accepts all four
58
+ * forms: `opc.ws://`, `opc.wss://`, `ws://`, `wss://`. Anything else throws.
59
+ *
60
+ * @throws {Error} if the scheme is not one of the four accepted forms.
61
+ */
62
+ export declare function parseWsEndpointUrl(endpointUrl: string): ParsedWsEndpoint;
63
+ /**
64
+ * WebSocket-backed client transport. A sibling of `ClientTCP_transport`: both extend
65
+ * {@link ClientTransportBase} and reuse its UACP HEL/ACK machinery; the only
66
+ * difference is the socket flavour (`WebSocket` here, `net.Socket` in TCP).
67
+ */
68
+ export declare class ClientWS_transport extends ClientTransportBase {
69
+ private readonly _webSocketCtor;
70
+ private readonly _strictSubprotocol;
71
+ constructor(options?: ClientWSTransportOptions);
72
+ /**
73
+ * Opens a WebSocket to `endpointUrl`, adapts it to `ISocketLike`, and
74
+ * drives the inherited HEL/ACK transaction from `ClientTransportBase`.
75
+ */
76
+ connect(endpointUrl: string, callback: ErrorCallback): void;
77
+ dispose(): void;
78
+ }
79
+ /**
80
+ * A factory that produces {@link ClientWS_transport} instances. Pass to
81
+ * `ClientSecureChannelLayerOptions.transportFactory` to route all channel
82
+ * traffic through a WebSocket.
83
+ *
84
+ * @example
85
+ * const ws = browserWsTransportFactory.create({});
86
+ * // or, on Node:
87
+ * import WebSocket from "ws";
88
+ * const factory: IClientTransportFactory = {
89
+ * create(s) { return new ClientWS_transport({ ...s, webSocketCtor: WebSocket as any }); }
90
+ * };
91
+ */
92
+ export declare const browserWsTransportFactory: IClientTransportFactory;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * @module node-opcua-client-browser
4
+ *
5
+ * `ClientWS_transport` — OPC UA WebSocket client transport (Part 6 §7.5).
6
+ *
7
+ * Subclasses `ClientTCP_transport` and reuses its HEL/ACK + chunk-reassembly
8
+ * machinery unchanged. The only swap is the socket-creation step: instead of
9
+ * opening a TCP connection, we open a `WebSocket` and hand a
10
+ * {@link WsSocketAdapter} to `TCP_transport._install_socket`.
11
+ *
12
+ * OPC UA WebSocket framing:
13
+ * - One UACP chunk per outgoing binary WebSocket frame (handled naturally —
14
+ * each `write(chunk)` call invokes `ws.send(chunk)` once).
15
+ * - Incoming bytes are fed into `TCP_transport`'s packet-assembler, which
16
+ * re-combines split chunks.
17
+ * - Subprotocol `opcua+uacp` is requested on the handshake. If the server
18
+ * echoes it back we record that; if not (common with a `websockify`
19
+ * bridge) we continue without failing — browsers still accept the
20
+ * connection provided the server's bytes are valid UACP.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.browserWsTransportFactory = exports.ClientWS_transport = exports.OPCUA_UACP_SUBPROTOCOL = void 0;
24
+ exports.parseWsEndpointUrl = parseWsEndpointUrl;
25
+ /* global WebSocket */
26
+ const node_opcua_assert_1 = require("node-opcua-assert");
27
+ const node_opcua_debug_1 = require("node-opcua-debug");
28
+ const node_opcua_transport_1 = require("node-opcua-transport");
29
+ const ws_socket_adapter_1 = require("./ws_socket_adapter");
30
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)("ClientWS_transport");
31
+ const warningLog = (0, node_opcua_debug_1.make_warningLog)("ClientWS_transport");
32
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)("ClientWS_transport");
33
+ /** WebSocket subprotocol name defined by OPC UA Part 6 §7.5 */
34
+ exports.OPCUA_UACP_SUBPROTOCOL = "opcua+uacp";
35
+ /**
36
+ * Parse and normalise an OPC UA WebSocket endpoint URL. Accepts all four
37
+ * forms: `opc.ws://`, `opc.wss://`, `ws://`, `wss://`. Anything else throws.
38
+ *
39
+ * @throws {Error} if the scheme is not one of the four accepted forms.
40
+ */
41
+ function parseWsEndpointUrl(endpointUrl) {
42
+ const m = /^(opc\.ws|opc\.wss|ws|wss):\/\/(.+)$/i.exec(endpointUrl);
43
+ if (!m) {
44
+ throw new Error(`[node-opcua-client-browser] unsupported endpoint URL scheme for WebSocket transport: ${endpointUrl} ` +
45
+ `(expected one of opc.ws://, opc.wss://, ws://, wss://)`);
46
+ }
47
+ const scheme = m[1].toLowerCase();
48
+ const secure = scheme === "opc.wss" || scheme === "wss";
49
+ const wsUrl = `${secure ? "wss" : "ws"}://${m[2]}`;
50
+ return { wsUrl, secure };
51
+ }
52
+ /**
53
+ * WebSocket-backed client transport. A sibling of `ClientTCP_transport`: both extend
54
+ * {@link ClientTransportBase} and reuse its UACP HEL/ACK machinery; the only
55
+ * difference is the socket flavour (`WebSocket` here, `net.Socket` in TCP).
56
+ */
57
+ class ClientWS_transport extends node_opcua_transport_1.ClientTransportBase {
58
+ _webSocketCtor;
59
+ _strictSubprotocol;
60
+ constructor(options) {
61
+ super(options);
62
+ const resolvedCtor = options?.webSocketCtor ??
63
+ (typeof globalThis !== "undefined" ? globalThis.WebSocket : undefined);
64
+ if (!resolvedCtor) {
65
+ throw new Error("[ClientWS_transport] no WebSocket constructor available: pass `webSocketCtor` explicitly or run in a browser / Node 22+");
66
+ }
67
+ this._webSocketCtor = resolvedCtor;
68
+ this._strictSubprotocol = !!options?.strictSubprotocol;
69
+ }
70
+ /**
71
+ * Opens a WebSocket to `endpointUrl`, adapts it to `ISocketLike`, and
72
+ * drives the inherited HEL/ACK transaction from `ClientTransportBase`.
73
+ */
74
+ connect(endpointUrl, callback) {
75
+ this.endpointUrl = endpointUrl;
76
+ /* c8 ignore next */
77
+ doDebug && debugLog(`ClientWS_transport#connect(endpointUrl = ${endpointUrl})`);
78
+ let parsed;
79
+ try {
80
+ parsed = parseWsEndpointUrl(endpointUrl);
81
+ }
82
+ catch (err) {
83
+ callback(err);
84
+ return;
85
+ }
86
+ let ws;
87
+ try {
88
+ ws = new this._webSocketCtor(parsed.wsUrl, exports.OPCUA_UACP_SUBPROTOCOL);
89
+ }
90
+ catch (err) {
91
+ /* c8 ignore next */
92
+ doDebug && debugLog("WebSocket construction failed", err.message);
93
+ callback(err);
94
+ return;
95
+ }
96
+ const socket = new ws_socket_adapter_1.WsSocketAdapter(ws, parsed.wsUrl);
97
+ const onEarlyError = (err) => {
98
+ callback(err);
99
+ };
100
+ socket.once("error", onEarlyError);
101
+ socket.once("connect", () => {
102
+ socket.removeListener("error", onEarlyError);
103
+ // Subprotocol negotiation check. Browser WS exposes `protocol` on the
104
+ // instance; `ws` package exposes it too. When the peer doesn't
105
+ // negotiate (e.g. a websockify bridge), `protocol` is an empty
106
+ // string. Only fail in strict mode.
107
+ const negotiated = ws.protocol;
108
+ if (negotiated && negotiated !== exports.OPCUA_UACP_SUBPROTOCOL) {
109
+ const msg = `unexpected WebSocket subprotocol negotiated: "${negotiated}" (expected "${exports.OPCUA_UACP_SUBPROTOCOL}")`;
110
+ if (this._strictSubprotocol) {
111
+ callback(new Error(`[ClientWS_transport] ${msg}`));
112
+ socket.destroy();
113
+ return;
114
+ }
115
+ warningLog(msg);
116
+ }
117
+ else if (!negotiated) {
118
+ /* c8 ignore next */
119
+ doDebug &&
120
+ debugLog(`peer did not echo the "${exports.OPCUA_UACP_SUBPROTOCOL}" subprotocol; continuing ` +
121
+ `(set strictSubprotocol=true to reject this)`);
122
+ }
123
+ // Install the WebSocket-backed socket adapter into the inherited TCP_transport machinery.
124
+ (0, node_opcua_assert_1.assert)(!this._socket, "transport should not have a socket yet");
125
+ this._install_socket(socket);
126
+ this._perform_HEL_ACK_transaction((err) => {
127
+ if (!err) {
128
+ /* c8 ignore next */
129
+ if (!this._socket) {
130
+ return callback(new Error("Abandoned"));
131
+ }
132
+ this._install_post_connect_error_handler(endpointUrl);
133
+ this.emit("connect");
134
+ }
135
+ else {
136
+ debugLog("_perform_HEL_ACK_transaction has failed with err=", err.message);
137
+ }
138
+ callback(err);
139
+ });
140
+ });
141
+ }
142
+ dispose() {
143
+ /* c8 ignore next */
144
+ doDebug && debugLog(" ClientWS_transport disposed");
145
+ super.dispose();
146
+ }
147
+ }
148
+ exports.ClientWS_transport = ClientWS_transport;
149
+ /**
150
+ * A factory that produces {@link ClientWS_transport} instances. Pass to
151
+ * `ClientSecureChannelLayerOptions.transportFactory` to route all channel
152
+ * traffic through a WebSocket.
153
+ *
154
+ * @example
155
+ * const ws = browserWsTransportFactory.create({});
156
+ * // or, on Node:
157
+ * import WebSocket from "ws";
158
+ * const factory: IClientTransportFactory = {
159
+ * create(s) { return new ClientWS_transport({ ...s, webSocketCtor: WebSocket as any }); }
160
+ * };
161
+ */
162
+ exports.browserWsTransportFactory = {
163
+ create(settings) {
164
+ return new ClientWS_transport(settings);
165
+ }
166
+ };
167
+ //# sourceMappingURL=client_ws_transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client_ws_transport.js","sourceRoot":"","sources":["../source/client_ws_transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @module node-opcua-client-browser
3
+ *
4
+ * Thin helper that constructs an `OPCUAClient` (from `node-opcua-client`)
5
+ * pre-wired with the browser WebSocket transport factory and, when no
6
+ * credentials are supplied, an in-memory certificate/key pair provider so
7
+ * no disk paths are touched.
8
+ *
9
+ * All other `OPCUAClient` configuration passes through unchanged — callers
10
+ * configure session timeouts, reconnection strategies, security policies,
11
+ * etc. exactly as they would for the Node client.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { createBrowserClient, AttributeIds, MessageSecurityMode, SecurityPolicy } from "node-opcua-client-browser";
16
+ *
17
+ * const client = createBrowserClient({
18
+ * endpointMustExist: false,
19
+ * securityMode: MessageSecurityMode.None,
20
+ * securityPolicy: SecurityPolicy.None,
21
+ * });
22
+ * await client.connect("opc.ws://localhost:4840");
23
+ * const session = await client.createSession();
24
+ * const dv = await session.read({ nodeId: "ns=1;s=Counter", attributeId: AttributeIds.Value });
25
+ * await session.close();
26
+ * await client.disconnect();
27
+ * ```
28
+ */
29
+ import { OPCUAClient, type OPCUAClientOptions } from "node-opcua-client";
30
+ import type { Certificate, PrivateKey } from "node-opcua-crypto/web";
31
+ /**
32
+ * Options accepted by {@link createBrowserClient}. Superset of
33
+ * {@link OPCUAClientOptions}; the browser-specific defaults described above
34
+ * apply unless the caller overrides them.
35
+ */
36
+ export interface CreateBrowserClientOptions extends OPCUAClientOptions {
37
+ /**
38
+ * Pre-built client certificate chain (DER). When combined with
39
+ * `clientPrivateKey`, it is loaded into an
40
+ * `InMemoryCertificateKeyPairProvider` so the owning `OPCUAClient`
41
+ * needs no disk cert files.
42
+ *
43
+ * If you already pass `certificateKeyPairProvider`, this option is
44
+ * ignored.
45
+ */
46
+ clientCertificate?: Certificate | Certificate[];
47
+ /**
48
+ * Pre-built client private key. Paired with `clientCertificate` to
49
+ * populate the in-memory provider.
50
+ *
51
+ * If you already pass `certificateKeyPairProvider`, this option is
52
+ * ignored.
53
+ */
54
+ clientPrivateKey?: PrivateKey;
55
+ }
56
+ /**
57
+ * Construct an `OPCUAClient` with browser-appropriate defaults.
58
+ *
59
+ * - `transportFactory` defaults to `browserWsTransportFactory` so the
60
+ * underlying secure channel opens a WebSocket. Callers can override by
61
+ * passing a different `transportFactory`.
62
+ * - `certificateKeyPairProvider` defaults to an
63
+ * `InMemoryCertificateKeyPairProvider`, populated with
64
+ * `clientCertificate` / `clientPrivateKey` when those are supplied.
65
+ * When they are NOT supplied, the provider is constructed empty —
66
+ * callers are expected to await `ensureCertificateExists(...)` on the
67
+ * returned client's credentials before connecting with a non-None
68
+ * security policy.
69
+ * - `clientCertificateManager` defaults to an `InMemoryCertificateStore`
70
+ * (auto-accept unknown peer certs). Callers can override by passing
71
+ * their own `clientCertificateManager` (e.g. one backed by IndexedDB
72
+ * or OPFS).
73
+ */
74
+ export declare function createBrowserClient(options?: CreateBrowserClientOptions): OPCUAClient;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * @module node-opcua-client-browser
4
+ *
5
+ * Thin helper that constructs an `OPCUAClient` (from `node-opcua-client`)
6
+ * pre-wired with the browser WebSocket transport factory and, when no
7
+ * credentials are supplied, an in-memory certificate/key pair provider so
8
+ * no disk paths are touched.
9
+ *
10
+ * All other `OPCUAClient` configuration passes through unchanged — callers
11
+ * configure session timeouts, reconnection strategies, security policies,
12
+ * etc. exactly as they would for the Node client.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createBrowserClient, AttributeIds, MessageSecurityMode, SecurityPolicy } from "node-opcua-client-browser";
17
+ *
18
+ * const client = createBrowserClient({
19
+ * endpointMustExist: false,
20
+ * securityMode: MessageSecurityMode.None,
21
+ * securityPolicy: SecurityPolicy.None,
22
+ * });
23
+ * await client.connect("opc.ws://localhost:4840");
24
+ * const session = await client.createSession();
25
+ * const dv = await session.read({ nodeId: "ns=1;s=Counter", attributeId: AttributeIds.Value });
26
+ * await session.close();
27
+ * await client.disconnect();
28
+ * ```
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.createBrowserClient = createBrowserClient;
32
+ const node_opcua_common_1 = require("node-opcua-common");
33
+ const node_opcua_client_1 = require("node-opcua-client");
34
+ const client_ws_transport_1 = require("./client_ws_transport");
35
+ /**
36
+ * Construct an `OPCUAClient` with browser-appropriate defaults.
37
+ *
38
+ * - `transportFactory` defaults to `browserWsTransportFactory` so the
39
+ * underlying secure channel opens a WebSocket. Callers can override by
40
+ * passing a different `transportFactory`.
41
+ * - `certificateKeyPairProvider` defaults to an
42
+ * `InMemoryCertificateKeyPairProvider`, populated with
43
+ * `clientCertificate` / `clientPrivateKey` when those are supplied.
44
+ * When they are NOT supplied, the provider is constructed empty —
45
+ * callers are expected to await `ensureCertificateExists(...)` on the
46
+ * returned client's credentials before connecting with a non-None
47
+ * security policy.
48
+ * - `clientCertificateManager` defaults to an `InMemoryCertificateStore`
49
+ * (auto-accept unknown peer certs). Callers can override by passing
50
+ * their own `clientCertificateManager` (e.g. one backed by IndexedDB
51
+ * or OPFS).
52
+ */
53
+ function createBrowserClient(options = {}) {
54
+ const { clientCertificate, clientPrivateKey, ...clientOptions } = options;
55
+ // Default to the browser WS transport factory unless the caller supplies one.
56
+ if (!clientOptions.transportFactory) {
57
+ clientOptions.transportFactory = client_ws_transport_1.browserWsTransportFactory;
58
+ }
59
+ // Default to an in-memory cert/key provider unless the caller supplies one.
60
+ // If the caller provided a client cert + private key, pre-populate the
61
+ // provider so the owning client can reach them without further async setup.
62
+ if (!clientOptions.certificateKeyPairProvider) {
63
+ const chain = clientCertificate
64
+ ? (Array.isArray(clientCertificate) ? clientCertificate : [clientCertificate])
65
+ : undefined;
66
+ clientOptions.certificateKeyPairProvider = new node_opcua_common_1.InMemoryCertificateKeyPairProvider(chain, clientPrivateKey);
67
+ }
68
+ // Default to an in-memory trust store unless the caller supplies one.
69
+ if (!clientOptions.clientCertificateManager) {
70
+ clientOptions.clientCertificateManager = new node_opcua_common_1.InMemoryCertificateStore({ autoAcceptUnknown: true });
71
+ }
72
+ return node_opcua_client_1.OPCUAClient.create(clientOptions);
73
+ }
74
+ //# sourceMappingURL=create_browser_client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_browser_client.js","sourceRoot":"","sources":["../source/create_browser_client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;AAqDH,kDAwBC;AA3ED,yDAAiG;AACjG,yDAAyE;AAGzE,+DAAkE;AA6BlE;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,mBAAmB,CAAC,UAAsC,EAAgC;IACtG,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAE1E,8EAA8E;IAC9E,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC;QAClC,aAAa,CAAC,gBAAgB,GAAG,+CAAyB,CAAC;IAC/D,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,4EAA4E;IAC5E,IAAI,CAAC,aAAa,CAAC,0BAA0B,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,iBAAiB;YAC3B,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;YAC9E,CAAC,CAAC,SAAS,CAAC;QAChB,aAAa,CAAC,0BAA0B,GAAG,IAAI,sDAAkC,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAC/G,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC,aAAa,CAAC,wBAAwB,EAAE,CAAC;QAC1C,aAAa,CAAC,wBAAwB,GAAG,IAAI,4CAAwB,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC;IAED,OAAO,+BAAW,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,47 @@
1
+ /*!
2
+ * The MIT License (MIT)
3
+ * Copyright (c) 2022-2025 Sterfive SAS - 833264583 RCS ORLEANS - France (https://www.sterfive.com)
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ * this software and associated documentation files (the "Software"), to deal in
7
+ * the Software without restriction, including without limitation the rights to
8
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ * the Software, and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ */
22
+ /**
23
+ * @module node-opcua-client-browser
24
+ *
25
+ * Browser entry point for `node-opcua-client`. Exposes the OPC UA WebSocket
26
+ * transport (`ClientWS_transport`, `browserWsTransportFactory`,
27
+ * `parseWsEndpointUrl`).
28
+ *
29
+ * Target environments: modern evergreen browsers (Chromium, Firefox, WebKit).
30
+ * The transport uses the OPC UA WebSocket mapping per Part 6 §7.5
31
+ * (`opcua+uacp` subprotocol, one UACP chunk per binary frame).
32
+ *
33
+ * NOTE: `createBrowserClient` is temporarily NOT re-exported from this
34
+ * barrel. Its `OPCUAClient` import drags in `node-opcua-client`, which
35
+ * still transitively pulls Node-only modules (`node:crypto`, `node:fs`,
36
+ * `node:dns`, `@ster5/global-mutex`, `multicast-dns`, …) at bundle
37
+ * time. The full plan to make those packages browser-safe lives at
38
+ * `C:\Users\etien\.claude\plans\run-pnpm-filter-node-opcua-client-browse-vivid-sparkle.md`.
39
+ * The Node-side helper itself is still built and unit-tested — its 8
40
+ * mocha cases in `test/unit/test_create_browser_client.ts` import it
41
+ * directly from `../../dist`. Re-export will be restored in Step 5 of
42
+ * the plan once Steps 1–4 land.
43
+ */
44
+ export * from "./client_ws_transport";
45
+ export * as uacp from "./uacp";
46
+ export { type WebSocketLike, WsSocketAdapter } from "./ws_socket_adapter";
47
+ export declare const VERSION = "2.172.0";
package/dist/index.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /*!
3
+ * The MIT License (MIT)
4
+ * Copyright (c) 2022-2025 Sterfive SAS - 833264583 RCS ORLEANS - France (https://www.sterfive.com)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ * this software and associated documentation files (the "Software"), to deal in
8
+ * the Software without restriction, including without limitation the rights to
9
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ * the Software, and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+ /**
24
+ * @module node-opcua-client-browser
25
+ *
26
+ * Browser entry point for `node-opcua-client`. Exposes the OPC UA WebSocket
27
+ * transport (`ClientWS_transport`, `browserWsTransportFactory`,
28
+ * `parseWsEndpointUrl`).
29
+ *
30
+ * Target environments: modern evergreen browsers (Chromium, Firefox, WebKit).
31
+ * The transport uses the OPC UA WebSocket mapping per Part 6 §7.5
32
+ * (`opcua+uacp` subprotocol, one UACP chunk per binary frame).
33
+ *
34
+ * NOTE: `createBrowserClient` is temporarily NOT re-exported from this
35
+ * barrel. Its `OPCUAClient` import drags in `node-opcua-client`, which
36
+ * still transitively pulls Node-only modules (`node:crypto`, `node:fs`,
37
+ * `node:dns`, `@ster5/global-mutex`, `multicast-dns`, …) at bundle
38
+ * time. The full plan to make those packages browser-safe lives at
39
+ * `C:\Users\etien\.claude\plans\run-pnpm-filter-node-opcua-client-browse-vivid-sparkle.md`.
40
+ * The Node-side helper itself is still built and unit-tested — its 8
41
+ * mocha cases in `test/unit/test_create_browser_client.ts` import it
42
+ * directly from `../../dist`. Re-export will be restored in Step 5 of
43
+ * the plan once Steps 1–4 land.
44
+ */
45
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
46
+ if (k2 === undefined) k2 = k;
47
+ var desc = Object.getOwnPropertyDescriptor(m, k);
48
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
49
+ desc = { enumerable: true, get: function() { return m[k]; } };
50
+ }
51
+ Object.defineProperty(o, k2, desc);
52
+ }) : (function(o, m, k, k2) {
53
+ if (k2 === undefined) k2 = k;
54
+ o[k2] = m[k];
55
+ }));
56
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
57
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
58
+ }) : function(o, v) {
59
+ o["default"] = v;
60
+ });
61
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
62
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
63
+ };
64
+ var __importStar = (this && this.__importStar) || (function () {
65
+ var ownKeys = function(o) {
66
+ ownKeys = Object.getOwnPropertyNames || function (o) {
67
+ var ar = [];
68
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
69
+ return ar;
70
+ };
71
+ return ownKeys(o);
72
+ };
73
+ return function (mod) {
74
+ if (mod && mod.__esModule) return mod;
75
+ var result = {};
76
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
77
+ __setModuleDefault(result, mod);
78
+ return result;
79
+ };
80
+ })();
81
+ Object.defineProperty(exports, "__esModule", { value: true });
82
+ exports.VERSION = exports.WsSocketAdapter = exports.uacp = void 0;
83
+ __exportStar(require("./client_ws_transport"), exports);
84
+ exports.uacp = __importStar(require("./uacp"));
85
+ var ws_socket_adapter_1 = require("./ws_socket_adapter");
86
+ Object.defineProperty(exports, "WsSocketAdapter", { enumerable: true, get: function () { return ws_socket_adapter_1.WsSocketAdapter; } });
87
+ exports.VERSION = "2.172.0";
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,wDAAsC;AACtC,+CAA+B;AAC/B,yDAA0E;AAA7C,oHAAA,eAAe,OAAA;AAE/B,QAAA,OAAO,GAAG,SAAS,CAAC"}
package/dist/uacp.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @module node-opcua-client-browser
3
+ * @internal
4
+ *
5
+ * UACP primitives usable in the browser.
6
+ *
7
+ * These are thin re-exports of the byte-level encoders/decoders from
8
+ * `node-opcua-transport`. Those modules are pure-JS (only `node:events` is
9
+ * pulled in, which Vite polyfills automatically) and are therefore safe to
10
+ * bundle for the browser. Consolidating them here gives us a single point of
11
+ * audit if a future release of `node-opcua-transport` introduces a Node-only
12
+ * import.
13
+ */
14
+ export { AcknowledgeMessage, HelloMessage, TCPErrorMessage, packTcpMessage, readRawMessageHeader, type TransportSettingsOptions } from "node-opcua-transport";