@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
package/dist/cjs/session-id.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SessionID = void 0;
|
|
4
|
+
exports.toNativeSessionID = toNativeSessionID;
|
|
4
5
|
/**
|
|
5
6
|
* Ergonomic wrapper around the native `FIX::SessionID`.
|
|
6
7
|
*/
|
|
@@ -64,4 +65,23 @@ class SessionID {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
exports.SessionID = SessionID;
|
|
67
|
-
|
|
68
|
+
/**
|
|
69
|
+
* @internal The native handle behind a {@link SessionID} argument.
|
|
70
|
+
*
|
|
71
|
+
* Duck-typed rather than `instanceof SessionID`: this package ships ESM and
|
|
72
|
+
* CJS builds, and a `SessionID` created by the other build is a different class
|
|
73
|
+
* with the same shape. Both builds load the same native addon, so its
|
|
74
|
+
* `SessionIDWrap` class is the one thing they share, and that is what the
|
|
75
|
+
* handle is checked against. Anything else -- `null`, `{}`, a `Session` -- is
|
|
76
|
+
* a `TypeError` naming the argument, rather than silently degrading to an
|
|
77
|
+
* `undefined` handle that the callee would read as "no session id given".
|
|
78
|
+
*/
|
|
79
|
+
function toNativeSessionID(value, name) {
|
|
80
|
+
const handle = typeof value === 'object' && value !== null
|
|
81
|
+
? value.nativeHandle
|
|
82
|
+
: undefined;
|
|
83
|
+
if (!(handle instanceof native_js_1.native.SessionIDWrap)) {
|
|
84
|
+
throw new TypeError(`${name} must be a SessionID`);
|
|
85
|
+
}
|
|
86
|
+
return handle;
|
|
87
|
+
}
|
|
@@ -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,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Session = void 0;
|
|
4
|
+
exports.lookupSession = lookupSession;
|
|
5
|
+
exports.doesSessionExist = doesSessionExist;
|
|
6
|
+
exports.getSessions = getSessions;
|
|
7
|
+
exports.numSessions = numSessions;
|
|
8
|
+
/**
|
|
9
|
+
* Ergonomic wrapper around a live native `FIX::Session`.
|
|
10
|
+
*/
|
|
11
|
+
const native_js_1 = require("./native.js");
|
|
12
|
+
const session_id_js_1 = require("./session-id.js");
|
|
13
|
+
/**
|
|
14
|
+
* A handle on one live FIX session (wraps `FIX::Session`).
|
|
15
|
+
*
|
|
16
|
+
* Obtain one with {@link lookupSession} (any session in the process) or
|
|
17
|
+
* {@link Engine.getSession} (a session of a particular engine); sessions
|
|
18
|
+
* cannot be constructed directly.
|
|
19
|
+
*
|
|
20
|
+
* **Lifetime.** The underlying `FIX::Session` is owned by its engine. This
|
|
21
|
+
* handle stores only the {@link SessionID} and the identity of the engine it
|
|
22
|
+
* was obtained from, and re-resolves the session on every call, so it never
|
|
23
|
+
* dangles: once that engine has been stopped (which destroys its sessions)
|
|
24
|
+
* every method throws a {@link QuickFixError} whose `fixErrorName` is
|
|
25
|
+
* `'SessionNotFound'` -- also after a new engine has been created with the
|
|
26
|
+
* same id. A handle never crosses over to another engine's session; obtain a
|
|
27
|
+
* new one from the new engine.
|
|
28
|
+
*
|
|
29
|
+
* **Everything is synchronous.** State queries, sequence-number access, the
|
|
30
|
+
* runtime option getters/setters, {@link Session.logon}, {@link Session.logout}
|
|
31
|
+
* and {@link Session.refresh} never take the mutex QuickFIX holds while running
|
|
32
|
+
* your application handlers, so they cannot deadlock the main thread, and calls
|
|
33
|
+
* made in program order take effect in program order. ({@link Session.logon} /
|
|
34
|
+
* {@link Session.logout} flip a plain flag the engine's network thread reads
|
|
35
|
+
* without a lock, exactly as QuickFIX's own `Initiator::stop()` does; the
|
|
36
|
+
* engine picks the change up on its next pass.) QuickFIX's
|
|
37
|
+
* `Session::disconnect()` / `reset()` are deliberately not exposed: they are
|
|
38
|
+
* only safe on the engine's own network thread (see ARCHITECTURE.md §5). Use
|
|
39
|
+
* {@link Session.logout} to bring a session down, and the sequence-number
|
|
40
|
+
* setters to reset its numbering -- on a quiet session; see
|
|
41
|
+
* {@link Session.setNextSenderMsgSeqNum}.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const session = initiator.getSession(sessionID)!;
|
|
46
|
+
* if (session.isLoggedOn()) {
|
|
47
|
+
* console.log('next outbound seq', session.getExpectedSenderNum());
|
|
48
|
+
* session.logout('maintenance');
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
class Session {
|
|
53
|
+
/** @internal Underlying native handle. */
|
|
54
|
+
#native;
|
|
55
|
+
/** @internal Use {@link lookupSession} / {@link Engine.getSession}. */
|
|
56
|
+
constructor(handle) {
|
|
57
|
+
this.#native = handle;
|
|
58
|
+
}
|
|
59
|
+
/** @internal Wrap an existing native session handle. */
|
|
60
|
+
static fromNative(handle) {
|
|
61
|
+
return new Session(handle);
|
|
62
|
+
}
|
|
63
|
+
/** @internal The native handle backing this session. */
|
|
64
|
+
get nativeHandle() {
|
|
65
|
+
return this.#native;
|
|
66
|
+
}
|
|
67
|
+
/** The id this handle refers to. Always available, even after the session is gone. */
|
|
68
|
+
get sessionID() {
|
|
69
|
+
return session_id_js_1.SessionID.fromNative(this.#native.getSessionID());
|
|
70
|
+
}
|
|
71
|
+
// --- state -----------------------------------------------------------------
|
|
72
|
+
/** `true` once a Logon has been both sent and received. */
|
|
73
|
+
isLoggedOn() {
|
|
74
|
+
return this.#native.isLoggedOn();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether the session is enabled. `false` after {@link Session.logout} until
|
|
78
|
+
* {@link Session.logon}; a disabled initiator session is not reconnected.
|
|
79
|
+
*/
|
|
80
|
+
isEnabled() {
|
|
81
|
+
return this.#native.isEnabled();
|
|
82
|
+
}
|
|
83
|
+
/** Whether this side has sent a Logon on the current connection. */
|
|
84
|
+
sentLogon() {
|
|
85
|
+
return this.#native.sentLogon();
|
|
86
|
+
}
|
|
87
|
+
/** Whether this side has sent a Logout on the current connection. */
|
|
88
|
+
sentLogout() {
|
|
89
|
+
return this.#native.sentLogout();
|
|
90
|
+
}
|
|
91
|
+
/** Whether a Logon has been received on the current connection. */
|
|
92
|
+
receivedLogon() {
|
|
93
|
+
return this.#native.receivedLogon();
|
|
94
|
+
}
|
|
95
|
+
/** `true` for a session owned by an {@link Initiator}. */
|
|
96
|
+
isInitiator() {
|
|
97
|
+
return this.#native.isInitiator();
|
|
98
|
+
}
|
|
99
|
+
/** `true` for a session owned by an {@link Acceptor}. */
|
|
100
|
+
isAcceptor() {
|
|
101
|
+
return this.#native.isAcceptor();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
105
|
+
* configured `StartTime`/`EndTime` window. Throws `RangeError` for an
|
|
106
|
+
* invalid `Date`.
|
|
107
|
+
*/
|
|
108
|
+
isSessionTime(now) {
|
|
109
|
+
return this.#native.isSessionTime(now?.getTime());
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether `now` (default: the current time) falls inside the session's
|
|
113
|
+
* configured `LogonTime`/`LogoutTime` window. Throws `RangeError` for an
|
|
114
|
+
* invalid `Date`.
|
|
115
|
+
*/
|
|
116
|
+
isLogonTime(now) {
|
|
117
|
+
return this.#native.isLogonTime(now?.getTime());
|
|
118
|
+
}
|
|
119
|
+
// --- sequence numbers --------------------------------------------------------
|
|
120
|
+
/** The MsgSeqNum (tag 34) the next outbound message will carry. */
|
|
121
|
+
getExpectedSenderNum() {
|
|
122
|
+
return this.#native.getExpectedSenderNum();
|
|
123
|
+
}
|
|
124
|
+
/** The MsgSeqNum (tag 34) expected on the next inbound message. */
|
|
125
|
+
getExpectedTargetNum() {
|
|
126
|
+
return this.#native.getExpectedTargetNum();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Set the MsgSeqNum the next outbound message will carry (writes to the
|
|
130
|
+
* message store; a file store may throw `IOException`).
|
|
131
|
+
*
|
|
132
|
+
* **Call it on a quiet session.** The setter is not serialised against a
|
|
133
|
+
* message the engine is sending or receiving on this session at the same
|
|
134
|
+
* time: QuickFIX reads the next number into the outbound header, runs your
|
|
135
|
+
* `toApp`/`toAdmin` handler, then increments from the store, all under a
|
|
136
|
+
* lock this call does not (and must not) take. A value set inside that
|
|
137
|
+
* window -- from a handler, or while a {@link sendToTarget} promise or a
|
|
138
|
+
* heartbeat is in flight on this session -- is overwritten by the increment,
|
|
139
|
+
* and the session ends up one off from its counterparty (a Resend Request or
|
|
140
|
+
* a "MsgSeqNum too low" Logout). Set it before {@link Engine.start}, from a
|
|
141
|
+
* `'logout'` event, or when you know nothing is in flight; never from a
|
|
142
|
+
* handler.
|
|
143
|
+
*
|
|
144
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
145
|
+
* 1-based).
|
|
146
|
+
*/
|
|
147
|
+
setNextSenderMsgSeqNum(seqNum) {
|
|
148
|
+
this.#native.setNextSenderMsgSeqNum(seqNum);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Set the MsgSeqNum expected on the next inbound message (writes to the
|
|
152
|
+
* message store; a file store may throw `IOException`).
|
|
153
|
+
*
|
|
154
|
+
* **Call it on a quiet session.** Like {@link Session.setNextSenderMsgSeqNum},
|
|
155
|
+
* it is not serialised against a message the engine is processing on this
|
|
156
|
+
* session: QuickFIX checks the inbound number, runs your `fromApp`/`fromAdmin`
|
|
157
|
+
* handler, then increments the expected number from the store under a lock
|
|
158
|
+
* this call does not take, so a value set meanwhile is overwritten. Set it
|
|
159
|
+
* before {@link Engine.start}, from a `'logout'` event, or when you know
|
|
160
|
+
* nothing is in flight; never from a handler.
|
|
161
|
+
*
|
|
162
|
+
* @throws `RangeError` if `seqNum` is not a positive safe integer (MsgSeqNum is
|
|
163
|
+
* 1-based).
|
|
164
|
+
*/
|
|
165
|
+
setNextTargetMsgSeqNum(seqNum) {
|
|
166
|
+
this.#native.setNextTargetMsgSeqNum(seqNum);
|
|
167
|
+
}
|
|
168
|
+
// --- runtime options -----------------------------------------------------------
|
|
169
|
+
// Each pair mirrors a QuickFIX session setting; the setter changes the LIVE
|
|
170
|
+
// session only (the SessionSettings it was created from are not updated).
|
|
171
|
+
/** `ResetOnLogon` */
|
|
172
|
+
getResetOnLogon() {
|
|
173
|
+
return this.#native.getResetOnLogon();
|
|
174
|
+
}
|
|
175
|
+
setResetOnLogon(value) {
|
|
176
|
+
this.#native.setResetOnLogon(value);
|
|
177
|
+
}
|
|
178
|
+
/** `ResetOnLogout` */
|
|
179
|
+
getResetOnLogout() {
|
|
180
|
+
return this.#native.getResetOnLogout();
|
|
181
|
+
}
|
|
182
|
+
setResetOnLogout(value) {
|
|
183
|
+
this.#native.setResetOnLogout(value);
|
|
184
|
+
}
|
|
185
|
+
/** `ResetOnDisconnect` */
|
|
186
|
+
getResetOnDisconnect() {
|
|
187
|
+
return this.#native.getResetOnDisconnect();
|
|
188
|
+
}
|
|
189
|
+
setResetOnDisconnect(value) {
|
|
190
|
+
this.#native.setResetOnDisconnect(value);
|
|
191
|
+
}
|
|
192
|
+
/** `RefreshOnLogon` */
|
|
193
|
+
getRefreshOnLogon() {
|
|
194
|
+
return this.#native.getRefreshOnLogon();
|
|
195
|
+
}
|
|
196
|
+
setRefreshOnLogon(value) {
|
|
197
|
+
this.#native.setRefreshOnLogon(value);
|
|
198
|
+
}
|
|
199
|
+
/** `CheckCompID` */
|
|
200
|
+
getCheckCompId() {
|
|
201
|
+
return this.#native.getCheckCompId();
|
|
202
|
+
}
|
|
203
|
+
setCheckCompId(value) {
|
|
204
|
+
this.#native.setCheckCompId(value);
|
|
205
|
+
}
|
|
206
|
+
/** `CheckLatency` */
|
|
207
|
+
getCheckLatency() {
|
|
208
|
+
return this.#native.getCheckLatency();
|
|
209
|
+
}
|
|
210
|
+
setCheckLatency(value) {
|
|
211
|
+
this.#native.setCheckLatency(value);
|
|
212
|
+
}
|
|
213
|
+
/** `PersistMessages` */
|
|
214
|
+
getPersistMessages() {
|
|
215
|
+
return this.#native.getPersistMessages();
|
|
216
|
+
}
|
|
217
|
+
setPersistMessages(value) {
|
|
218
|
+
this.#native.setPersistMessages(value);
|
|
219
|
+
}
|
|
220
|
+
/** `SendRedundantResendRequests` */
|
|
221
|
+
getSendRedundantResendRequests() {
|
|
222
|
+
return this.#native.getSendRedundantResendRequests();
|
|
223
|
+
}
|
|
224
|
+
setSendRedundantResendRequests(value) {
|
|
225
|
+
this.#native.setSendRedundantResendRequests(value);
|
|
226
|
+
}
|
|
227
|
+
/** `ValidateLengthAndChecksum` */
|
|
228
|
+
getValidateLengthAndChecksum() {
|
|
229
|
+
return this.#native.getValidateLengthAndChecksum();
|
|
230
|
+
}
|
|
231
|
+
setValidateLengthAndChecksum(value) {
|
|
232
|
+
this.#native.setValidateLengthAndChecksum(value);
|
|
233
|
+
}
|
|
234
|
+
/** `SendNextExpectedMsgSeqNum` */
|
|
235
|
+
getSendNextExpectedMsgSeqNum() {
|
|
236
|
+
return this.#native.getSendNextExpectedMsgSeqNum();
|
|
237
|
+
}
|
|
238
|
+
setSendNextExpectedMsgSeqNum(value) {
|
|
239
|
+
this.#native.setSendNextExpectedMsgSeqNum(value);
|
|
240
|
+
}
|
|
241
|
+
/** `NonStopSession` */
|
|
242
|
+
getIsNonStopSession() {
|
|
243
|
+
return this.#native.getIsNonStopSession();
|
|
244
|
+
}
|
|
245
|
+
setIsNonStopSession(value) {
|
|
246
|
+
this.#native.setIsNonStopSession(value);
|
|
247
|
+
}
|
|
248
|
+
/** `LogonTimeout`, in seconds. */
|
|
249
|
+
getLogonTimeout() {
|
|
250
|
+
return this.#native.getLogonTimeout();
|
|
251
|
+
}
|
|
252
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
253
|
+
setLogonTimeout(seconds) {
|
|
254
|
+
this.#native.setLogonTimeout(seconds);
|
|
255
|
+
}
|
|
256
|
+
/** `LogoutTimeout`, in seconds. */
|
|
257
|
+
getLogoutTimeout() {
|
|
258
|
+
return this.#native.getLogoutTimeout();
|
|
259
|
+
}
|
|
260
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
261
|
+
setLogoutTimeout(seconds) {
|
|
262
|
+
this.#native.setLogoutTimeout(seconds);
|
|
263
|
+
}
|
|
264
|
+
/** `MaxLatency`, in seconds. */
|
|
265
|
+
getMaxLatency() {
|
|
266
|
+
return this.#native.getMaxLatency();
|
|
267
|
+
}
|
|
268
|
+
/** @throws {RangeError} if `seconds` is not an integer in the int32 range. */
|
|
269
|
+
setMaxLatency(seconds) {
|
|
270
|
+
this.#native.setMaxLatency(seconds);
|
|
271
|
+
}
|
|
272
|
+
/** `TimestampPrecision` (number of sub-second digits, `0..9`). */
|
|
273
|
+
getTimestampPrecision() {
|
|
274
|
+
return this.#native.getTimestampPrecision();
|
|
275
|
+
}
|
|
276
|
+
/** @throws `RangeError` unless `precision` is an integer in `0..9`. */
|
|
277
|
+
setTimestampPrecision(precision) {
|
|
278
|
+
this.#native.setTimestampPrecision(precision);
|
|
279
|
+
}
|
|
280
|
+
// --- control -----------------------------------------------------------------------
|
|
281
|
+
/**
|
|
282
|
+
* Enable the session. An initiator session reconnects and logs on at its
|
|
283
|
+
* next `ReconnectInterval`; an acceptor session accepts the next inbound
|
|
284
|
+
* Logon. Takes effect immediately ({@link Session.isEnabled} is `true` on
|
|
285
|
+
* return); observe the handshake through the engine's `'logon'` event.
|
|
286
|
+
*/
|
|
287
|
+
logon() {
|
|
288
|
+
this.#native.logon();
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Disable the session and initiate a graceful Logout (sent with `reason` as
|
|
292
|
+
* tag 58 on the next engine tick). The session stays disabled — it will not
|
|
293
|
+
* reconnect — until {@link Session.logon}. Takes effect immediately
|
|
294
|
+
* ({@link Session.isEnabled} is `false` on return); observe completion
|
|
295
|
+
* through the engine's `'logout'` event.
|
|
296
|
+
*/
|
|
297
|
+
logout(reason) {
|
|
298
|
+
this.#native.logout(reason);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Re-read the session state (sequence numbers etc.) from the message store.
|
|
302
|
+
*
|
|
303
|
+
* @throws {QuickFixError} `IOException` if the store cannot be read.
|
|
304
|
+
*/
|
|
305
|
+
refresh() {
|
|
306
|
+
this.#native.refresh();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.Session = Session;
|
|
310
|
+
/**
|
|
311
|
+
* Look up a live session by id, across every engine in this process.
|
|
312
|
+
*
|
|
313
|
+
* @returns The {@link Session}, or `undefined` if no session with that id
|
|
314
|
+
* exists (never created, or its engine has been stopped).
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* const session = lookupSession(new SessionID('FIX.4.4', 'CLIENT', 'BROKER'));
|
|
319
|
+
* console.log(session?.isLoggedOn());
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
function lookupSession(sessionID) {
|
|
323
|
+
const handle = native_js_1.native.lookupSession((0, session_id_js_1.toNativeSessionID)(sessionID, 'sessionID'));
|
|
324
|
+
return handle ? Session.fromNative(handle) : undefined;
|
|
325
|
+
}
|
|
326
|
+
/** Whether a session with this id exists in this process. */
|
|
327
|
+
function doesSessionExist(sessionID) {
|
|
328
|
+
return native_js_1.native.doesSessionExist((0, session_id_js_1.toNativeSessionID)(sessionID, 'sessionID'));
|
|
329
|
+
}
|
|
330
|
+
/** The ids of every session in this process, across all engines. */
|
|
331
|
+
function getSessions() {
|
|
332
|
+
return native_js_1.native.getSessions().map((h) => session_id_js_1.SessionID.fromNative(h));
|
|
333
|
+
}
|
|
334
|
+
/** The number of sessions in this process, across all engines. */
|
|
335
|
+
function numSessions() {
|
|
336
|
+
return native_js_1.native.numSessions();
|
|
337
|
+
}
|