@homaiohq/napi-quickfix 0.1.0 → 0.3.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/CMakeLists.txt +5 -1
- package/README.md +344 -18
- package/cpp/acceptor_wrap.cpp +253 -19
- package/cpp/acceptor_wrap.h +60 -1
- package/cpp/addon.cpp +10 -6
- package/cpp/application_bridge.cpp +114 -30
- package/cpp/application_bridge.h +57 -4
- package/cpp/data_dictionary_wrap.cpp +89 -3
- package/cpp/data_dictionary_wrap.h +27 -5
- package/cpp/engine_common.h +108 -0
- package/cpp/engine_workers.h +99 -18
- package/cpp/field_map_ops.h +492 -0
- package/cpp/group_wrap.cpp +221 -0
- package/cpp/group_wrap.h +76 -0
- package/cpp/initiator_wrap.cpp +262 -25
- package/cpp/initiator_wrap.h +37 -2
- package/cpp/message_wrap.cpp +171 -78
- package/cpp/message_wrap.h +47 -5
- package/cpp/session_op_gate.h +201 -0
- package/cpp/session_static.cpp +139 -12
- package/cpp/session_wrap.cpp +438 -0
- package/cpp/session_wrap.h +144 -0
- package/dist/cjs/acceptor.d.ts +0 -1
- package/dist/cjs/acceptor.js +0 -1
- package/dist/cjs/application.d.ts +0 -1
- package/dist/cjs/application.js +0 -1
- package/dist/cjs/data-dictionary.d.ts +21 -3
- package/dist/cjs/data-dictionary.js +32 -4
- package/dist/cjs/engine.d.ts +42 -4
- package/dist/cjs/engine.js +50 -5
- package/dist/cjs/enums.d.ts +48 -14
- package/dist/cjs/enums.js +20 -25
- package/dist/cjs/field-value.d.ts +12 -0
- package/dist/cjs/field-value.js +40 -0
- package/dist/cjs/generated/fields.d.ts +0 -1
- package/dist/cjs/generated/fields.js +0 -1
- package/dist/cjs/generated/values.d.ts +8548 -0
- package/dist/cjs/generated/values.js +8580 -0
- package/dist/cjs/group.d.ts +126 -0
- package/dist/cjs/group.js +185 -0
- package/dist/cjs/index.d.ts +28 -5
- package/dist/cjs/index.js +30 -28
- package/dist/cjs/initiator.d.ts +0 -1
- package/dist/cjs/initiator.js +0 -1
- package/dist/cjs/message.d.ts +129 -5
- package/dist/cjs/message.js +146 -9
- package/dist/cjs/native.d.ts +179 -17
- package/dist/cjs/native.js +0 -1
- package/dist/cjs/session-id.d.ts +12 -1
- package/dist/cjs/session-id.js +21 -1
- package/dist/cjs/session-settings.d.ts +0 -1
- package/dist/cjs/session-settings.js +0 -1
- package/dist/cjs/session.d.ts +213 -0
- package/dist/cjs/session.js +337 -0
- package/dist/cjs/tls-tunnel.d.ts +143 -0
- package/dist/cjs/tls-tunnel.js +261 -0
- package/dist/esm/acceptor.d.ts +0 -1
- package/dist/esm/acceptor.js +0 -1
- package/dist/esm/application.d.ts +0 -1
- package/dist/esm/application.js +0 -1
- package/dist/esm/data-dictionary.d.ts +21 -3
- package/dist/esm/data-dictionary.js +32 -4
- package/dist/esm/engine.d.ts +42 -4
- package/dist/esm/engine.js +51 -6
- package/dist/esm/enums.d.ts +48 -14
- package/dist/esm/enums.js +14 -24
- package/dist/esm/field-value.d.ts +12 -0
- package/dist/esm/field-value.js +37 -0
- package/dist/esm/generated/fields.d.ts +0 -1
- package/dist/esm/generated/fields.js +0 -1
- package/dist/esm/generated/values.d.ts +8548 -0
- package/dist/esm/generated/values.js +8564 -0
- package/dist/esm/group.d.ts +126 -0
- package/dist/esm/group.js +181 -0
- package/dist/esm/index.d.ts +28 -5
- package/dist/esm/index.js +18 -26
- package/dist/esm/initiator.d.ts +0 -1
- package/dist/esm/initiator.js +0 -1
- package/dist/esm/message.d.ts +129 -5
- package/dist/esm/message.js +146 -9
- package/dist/esm/native.d.ts +179 -17
- package/dist/esm/native.js +0 -1
- package/dist/esm/session-id.d.ts +12 -1
- package/dist/esm/session-id.js +20 -1
- package/dist/esm/session-settings.d.ts +0 -1
- package/dist/esm/session-settings.js +0 -1
- package/dist/esm/session.d.ts +213 -0
- package/dist/esm/session.js +329 -0
- package/dist/esm/tls-tunnel.d.ts +143 -0
- package/dist/esm/tls-tunnel.js +256 -0
- package/package.json +13 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/cpp/enums.cpp +0 -66
- package/dist/cjs/acceptor.d.ts.map +0 -1
- package/dist/cjs/acceptor.js.map +0 -1
- package/dist/cjs/application.d.ts.map +0 -1
- package/dist/cjs/application.js.map +0 -1
- package/dist/cjs/data-dictionary.d.ts.map +0 -1
- package/dist/cjs/data-dictionary.js.map +0 -1
- package/dist/cjs/engine.d.ts.map +0 -1
- package/dist/cjs/engine.js.map +0 -1
- package/dist/cjs/enums.d.ts.map +0 -1
- package/dist/cjs/enums.js.map +0 -1
- package/dist/cjs/generated/fields.d.ts.map +0 -1
- package/dist/cjs/generated/fields.js.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/initiator.d.ts.map +0 -1
- package/dist/cjs/initiator.js.map +0 -1
- package/dist/cjs/message.d.ts.map +0 -1
- package/dist/cjs/message.js.map +0 -1
- package/dist/cjs/native.d.ts.map +0 -1
- package/dist/cjs/native.js.map +0 -1
- package/dist/cjs/session-id.d.ts.map +0 -1
- package/dist/cjs/session-id.js.map +0 -1
- package/dist/cjs/session-settings.d.ts.map +0 -1
- package/dist/cjs/session-settings.js.map +0 -1
- package/dist/esm/acceptor.d.ts.map +0 -1
- package/dist/esm/acceptor.js.map +0 -1
- package/dist/esm/application.d.ts.map +0 -1
- package/dist/esm/application.js.map +0 -1
- package/dist/esm/data-dictionary.d.ts.map +0 -1
- package/dist/esm/data-dictionary.js.map +0 -1
- package/dist/esm/engine.d.ts.map +0 -1
- package/dist/esm/engine.js.map +0 -1
- package/dist/esm/enums.d.ts.map +0 -1
- package/dist/esm/enums.js.map +0 -1
- package/dist/esm/generated/fields.d.ts.map +0 -1
- package/dist/esm/generated/fields.js.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/initiator.d.ts.map +0 -1
- package/dist/esm/initiator.js.map +0 -1
- package/dist/esm/message.d.ts.map +0 -1
- package/dist/esm/message.js.map +0 -1
- package/dist/esm/native.d.ts.map +0 -1
- package/dist/esm/native.js.map +0 -1
- package/dist/esm/session-id.d.ts.map +0 -1
- package/dist/esm/session-id.js.map +0 -1
- package/dist/esm/session-settings.d.ts.map +0 -1
- package/dist/esm/session-settings.js.map +0 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ergonomic wrapper around a live native `FIX::Session`.
|
|
3
|
+
*/
|
|
4
|
+
import { type NativeSession } from './native.js';
|
|
5
|
+
import { SessionID } from './session-id.js';
|
|
6
|
+
/**
|
|
7
|
+
* A handle on one live FIX session (wraps `FIX::Session`).
|
|
8
|
+
*
|
|
9
|
+
* Obtain one with {@link lookupSession} (any session in the process) or
|
|
10
|
+
* {@link Engine.getSession} (a session of a particular engine); sessions
|
|
11
|
+
* cannot be constructed directly.
|
|
12
|
+
*
|
|
13
|
+
* **Lifetime.** The underlying `FIX::Session` is owned by its engine. This
|
|
14
|
+
* handle stores only the {@link SessionID} and the identity of the engine it
|
|
15
|
+
* was obtained from, and re-resolves the session on every call, so it never
|
|
16
|
+
* dangles: once that engine has been stopped (which destroys its sessions)
|
|
17
|
+
* every method throws a {@link QuickFixError} whose `fixErrorName` is
|
|
18
|
+
* `'SessionNotFound'` -- also after a new engine has been created with the
|
|
19
|
+
* same id. A handle never crosses over to another engine's session; obtain a
|
|
20
|
+
* new one from the new engine.
|
|
21
|
+
*
|
|
22
|
+
* **Everything is synchronous.** State queries, sequence-number access, the
|
|
23
|
+
* runtime option getters/setters, {@link Session.logon}, {@link Session.logout}
|
|
24
|
+
* and {@link Session.refresh} never take the mutex QuickFIX holds while running
|
|
25
|
+
* your application handlers, so they cannot deadlock the main thread, and calls
|
|
26
|
+
* made in program order take effect in program order. ({@link Session.logon} /
|
|
27
|
+
* {@link Session.logout} flip a plain flag the engine's network thread reads
|
|
28
|
+
* without a lock, exactly as QuickFIX's own `Initiator::stop()` does; the
|
|
29
|
+
* engine picks the change up on its next pass.) QuickFIX's
|
|
30
|
+
* `Session::disconnect()` / `reset()` are deliberately not exposed: they are
|
|
31
|
+
* only safe on the engine's own network thread (see ARCHITECTURE.md §5). Use
|
|
32
|
+
* {@link Session.logout} to bring a session down, and the sequence-number
|
|
33
|
+
* setters to reset its numbering -- on a quiet session; see
|
|
34
|
+
* {@link Session.setNextSenderMsgSeqNum}.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const session = initiator.getSession(sessionID)!;
|
|
39
|
+
* if (session.isLoggedOn()) {
|
|
40
|
+
* console.log('next outbound seq', session.getExpectedSenderNum());
|
|
41
|
+
* session.logout('maintenance');
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class Session {
|
|
46
|
+
#private;
|
|
47
|
+
/** @internal Use {@link lookupSession} / {@link Engine.getSession}. */
|
|
48
|
+
private constructor();
|
|
49
|
+
/** @internal Wrap an existing native session handle. */
|
|
50
|
+
static fromNative(handle: NativeSession): Session;
|
|
51
|
+
/** @internal The native handle backing this session. */
|
|
52
|
+
get nativeHandle(): NativeSession;
|
|
53
|
+
/** The id this handle refers to. Always available, even after the session is gone. */
|
|
54
|
+
get sessionID(): SessionID;
|
|
55
|
+
/** `true` once a Logon has been both sent and received. */
|
|
56
|
+
isLoggedOn(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the session is enabled. `false` after {@link Session.logout} until
|
|
59
|
+
* {@link Session.logon}; a disabled initiator session is not reconnected.
|
|
60
|
+
*/
|
|
61
|
+
isEnabled(): boolean;
|
|
62
|
+
/** Whether this side has sent a Logon on the current connection. */
|
|
63
|
+
sentLogon(): boolean;
|
|
64
|
+
/** Whether this side has sent a Logout on the current connection. */
|
|
65
|
+
sentLogout(): boolean;
|
|
66
|
+
/** Whether a Logon has been received on the current connection. */
|
|
67
|
+
receivedLogon(): boolean;
|
|
68
|
+
/** `true` for a session owned by an {@link Initiator}. */
|
|
69
|
+
isInitiator(): boolean;
|
|
70
|
+
/** `true` for a session owned by an {@link Acceptor}. */
|
|
71
|
+
isAcceptor(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
74
|
+
* configured `StartTime`/`EndTime` window. Throws `RangeError` for an
|
|
75
|
+
* invalid `Date`.
|
|
76
|
+
*/
|
|
77
|
+
isSessionTime(now?: Date): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
80
|
+
* configured `LogonTime`/`LogoutTime` window. Throws `RangeError` for an
|
|
81
|
+
* invalid `Date`.
|
|
82
|
+
*/
|
|
83
|
+
isLogonTime(now?: Date): boolean;
|
|
84
|
+
/** The MsgSeqNum (tag 34) the next outbound message will carry. */
|
|
85
|
+
getExpectedSenderNum(): number;
|
|
86
|
+
/** The MsgSeqNum (tag 34) expected on the next inbound message. */
|
|
87
|
+
getExpectedTargetNum(): number;
|
|
88
|
+
/**
|
|
89
|
+
* Set the MsgSeqNum the next outbound message will carry (writes to the
|
|
90
|
+
* message store; a file store may throw `IOException`).
|
|
91
|
+
*
|
|
92
|
+
* **Call it on a quiet session.** The setter is not serialised against a
|
|
93
|
+
* message the engine is sending or receiving on this session at the same
|
|
94
|
+
* time: QuickFIX reads the next number into the outbound header, runs your
|
|
95
|
+
* `toApp`/`toAdmin` handler, then increments from the store, all under a
|
|
96
|
+
* lock this call does not (and must not) take. A value set inside that
|
|
97
|
+
* window -- from a handler, or while a {@link sendToTarget} promise or a
|
|
98
|
+
* heartbeat is in flight on this session -- is overwritten by the increment,
|
|
99
|
+
* and the session ends up one off from its counterparty (a Resend Request or
|
|
100
|
+
* a "MsgSeqNum too low" Logout). Set it before {@link Engine.start}, from a
|
|
101
|
+
* `'logout'` event, or when you know nothing is in flight; never from a
|
|
102
|
+
* handler.
|
|
103
|
+
*
|
|
104
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
105
|
+
* 1-based).
|
|
106
|
+
*/
|
|
107
|
+
setNextSenderMsgSeqNum(seqNum: number): void;
|
|
108
|
+
/**
|
|
109
|
+
* Set the MsgSeqNum expected on the next inbound message (writes to the
|
|
110
|
+
* message store; a file store may throw `IOException`).
|
|
111
|
+
*
|
|
112
|
+
* **Call it on a quiet session.** Like {@link Session.setNextSenderMsgSeqNum},
|
|
113
|
+
* it is not serialised against a message the engine is processing on this
|
|
114
|
+
* session: QuickFIX checks the inbound number, runs your `fromApp`/`fromAdmin`
|
|
115
|
+
* handler, then increments the expected number from the store under a lock
|
|
116
|
+
* this call does not take, so a value set meanwhile is overwritten. Set it
|
|
117
|
+
* before {@link Engine.start}, from a `'logout'` event, or when you know
|
|
118
|
+
* nothing is in flight; never from a handler.
|
|
119
|
+
*
|
|
120
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
121
|
+
* 1-based).
|
|
122
|
+
*/
|
|
123
|
+
setNextTargetMsgSeqNum(seqNum: number): void;
|
|
124
|
+
/** `ResetOnLogon` */
|
|
125
|
+
getResetOnLogon(): boolean;
|
|
126
|
+
setResetOnLogon(value: boolean): void;
|
|
127
|
+
/** `ResetOnLogout` */
|
|
128
|
+
getResetOnLogout(): boolean;
|
|
129
|
+
setResetOnLogout(value: boolean): void;
|
|
130
|
+
/** `ResetOnDisconnect` */
|
|
131
|
+
getResetOnDisconnect(): boolean;
|
|
132
|
+
setResetOnDisconnect(value: boolean): void;
|
|
133
|
+
/** `RefreshOnLogon` */
|
|
134
|
+
getRefreshOnLogon(): boolean;
|
|
135
|
+
setRefreshOnLogon(value: boolean): void;
|
|
136
|
+
/** `CheckCompID` */
|
|
137
|
+
getCheckCompId(): boolean;
|
|
138
|
+
setCheckCompId(value: boolean): void;
|
|
139
|
+
/** `CheckLatency` */
|
|
140
|
+
getCheckLatency(): boolean;
|
|
141
|
+
setCheckLatency(value: boolean): void;
|
|
142
|
+
/** `PersistMessages` */
|
|
143
|
+
getPersistMessages(): boolean;
|
|
144
|
+
setPersistMessages(value: boolean): void;
|
|
145
|
+
/** `SendRedundantResendRequests` */
|
|
146
|
+
getSendRedundantResendRequests(): boolean;
|
|
147
|
+
setSendRedundantResendRequests(value: boolean): void;
|
|
148
|
+
/** `ValidateLengthAndChecksum` */
|
|
149
|
+
getValidateLengthAndChecksum(): boolean;
|
|
150
|
+
setValidateLengthAndChecksum(value: boolean): void;
|
|
151
|
+
/** `SendNextExpectedMsgSeqNum` */
|
|
152
|
+
getSendNextExpectedMsgSeqNum(): boolean;
|
|
153
|
+
setSendNextExpectedMsgSeqNum(value: boolean): void;
|
|
154
|
+
/** `NonStopSession` */
|
|
155
|
+
getIsNonStopSession(): boolean;
|
|
156
|
+
setIsNonStopSession(value: boolean): void;
|
|
157
|
+
/** `LogonTimeout`, in seconds. */
|
|
158
|
+
getLogonTimeout(): number;
|
|
159
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
160
|
+
setLogonTimeout(seconds: number): void;
|
|
161
|
+
/** `LogoutTimeout`, in seconds. */
|
|
162
|
+
getLogoutTimeout(): number;
|
|
163
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
164
|
+
setLogoutTimeout(seconds: number): void;
|
|
165
|
+
/** `MaxLatency`, in seconds. */
|
|
166
|
+
getMaxLatency(): number;
|
|
167
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
168
|
+
setMaxLatency(seconds: number): void;
|
|
169
|
+
/** `TimestampPrecision` (number of sub-second digits, `0..9`). */
|
|
170
|
+
getTimestampPrecision(): number;
|
|
171
|
+
/** @throws `RangeError` unless `precision` is an integer in `0..9`. */
|
|
172
|
+
setTimestampPrecision(precision: number): void;
|
|
173
|
+
/**
|
|
174
|
+
* Enable the session. An initiator session reconnects and logs on at its
|
|
175
|
+
* next `ReconnectInterval`; an acceptor session accepts the next inbound
|
|
176
|
+
* Logon. Takes effect immediately ({@link Session.isEnabled} is `true` on
|
|
177
|
+
* return); observe the handshake through the engine's `'logon'` event.
|
|
178
|
+
*/
|
|
179
|
+
logon(): void;
|
|
180
|
+
/**
|
|
181
|
+
* Disable the session and initiate a graceful Logout (sent with `reason` as
|
|
182
|
+
* tag 58 on the next engine tick). The session stays disabled — it will not
|
|
183
|
+
* reconnect — until {@link Session.logon}. Takes effect immediately
|
|
184
|
+
* ({@link Session.isEnabled} is `false` on return); observe completion
|
|
185
|
+
* through the engine's `'logout'` event.
|
|
186
|
+
*/
|
|
187
|
+
logout(reason?: string): void;
|
|
188
|
+
/**
|
|
189
|
+
* Re-read the session state (sequence numbers etc.) from the message store.
|
|
190
|
+
*
|
|
191
|
+
* @throws {QuickFixError} `IOException` if the store cannot be read.
|
|
192
|
+
*/
|
|
193
|
+
refresh(): void;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Look up a live session by id, across every engine in this process.
|
|
197
|
+
*
|
|
198
|
+
* @returns The {@link Session}, or `undefined` if no session with that id
|
|
199
|
+
* exists (never created, or its engine has been stopped).
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* const session = lookupSession(new SessionID('FIX.4.4', 'CLIENT', 'BROKER'));
|
|
204
|
+
* console.log(session?.isLoggedOn());
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export declare function lookupSession(sessionID: SessionID): Session | undefined;
|
|
208
|
+
/** Whether a session with this id exists in this process. */
|
|
209
|
+
export declare function doesSessionExist(sessionID: SessionID): boolean;
|
|
210
|
+
/** The ids of every session in this process, across all engines. */
|
|
211
|
+
export declare function getSessions(): SessionID[];
|
|
212
|
+
/** The number of sessions in this process, across all engines. */
|
|
213
|
+
export declare function numSessions(): number;
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ergonomic wrapper around a live native `FIX::Session`.
|
|
3
|
+
*/
|
|
4
|
+
import { native } from './native.js';
|
|
5
|
+
import { SessionID, toNativeSessionID } from './session-id.js';
|
|
6
|
+
/**
|
|
7
|
+
* A handle on one live FIX session (wraps `FIX::Session`).
|
|
8
|
+
*
|
|
9
|
+
* Obtain one with {@link lookupSession} (any session in the process) or
|
|
10
|
+
* {@link Engine.getSession} (a session of a particular engine); sessions
|
|
11
|
+
* cannot be constructed directly.
|
|
12
|
+
*
|
|
13
|
+
* **Lifetime.** The underlying `FIX::Session` is owned by its engine. This
|
|
14
|
+
* handle stores only the {@link SessionID} and the identity of the engine it
|
|
15
|
+
* was obtained from, and re-resolves the session on every call, so it never
|
|
16
|
+
* dangles: once that engine has been stopped (which destroys its sessions)
|
|
17
|
+
* every method throws a {@link QuickFixError} whose `fixErrorName` is
|
|
18
|
+
* `'SessionNotFound'` -- also after a new engine has been created with the
|
|
19
|
+
* same id. A handle never crosses over to another engine's session; obtain a
|
|
20
|
+
* new one from the new engine.
|
|
21
|
+
*
|
|
22
|
+
* **Everything is synchronous.** State queries, sequence-number access, the
|
|
23
|
+
* runtime option getters/setters, {@link Session.logon}, {@link Session.logout}
|
|
24
|
+
* and {@link Session.refresh} never take the mutex QuickFIX holds while running
|
|
25
|
+
* your application handlers, so they cannot deadlock the main thread, and calls
|
|
26
|
+
* made in program order take effect in program order. ({@link Session.logon} /
|
|
27
|
+
* {@link Session.logout} flip a plain flag the engine's network thread reads
|
|
28
|
+
* without a lock, exactly as QuickFIX's own `Initiator::stop()` does; the
|
|
29
|
+
* engine picks the change up on its next pass.) QuickFIX's
|
|
30
|
+
* `Session::disconnect()` / `reset()` are deliberately not exposed: they are
|
|
31
|
+
* only safe on the engine's own network thread (see ARCHITECTURE.md §5). Use
|
|
32
|
+
* {@link Session.logout} to bring a session down, and the sequence-number
|
|
33
|
+
* setters to reset its numbering -- on a quiet session; see
|
|
34
|
+
* {@link Session.setNextSenderMsgSeqNum}.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const session = initiator.getSession(sessionID)!;
|
|
39
|
+
* if (session.isLoggedOn()) {
|
|
40
|
+
* console.log('next outbound seq', session.getExpectedSenderNum());
|
|
41
|
+
* session.logout('maintenance');
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class Session {
|
|
46
|
+
/** @internal Underlying native handle. */
|
|
47
|
+
#native;
|
|
48
|
+
/** @internal Use {@link lookupSession} / {@link Engine.getSession}. */
|
|
49
|
+
constructor(handle) {
|
|
50
|
+
this.#native = handle;
|
|
51
|
+
}
|
|
52
|
+
/** @internal Wrap an existing native session handle. */
|
|
53
|
+
static fromNative(handle) {
|
|
54
|
+
return new Session(handle);
|
|
55
|
+
}
|
|
56
|
+
/** @internal The native handle backing this session. */
|
|
57
|
+
get nativeHandle() {
|
|
58
|
+
return this.#native;
|
|
59
|
+
}
|
|
60
|
+
/** The id this handle refers to. Always available, even after the session is gone. */
|
|
61
|
+
get sessionID() {
|
|
62
|
+
return SessionID.fromNative(this.#native.getSessionID());
|
|
63
|
+
}
|
|
64
|
+
// --- state -----------------------------------------------------------------
|
|
65
|
+
/** `true` once a Logon has been both sent and received. */
|
|
66
|
+
isLoggedOn() {
|
|
67
|
+
return this.#native.isLoggedOn();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether the session is enabled. `false` after {@link Session.logout} until
|
|
71
|
+
* {@link Session.logon}; a disabled initiator session is not reconnected.
|
|
72
|
+
*/
|
|
73
|
+
isEnabled() {
|
|
74
|
+
return this.#native.isEnabled();
|
|
75
|
+
}
|
|
76
|
+
/** Whether this side has sent a Logon on the current connection. */
|
|
77
|
+
sentLogon() {
|
|
78
|
+
return this.#native.sentLogon();
|
|
79
|
+
}
|
|
80
|
+
/** Whether this side has sent a Logout on the current connection. */
|
|
81
|
+
sentLogout() {
|
|
82
|
+
return this.#native.sentLogout();
|
|
83
|
+
}
|
|
84
|
+
/** Whether a Logon has been received on the current connection. */
|
|
85
|
+
receivedLogon() {
|
|
86
|
+
return this.#native.receivedLogon();
|
|
87
|
+
}
|
|
88
|
+
/** `true` for a session owned by an {@link Initiator}. */
|
|
89
|
+
isInitiator() {
|
|
90
|
+
return this.#native.isInitiator();
|
|
91
|
+
}
|
|
92
|
+
/** `true` for a session owned by an {@link Acceptor}. */
|
|
93
|
+
isAcceptor() {
|
|
94
|
+
return this.#native.isAcceptor();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
98
|
+
* configured `StartTime`/`EndTime` window. Throws `RangeError` for an
|
|
99
|
+
* invalid `Date`.
|
|
100
|
+
*/
|
|
101
|
+
isSessionTime(now) {
|
|
102
|
+
return this.#native.isSessionTime(now?.getTime());
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
106
|
+
* configured `LogonTime`/`LogoutTime` window. Throws `RangeError` for an
|
|
107
|
+
* invalid `Date`.
|
|
108
|
+
*/
|
|
109
|
+
isLogonTime(now) {
|
|
110
|
+
return this.#native.isLogonTime(now?.getTime());
|
|
111
|
+
}
|
|
112
|
+
// --- sequence numbers --------------------------------------------------------
|
|
113
|
+
/** The MsgSeqNum (tag 34) the next outbound message will carry. */
|
|
114
|
+
getExpectedSenderNum() {
|
|
115
|
+
return this.#native.getExpectedSenderNum();
|
|
116
|
+
}
|
|
117
|
+
/** The MsgSeqNum (tag 34) expected on the next inbound message. */
|
|
118
|
+
getExpectedTargetNum() {
|
|
119
|
+
return this.#native.getExpectedTargetNum();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Set the MsgSeqNum the next outbound message will carry (writes to the
|
|
123
|
+
* message store; a file store may throw `IOException`).
|
|
124
|
+
*
|
|
125
|
+
* **Call it on a quiet session.** The setter is not serialised against a
|
|
126
|
+
* message the engine is sending or receiving on this session at the same
|
|
127
|
+
* time: QuickFIX reads the next number into the outbound header, runs your
|
|
128
|
+
* `toApp`/`toAdmin` handler, then increments from the store, all under a
|
|
129
|
+
* lock this call does not (and must not) take. A value set inside that
|
|
130
|
+
* window -- from a handler, or while a {@link sendToTarget} promise or a
|
|
131
|
+
* heartbeat is in flight on this session -- is overwritten by the increment,
|
|
132
|
+
* and the session ends up one off from its counterparty (a Resend Request or
|
|
133
|
+
* a "MsgSeqNum too low" Logout). Set it before {@link Engine.start}, from a
|
|
134
|
+
* `'logout'` event, or when you know nothing is in flight; never from a
|
|
135
|
+
* handler.
|
|
136
|
+
*
|
|
137
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
138
|
+
* 1-based).
|
|
139
|
+
*/
|
|
140
|
+
setNextSenderMsgSeqNum(seqNum) {
|
|
141
|
+
this.#native.setNextSenderMsgSeqNum(seqNum);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Set the MsgSeqNum expected on the next inbound message (writes to the
|
|
145
|
+
* message store; a file store may throw `IOException`).
|
|
146
|
+
*
|
|
147
|
+
* **Call it on a quiet session.** Like {@link Session.setNextSenderMsgSeqNum},
|
|
148
|
+
* it is not serialised against a message the engine is processing on this
|
|
149
|
+
* session: QuickFIX checks the inbound number, runs your `fromApp`/`fromAdmin`
|
|
150
|
+
* handler, then increments the expected number from the store under a lock
|
|
151
|
+
* this call does not take, so a value set meanwhile is overwritten. Set it
|
|
152
|
+
* before {@link Engine.start}, from a `'logout'` event, or when you know
|
|
153
|
+
* nothing is in flight; never from a handler.
|
|
154
|
+
*
|
|
155
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
156
|
+
* 1-based).
|
|
157
|
+
*/
|
|
158
|
+
setNextTargetMsgSeqNum(seqNum) {
|
|
159
|
+
this.#native.setNextTargetMsgSeqNum(seqNum);
|
|
160
|
+
}
|
|
161
|
+
// --- runtime options -----------------------------------------------------------
|
|
162
|
+
// Each pair mirrors a QuickFIX session setting; the setter changes the LIVE
|
|
163
|
+
// session only (the SessionSettings it was created from are not updated).
|
|
164
|
+
/** `ResetOnLogon` */
|
|
165
|
+
getResetOnLogon() {
|
|
166
|
+
return this.#native.getResetOnLogon();
|
|
167
|
+
}
|
|
168
|
+
setResetOnLogon(value) {
|
|
169
|
+
this.#native.setResetOnLogon(value);
|
|
170
|
+
}
|
|
171
|
+
/** `ResetOnLogout` */
|
|
172
|
+
getResetOnLogout() {
|
|
173
|
+
return this.#native.getResetOnLogout();
|
|
174
|
+
}
|
|
175
|
+
setResetOnLogout(value) {
|
|
176
|
+
this.#native.setResetOnLogout(value);
|
|
177
|
+
}
|
|
178
|
+
/** `ResetOnDisconnect` */
|
|
179
|
+
getResetOnDisconnect() {
|
|
180
|
+
return this.#native.getResetOnDisconnect();
|
|
181
|
+
}
|
|
182
|
+
setResetOnDisconnect(value) {
|
|
183
|
+
this.#native.setResetOnDisconnect(value);
|
|
184
|
+
}
|
|
185
|
+
/** `RefreshOnLogon` */
|
|
186
|
+
getRefreshOnLogon() {
|
|
187
|
+
return this.#native.getRefreshOnLogon();
|
|
188
|
+
}
|
|
189
|
+
setRefreshOnLogon(value) {
|
|
190
|
+
this.#native.setRefreshOnLogon(value);
|
|
191
|
+
}
|
|
192
|
+
/** `CheckCompID` */
|
|
193
|
+
getCheckCompId() {
|
|
194
|
+
return this.#native.getCheckCompId();
|
|
195
|
+
}
|
|
196
|
+
setCheckCompId(value) {
|
|
197
|
+
this.#native.setCheckCompId(value);
|
|
198
|
+
}
|
|
199
|
+
/** `CheckLatency` */
|
|
200
|
+
getCheckLatency() {
|
|
201
|
+
return this.#native.getCheckLatency();
|
|
202
|
+
}
|
|
203
|
+
setCheckLatency(value) {
|
|
204
|
+
this.#native.setCheckLatency(value);
|
|
205
|
+
}
|
|
206
|
+
/** `PersistMessages` */
|
|
207
|
+
getPersistMessages() {
|
|
208
|
+
return this.#native.getPersistMessages();
|
|
209
|
+
}
|
|
210
|
+
setPersistMessages(value) {
|
|
211
|
+
this.#native.setPersistMessages(value);
|
|
212
|
+
}
|
|
213
|
+
/** `SendRedundantResendRequests` */
|
|
214
|
+
getSendRedundantResendRequests() {
|
|
215
|
+
return this.#native.getSendRedundantResendRequests();
|
|
216
|
+
}
|
|
217
|
+
setSendRedundantResendRequests(value) {
|
|
218
|
+
this.#native.setSendRedundantResendRequests(value);
|
|
219
|
+
}
|
|
220
|
+
/** `ValidateLengthAndChecksum` */
|
|
221
|
+
getValidateLengthAndChecksum() {
|
|
222
|
+
return this.#native.getValidateLengthAndChecksum();
|
|
223
|
+
}
|
|
224
|
+
setValidateLengthAndChecksum(value) {
|
|
225
|
+
this.#native.setValidateLengthAndChecksum(value);
|
|
226
|
+
}
|
|
227
|
+
/** `SendNextExpectedMsgSeqNum` */
|
|
228
|
+
getSendNextExpectedMsgSeqNum() {
|
|
229
|
+
return this.#native.getSendNextExpectedMsgSeqNum();
|
|
230
|
+
}
|
|
231
|
+
setSendNextExpectedMsgSeqNum(value) {
|
|
232
|
+
this.#native.setSendNextExpectedMsgSeqNum(value);
|
|
233
|
+
}
|
|
234
|
+
/** `NonStopSession` */
|
|
235
|
+
getIsNonStopSession() {
|
|
236
|
+
return this.#native.getIsNonStopSession();
|
|
237
|
+
}
|
|
238
|
+
setIsNonStopSession(value) {
|
|
239
|
+
this.#native.setIsNonStopSession(value);
|
|
240
|
+
}
|
|
241
|
+
/** `LogonTimeout`, in seconds. */
|
|
242
|
+
getLogonTimeout() {
|
|
243
|
+
return this.#native.getLogonTimeout();
|
|
244
|
+
}
|
|
245
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
246
|
+
setLogonTimeout(seconds) {
|
|
247
|
+
this.#native.setLogonTimeout(seconds);
|
|
248
|
+
}
|
|
249
|
+
/** `LogoutTimeout`, in seconds. */
|
|
250
|
+
getLogoutTimeout() {
|
|
251
|
+
return this.#native.getLogoutTimeout();
|
|
252
|
+
}
|
|
253
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
254
|
+
setLogoutTimeout(seconds) {
|
|
255
|
+
this.#native.setLogoutTimeout(seconds);
|
|
256
|
+
}
|
|
257
|
+
/** `MaxLatency`, in seconds. */
|
|
258
|
+
getMaxLatency() {
|
|
259
|
+
return this.#native.getMaxLatency();
|
|
260
|
+
}
|
|
261
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
262
|
+
setMaxLatency(seconds) {
|
|
263
|
+
this.#native.setMaxLatency(seconds);
|
|
264
|
+
}
|
|
265
|
+
/** `TimestampPrecision` (number of sub-second digits, `0..9`). */
|
|
266
|
+
getTimestampPrecision() {
|
|
267
|
+
return this.#native.getTimestampPrecision();
|
|
268
|
+
}
|
|
269
|
+
/** @throws `RangeError` unless `precision` is an integer in `0..9`. */
|
|
270
|
+
setTimestampPrecision(precision) {
|
|
271
|
+
this.#native.setTimestampPrecision(precision);
|
|
272
|
+
}
|
|
273
|
+
// --- control -----------------------------------------------------------------------
|
|
274
|
+
/**
|
|
275
|
+
* Enable the session. An initiator session reconnects and logs on at its
|
|
276
|
+
* next `ReconnectInterval`; an acceptor session accepts the next inbound
|
|
277
|
+
* Logon. Takes effect immediately ({@link Session.isEnabled} is `true` on
|
|
278
|
+
* return); observe the handshake through the engine's `'logon'` event.
|
|
279
|
+
*/
|
|
280
|
+
logon() {
|
|
281
|
+
this.#native.logon();
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Disable the session and initiate a graceful Logout (sent with `reason` as
|
|
285
|
+
* tag 58 on the next engine tick). The session stays disabled — it will not
|
|
286
|
+
* reconnect — until {@link Session.logon}. Takes effect immediately
|
|
287
|
+
* ({@link Session.isEnabled} is `false` on return); observe completion
|
|
288
|
+
* through the engine's `'logout'` event.
|
|
289
|
+
*/
|
|
290
|
+
logout(reason) {
|
|
291
|
+
this.#native.logout(reason);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Re-read the session state (sequence numbers etc.) from the message store.
|
|
295
|
+
*
|
|
296
|
+
* @throws {QuickFixError} `IOException` if the store cannot be read.
|
|
297
|
+
*/
|
|
298
|
+
refresh() {
|
|
299
|
+
this.#native.refresh();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Look up a live session by id, across every engine in this process.
|
|
304
|
+
*
|
|
305
|
+
* @returns The {@link Session}, or `undefined` if no session with that id
|
|
306
|
+
* exists (never created, or its engine has been stopped).
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* const session = lookupSession(new SessionID('FIX.4.4', 'CLIENT', 'BROKER'));
|
|
311
|
+
* console.log(session?.isLoggedOn());
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
export function lookupSession(sessionID) {
|
|
315
|
+
const handle = native.lookupSession(toNativeSessionID(sessionID, 'sessionID'));
|
|
316
|
+
return handle ? Session.fromNative(handle) : undefined;
|
|
317
|
+
}
|
|
318
|
+
/** Whether a session with this id exists in this process. */
|
|
319
|
+
export function doesSessionExist(sessionID) {
|
|
320
|
+
return native.doesSessionExist(toNativeSessionID(sessionID, 'sessionID'));
|
|
321
|
+
}
|
|
322
|
+
/** The ids of every session in this process, across all engines. */
|
|
323
|
+
export function getSessions() {
|
|
324
|
+
return native.getSessions().map((h) => SessionID.fromNative(h));
|
|
325
|
+
}
|
|
326
|
+
/** The number of sessions in this process, across all engines. */
|
|
327
|
+
export function numSessions() {
|
|
328
|
+
return native.numSessions();
|
|
329
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process TLS tunnel for initiators: the stunnel client model.
|
|
3
|
+
*
|
|
4
|
+
* QuickFIX is built without its OpenSSL-based `SSLSocketInitiator` (see
|
|
5
|
+
* CMakeLists.txt), so TLS is terminated in Node instead: the tunnel listens on
|
|
6
|
+
* a loopback port, the plain `FIX::SocketInitiator` connects to it (point
|
|
7
|
+
* `SocketConnectHost`/`SocketConnectPort` at {@link TlsTunnel.host} /
|
|
8
|
+
* {@link TlsTunnel.port}), and every accepted connection is carried to the real
|
|
9
|
+
* counterparty over `tls.connect`.
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'node:events';
|
|
12
|
+
import { type Server } from 'node:net';
|
|
13
|
+
import { type CipherNameAndProtocol, type ConnectionOptions, type PeerCertificate } from 'node:tls';
|
|
14
|
+
/** Options for {@link createTlsTunnel}. */
|
|
15
|
+
export interface TlsTunnelOptions {
|
|
16
|
+
/** Host name or IP of the counterparty's TLS endpoint. */
|
|
17
|
+
host: string;
|
|
18
|
+
/** Port of the counterparty's TLS endpoint. */
|
|
19
|
+
port: number;
|
|
20
|
+
/**
|
|
21
|
+
* Passed to `tls.connect()` for every connection (CA, client certificate for
|
|
22
|
+
* mTLS, `minVersion`, ciphers, ...). `servername` (SNI, and the name the
|
|
23
|
+
* server certificate is checked against) defaults to `host` unless `host` is
|
|
24
|
+
* an IP address.
|
|
25
|
+
*/
|
|
26
|
+
tls?: Omit<ConnectionOptions, 'host' | 'port' | 'socket' | 'path'>;
|
|
27
|
+
/**
|
|
28
|
+
* Loopback port to listen on. Defaults to `0`, an ephemeral port read back
|
|
29
|
+
* from {@link TlsTunnel.port}. Set it to point a static `.cfg` file at the
|
|
30
|
+
* tunnel, like stunnel's `accept = 127.0.0.1:<port>`.
|
|
31
|
+
*/
|
|
32
|
+
localPort?: number;
|
|
33
|
+
}
|
|
34
|
+
/** Details of an established TLS connection, emitted as `'secureConnect'`. */
|
|
35
|
+
export interface TlsTunnelSecureConnectInfo {
|
|
36
|
+
/** Whether the server certificate was verified against the trusted CAs. */
|
|
37
|
+
authorized: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Why verification failed, as the OpenSSL verification code (e.g.
|
|
40
|
+
* `'UNABLE_TO_VERIFY_LEAF_SIGNATURE'`, the `code` a `'tlsError'` carries
|
|
41
|
+
* otherwise). Only reachable with `rejectUnauthorized: false`.
|
|
42
|
+
*/
|
|
43
|
+
authorizationError?: string;
|
|
44
|
+
/** Negotiated protocol, e.g. `'TLSv1.3'`. */
|
|
45
|
+
protocol: string | null;
|
|
46
|
+
/** Negotiated cipher. */
|
|
47
|
+
cipher: CipherNameAndProtocol;
|
|
48
|
+
/** The server's certificate (an empty object if it sent none). */
|
|
49
|
+
peerCertificate: PeerCertificate;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Events emitted by a {@link TlsTunnel}. There is deliberately no `'error'`
|
|
53
|
+
* event: connection failures are reported as `'tlsError'`, so a tunnel without
|
|
54
|
+
* listeners never crashes the process.
|
|
55
|
+
*/
|
|
56
|
+
export interface TlsTunnelEvents {
|
|
57
|
+
/** A connection to the counterparty completed its TLS handshake. */
|
|
58
|
+
secureConnect: [info: TlsTunnelSecureConnectInfo];
|
|
59
|
+
/**
|
|
60
|
+
* A connection to the counterparty failed (DNS, TCP, handshake, certificate
|
|
61
|
+
* verification, `tls` options `tls.connect` rejects) or broke. The
|
|
62
|
+
* initiator's loopback connection is closed with it, so QuickFIX reconnects
|
|
63
|
+
* after its `ReconnectInterval`. Also emitted when the loopback server fails
|
|
64
|
+
* to accept a connection (e.g. `EMFILE`).
|
|
65
|
+
*/
|
|
66
|
+
tlsError: [error: Error];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A running TLS tunnel, created by {@link createTlsTunnel}.
|
|
70
|
+
*
|
|
71
|
+
* Its handles are unref'd: the tunnel alone never keeps the process alive (a
|
|
72
|
+
* started engine does). Close it after the engine has stopped, so a graceful
|
|
73
|
+
* Logout still reaches the counterparty.
|
|
74
|
+
*/
|
|
75
|
+
export declare class TlsTunnel extends EventEmitter {
|
|
76
|
+
#private;
|
|
77
|
+
/** Loopback address the tunnel listens on (`SocketConnectHost`). */
|
|
78
|
+
readonly host = "127.0.0.1";
|
|
79
|
+
/** Loopback port the tunnel listens on (`SocketConnectPort`). */
|
|
80
|
+
readonly port: number;
|
|
81
|
+
/** The counterparty's TLS endpoint. */
|
|
82
|
+
readonly remote: {
|
|
83
|
+
readonly host: string;
|
|
84
|
+
readonly port: number;
|
|
85
|
+
};
|
|
86
|
+
/** @internal Use {@link createTlsTunnel}. */
|
|
87
|
+
constructor(server: Server, port: number, options: TlsTunnelOptions);
|
|
88
|
+
/**
|
|
89
|
+
* Stop listening and destroy every live connection. Idempotent: later calls
|
|
90
|
+
* return the same Promise.
|
|
91
|
+
*/
|
|
92
|
+
close(): Promise<void>;
|
|
93
|
+
/** Same as {@link TlsTunnel.close}; enables `await using tunnel = ...`. */
|
|
94
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Open a TLS tunnel to `options.host`:`options.port` and resolve once it
|
|
98
|
+
* listens on a loopback port.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const tunnel = await createTlsTunnel({
|
|
103
|
+
* host: 'fix.broker.com',
|
|
104
|
+
* port: 9443,
|
|
105
|
+
* tls: { ca: readFileSync('broker-ca.pem') },
|
|
106
|
+
* });
|
|
107
|
+
* tunnel.on('tlsError', (err) => console.error('TLS:', err.message));
|
|
108
|
+
* const settings = SessionSettings.fromString(`
|
|
109
|
+
* [DEFAULT]
|
|
110
|
+
* ConnectionType=initiator
|
|
111
|
+
* SocketConnectHost=${tunnel.host}
|
|
112
|
+
* SocketConnectPort=${tunnel.port}
|
|
113
|
+
* ...`);
|
|
114
|
+
* const initiator = new Initiator({ settings, handlers });
|
|
115
|
+
* await initiator.start();
|
|
116
|
+
* // ... later: engine first, so the Logout still crosses the tunnel
|
|
117
|
+
* await initiator.stop();
|
|
118
|
+
* await tunnel.close();
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @returns A Promise that rejects with a `TypeError` if `host`, `port` or
|
|
122
|
+
* `localPort` is invalid, and with the listen error (e.g. `EADDRINUSE` for a
|
|
123
|
+
* fixed `localPort` already in use) if the loopback port cannot be bound.
|
|
124
|
+
*/
|
|
125
|
+
export declare function createTlsTunnel(options: TlsTunnelOptions): Promise<TlsTunnel>;
|
|
126
|
+
export interface TlsTunnel {
|
|
127
|
+
/** Register a listener for a tunnel event (typed per {@link TlsTunnelEvents}). */
|
|
128
|
+
on<E extends keyof TlsTunnelEvents>(event: E, listener: (...args: TlsTunnelEvents[E]) => void): this;
|
|
129
|
+
/** Register a one-shot listener for a tunnel event. */
|
|
130
|
+
once<E extends keyof TlsTunnelEvents>(event: E, listener: (...args: TlsTunnelEvents[E]) => void): this;
|
|
131
|
+
/** Alias of {@link TlsTunnel.on}. */
|
|
132
|
+
addListener<E extends keyof TlsTunnelEvents>(event: E, listener: (...args: TlsTunnelEvents[E]) => void): this;
|
|
133
|
+
/** Remove a previously registered listener. */
|
|
134
|
+
off<E extends keyof TlsTunnelEvents>(event: E, listener: (...args: TlsTunnelEvents[E]) => void): this;
|
|
135
|
+
/** Alias of {@link TlsTunnel.off}. */
|
|
136
|
+
removeListener<E extends keyof TlsTunnelEvents>(event: E, listener: (...args: TlsTunnelEvents[E]) => void): this;
|
|
137
|
+
/** Remove all listeners (optionally for a single event). */
|
|
138
|
+
removeAllListeners<E extends keyof TlsTunnelEvents>(event?: E): this;
|
|
139
|
+
/** Emit a tunnel event with its typed arguments. */
|
|
140
|
+
emit<E extends keyof TlsTunnelEvents>(event: E, ...args: TlsTunnelEvents[E]): boolean;
|
|
141
|
+
/** List the listeners registered for an event. */
|
|
142
|
+
listeners<E extends keyof TlsTunnelEvents>(event: E): Array<(...args: TlsTunnelEvents[E]) => void>;
|
|
143
|
+
}
|