@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.
Files changed (145) hide show
  1. package/CMakeLists.txt +5 -1
  2. package/README.md +344 -18
  3. package/cpp/acceptor_wrap.cpp +253 -19
  4. package/cpp/acceptor_wrap.h +60 -1
  5. package/cpp/addon.cpp +10 -6
  6. package/cpp/application_bridge.cpp +114 -30
  7. package/cpp/application_bridge.h +57 -4
  8. package/cpp/data_dictionary_wrap.cpp +89 -3
  9. package/cpp/data_dictionary_wrap.h +27 -5
  10. package/cpp/engine_common.h +108 -0
  11. package/cpp/engine_workers.h +99 -18
  12. package/cpp/field_map_ops.h +492 -0
  13. package/cpp/group_wrap.cpp +221 -0
  14. package/cpp/group_wrap.h +76 -0
  15. package/cpp/initiator_wrap.cpp +262 -25
  16. package/cpp/initiator_wrap.h +37 -2
  17. package/cpp/message_wrap.cpp +171 -78
  18. package/cpp/message_wrap.h +47 -5
  19. package/cpp/session_op_gate.h +201 -0
  20. package/cpp/session_static.cpp +139 -12
  21. package/cpp/session_wrap.cpp +438 -0
  22. package/cpp/session_wrap.h +144 -0
  23. package/dist/cjs/acceptor.d.ts +0 -1
  24. package/dist/cjs/acceptor.js +0 -1
  25. package/dist/cjs/application.d.ts +0 -1
  26. package/dist/cjs/application.js +0 -1
  27. package/dist/cjs/data-dictionary.d.ts +21 -3
  28. package/dist/cjs/data-dictionary.js +32 -4
  29. package/dist/cjs/engine.d.ts +42 -4
  30. package/dist/cjs/engine.js +50 -5
  31. package/dist/cjs/enums.d.ts +48 -14
  32. package/dist/cjs/enums.js +20 -25
  33. package/dist/cjs/field-value.d.ts +12 -0
  34. package/dist/cjs/field-value.js +40 -0
  35. package/dist/cjs/generated/fields.d.ts +0 -1
  36. package/dist/cjs/generated/fields.js +0 -1
  37. package/dist/cjs/generated/values.d.ts +8548 -0
  38. package/dist/cjs/generated/values.js +8580 -0
  39. package/dist/cjs/group.d.ts +126 -0
  40. package/dist/cjs/group.js +185 -0
  41. package/dist/cjs/index.d.ts +28 -5
  42. package/dist/cjs/index.js +30 -28
  43. package/dist/cjs/initiator.d.ts +0 -1
  44. package/dist/cjs/initiator.js +0 -1
  45. package/dist/cjs/message.d.ts +129 -5
  46. package/dist/cjs/message.js +146 -9
  47. package/dist/cjs/native.d.ts +179 -17
  48. package/dist/cjs/native.js +0 -1
  49. package/dist/cjs/session-id.d.ts +12 -1
  50. package/dist/cjs/session-id.js +21 -1
  51. package/dist/cjs/session-settings.d.ts +0 -1
  52. package/dist/cjs/session-settings.js +0 -1
  53. package/dist/cjs/session.d.ts +213 -0
  54. package/dist/cjs/session.js +337 -0
  55. package/dist/cjs/tls-tunnel.d.ts +143 -0
  56. package/dist/cjs/tls-tunnel.js +261 -0
  57. package/dist/esm/acceptor.d.ts +0 -1
  58. package/dist/esm/acceptor.js +0 -1
  59. package/dist/esm/application.d.ts +0 -1
  60. package/dist/esm/application.js +0 -1
  61. package/dist/esm/data-dictionary.d.ts +21 -3
  62. package/dist/esm/data-dictionary.js +32 -4
  63. package/dist/esm/engine.d.ts +42 -4
  64. package/dist/esm/engine.js +51 -6
  65. package/dist/esm/enums.d.ts +48 -14
  66. package/dist/esm/enums.js +14 -24
  67. package/dist/esm/field-value.d.ts +12 -0
  68. package/dist/esm/field-value.js +37 -0
  69. package/dist/esm/generated/fields.d.ts +0 -1
  70. package/dist/esm/generated/fields.js +0 -1
  71. package/dist/esm/generated/values.d.ts +8548 -0
  72. package/dist/esm/generated/values.js +8564 -0
  73. package/dist/esm/group.d.ts +126 -0
  74. package/dist/esm/group.js +181 -0
  75. package/dist/esm/index.d.ts +28 -5
  76. package/dist/esm/index.js +18 -26
  77. package/dist/esm/initiator.d.ts +0 -1
  78. package/dist/esm/initiator.js +0 -1
  79. package/dist/esm/message.d.ts +129 -5
  80. package/dist/esm/message.js +146 -9
  81. package/dist/esm/native.d.ts +179 -17
  82. package/dist/esm/native.js +0 -1
  83. package/dist/esm/session-id.d.ts +12 -1
  84. package/dist/esm/session-id.js +20 -1
  85. package/dist/esm/session-settings.d.ts +0 -1
  86. package/dist/esm/session-settings.js +0 -1
  87. package/dist/esm/session.d.ts +213 -0
  88. package/dist/esm/session.js +329 -0
  89. package/dist/esm/tls-tunnel.d.ts +143 -0
  90. package/dist/esm/tls-tunnel.js +256 -0
  91. package/package.json +13 -1
  92. package/prebuilds/darwin-arm64/node.napi.node +0 -0
  93. package/prebuilds/darwin-x64/node.napi.node +0 -0
  94. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  95. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  96. package/prebuilds/win32-x64/node.napi.node +0 -0
  97. package/cpp/enums.cpp +0 -66
  98. package/dist/cjs/acceptor.d.ts.map +0 -1
  99. package/dist/cjs/acceptor.js.map +0 -1
  100. package/dist/cjs/application.d.ts.map +0 -1
  101. package/dist/cjs/application.js.map +0 -1
  102. package/dist/cjs/data-dictionary.d.ts.map +0 -1
  103. package/dist/cjs/data-dictionary.js.map +0 -1
  104. package/dist/cjs/engine.d.ts.map +0 -1
  105. package/dist/cjs/engine.js.map +0 -1
  106. package/dist/cjs/enums.d.ts.map +0 -1
  107. package/dist/cjs/enums.js.map +0 -1
  108. package/dist/cjs/generated/fields.d.ts.map +0 -1
  109. package/dist/cjs/generated/fields.js.map +0 -1
  110. package/dist/cjs/index.d.ts.map +0 -1
  111. package/dist/cjs/index.js.map +0 -1
  112. package/dist/cjs/initiator.d.ts.map +0 -1
  113. package/dist/cjs/initiator.js.map +0 -1
  114. package/dist/cjs/message.d.ts.map +0 -1
  115. package/dist/cjs/message.js.map +0 -1
  116. package/dist/cjs/native.d.ts.map +0 -1
  117. package/dist/cjs/native.js.map +0 -1
  118. package/dist/cjs/session-id.d.ts.map +0 -1
  119. package/dist/cjs/session-id.js.map +0 -1
  120. package/dist/cjs/session-settings.d.ts.map +0 -1
  121. package/dist/cjs/session-settings.js.map +0 -1
  122. package/dist/esm/acceptor.d.ts.map +0 -1
  123. package/dist/esm/acceptor.js.map +0 -1
  124. package/dist/esm/application.d.ts.map +0 -1
  125. package/dist/esm/application.js.map +0 -1
  126. package/dist/esm/data-dictionary.d.ts.map +0 -1
  127. package/dist/esm/data-dictionary.js.map +0 -1
  128. package/dist/esm/engine.d.ts.map +0 -1
  129. package/dist/esm/engine.js.map +0 -1
  130. package/dist/esm/enums.d.ts.map +0 -1
  131. package/dist/esm/enums.js.map +0 -1
  132. package/dist/esm/generated/fields.d.ts.map +0 -1
  133. package/dist/esm/generated/fields.js.map +0 -1
  134. package/dist/esm/index.d.ts.map +0 -1
  135. package/dist/esm/index.js.map +0 -1
  136. package/dist/esm/initiator.d.ts.map +0 -1
  137. package/dist/esm/initiator.js.map +0 -1
  138. package/dist/esm/message.d.ts.map +0 -1
  139. package/dist/esm/message.js.map +0 -1
  140. package/dist/esm/native.d.ts.map +0 -1
  141. package/dist/esm/native.js.map +0 -1
  142. package/dist/esm/session-id.d.ts.map +0 -1
  143. package/dist/esm/session-id.js.map +0 -1
  144. package/dist/esm/session-settings.d.ts.map +0 -1
  145. package/dist/esm/session-settings.js.map +0 -1
@@ -2,11 +2,15 @@
2
2
  * Ergonomic wrapper around the native `FIX::Message`.
3
3
  */
4
4
  import { native } from './native.js';
5
+ import { Group } from './group.js';
6
+ import { formatFieldValue } from './field-value.js';
5
7
  /**
6
8
  * A FIX message.
7
9
  *
8
10
  * Field values are accepted as `string | number` on input (numbers are
9
- * stringified, since FIX is string-on-the-wire) and always returned as `string`.
11
+ * stringified, since FIX is string-on-the-wire, without exponent notation:
12
+ * `1e-7` is written `0.0000001`) and always returned as `string`. `NaN` and
13
+ * `±Infinity` throw a `RangeError`.
10
14
  *
11
15
  * @example
12
16
  * ```ts
@@ -19,6 +23,12 @@ import { native } from './native.js';
19
23
  *
20
24
  * const parsed = Message.parse(msg.toString());
21
25
  * console.log(parsed.getMsgType());
26
+ *
27
+ * // Repeating groups: build entries with Group, read them back by 1-based index.
28
+ * msg.addGroup(new Group(FIELD.NoPartyIDs, FIELD.PartyID).setField(FIELD.PartyID, 'TRADER-1'));
29
+ * const dd = DataDictionary.fromFile('FIX44.xml');
30
+ * const withGroups = Message.parse(msg.toString(), { dictionary: dd });
31
+ * withGroups.getGroup(1, FIELD.NoPartyIDs).getField(FIELD.PartyID); // 'TRADER-1'
22
32
  * ```
23
33
  */
24
34
  export class Message {
@@ -40,7 +50,9 @@ export class Message {
40
50
  this.#native = new native.MessageWrap();
41
51
  }
42
52
  else if (typeof raw === 'string') {
43
- this.#native = new native.MessageWrap(raw, opts?.validate ?? false);
53
+ const sessionDD = opts?.sessionDictionary ?? opts?.dictionary;
54
+ const appDD = opts?.applicationDictionary ?? opts?.dictionary;
55
+ this.#native = new native.MessageWrap(raw, opts?.validate ?? false, sessionDD?.nativeHandle ?? null, appDD?.nativeHandle ?? null);
44
56
  }
45
57
  else {
46
58
  // Adopt an existing native handle (bridge path).
@@ -50,8 +62,18 @@ export class Message {
50
62
  /**
51
63
  * Parse a raw FIX wire string into a {@link Message}.
52
64
  *
65
+ * Pass a `dictionary` to parse repeating groups structurally; without one
66
+ * the parse is flat (see {@link MessageParseOptions}).
67
+ *
53
68
  * @param raw Raw FIX wire string (SOH-delimited).
54
69
  * @param opts Optional parse options.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const dd = DataDictionary.fromFile('./spec/FIX44.xml');
74
+ * const msg = Message.parse(raw, { dictionary: dd });
75
+ * msg.getGroup(1, FIELD.NoPartyIDs).getField(FIELD.PartyID);
76
+ * ```
55
77
  */
56
78
  static parse(raw, opts) {
57
79
  return new Message(raw, opts);
@@ -65,17 +87,133 @@ export class Message {
65
87
  return this.#native;
66
88
  }
67
89
  /**
68
- * Read a body field by tag.
69
- * @throws A `QuickFixError` (`fixError: 'FieldNotFound'`) if absent.
90
+ * Read a field by tag. Standard header/trailer tags (`MsgType`,
91
+ * `SenderCompID`, `CheckSum`, ...) are read from their section, and so are
92
+ * the header/trailer fields of the session dictionary the message was
93
+ * parsed with (`sessionDictionary` / `dictionary`) or delivered with (a
94
+ * message handed to an engine handler). Any other tag is read from the body,
95
+ * or from the header or trailer if that is where it is.
96
+ * @throws A `QuickFixError` (`fixErrorName: 'FieldNotFound'`) if absent.
70
97
  */
71
98
  getField(tag) {
72
99
  return this.#native.getField(tag);
73
100
  }
74
- /** Set a body field. Returns `this` for chaining. */
101
+ /**
102
+ * Set a field. Standard header/trailer tags and the session dictionary's
103
+ * header/trailer fields auto-route to their section (see
104
+ * {@link Message.getField}). Any other tag is updated in place if the header
105
+ * or trailer already holds it; otherwise it goes to the body. On a message
106
+ * without a session dictionary (built with `new Message()`), put a new
107
+ * custom header field in the header with {@link Message.setHeaderField}.
108
+ * Returns `this` for chaining.
109
+ */
75
110
  setField(tag, value) {
76
- this.#native.setField(tag, String(value));
111
+ this.#native.setField(tag, formatFieldValue(value));
112
+ return this;
113
+ }
114
+ /** Whether a field is present (same auto-routing as {@link Message.getField}). */
115
+ isSetField(tag) {
116
+ return this.#native.isSetField(tag);
117
+ }
118
+ /** Remove a field (no-op if absent; same auto-routing). Returns `this` for chaining. */
119
+ removeField(tag) {
120
+ this.#native.removeField(tag);
121
+ return this;
122
+ }
123
+ /** Read a field by tag, or `undefined` if absent (same auto-routing). */
124
+ getFieldIfSet(tag) {
125
+ return this.#native.getFieldIfSet(tag);
126
+ }
127
+ /** Whether the header, body and trailer have no fields of their own. */
128
+ isEmpty() {
129
+ return this.#native.isEmpty();
130
+ }
131
+ /** The number of body fields, including every field of every group entry. */
132
+ totalFields() {
133
+ return this.#native.totalFields();
134
+ }
135
+ /** Remove every header, body and trailer field and group. Returns `this` for chaining. */
136
+ clear() {
137
+ this.#native.clear();
138
+ return this;
139
+ }
140
+ /** The body's own fields, in wire order, as `[tag, value]` pairs (group entries excluded). */
141
+ fields() {
142
+ return this.#native.fields();
143
+ }
144
+ /** The header fields, in wire order, as `[tag, value]` pairs. */
145
+ headerFields() {
146
+ return this.#native.headerFields();
147
+ }
148
+ /** The trailer fields, in wire order, as `[tag, value]` pairs. */
149
+ trailerFields() {
150
+ return this.#native.trailerFields();
151
+ }
152
+ /** Iterates the body's own fields as `[tag, value]` pairs (see {@link Message.fields}). */
153
+ [Symbol.iterator]() {
154
+ return this.fields()[Symbol.iterator]();
155
+ }
156
+ /**
157
+ * Append a repeating-group entry under its count tag (`group.field`). A copy
158
+ * is stored and the count field is updated. Header groups (e.g. `NoHops`)
159
+ * auto-route to the header. Returns `this` for chaining.
160
+ *
161
+ * @throws `TypeError` when `group.delim` differs from the delimiter of the
162
+ * entries already stored under `group.field`.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * order.addGroup(new Group(FIELD.NoPartyIDs, FIELD.PartyID).setField(FIELD.PartyID, 'X'));
167
+ * ```
168
+ */
169
+ addGroup(group) {
170
+ this.#native.addGroup(group.nativeHandle);
171
+ return this;
172
+ }
173
+ /**
174
+ * A **snapshot copy** of the `index`-th entry (1-based) of the repeating
175
+ * group `tag`. Mutating the returned {@link Group} does not change this
176
+ * message; write it back with {@link Message.replaceGroup}.
177
+ *
178
+ * Groups exist only on messages built with {@link Message.addGroup} or
179
+ * parsed with a dictionary, which includes every message the engine hands
180
+ * to your handlers when the session has `UseDataDictionary=Y` (the
181
+ * default); a flat parse, or `UseDataDictionary=N`, leaves none.
182
+ *
183
+ * @throws A `QuickFixError` (`fixErrorName: 'FieldNotFound'`) when the tag
184
+ * has no entries or `index` is out of range.
185
+ */
186
+ getGroup(index, tag) {
187
+ return Group.fromNative(this.#native.getGroup(index, tag));
188
+ }
189
+ /**
190
+ * Overwrite the `index`-th entry (1-based) under `group.field` with a copy of
191
+ * `group`. Returns `this` for chaining.
192
+ * @throws A `QuickFixError` (`fixErrorName: 'FieldNotFound'`) when that
193
+ * entry does not exist.
194
+ * @throws `TypeError` when `group.delim` differs from the stored entry's
195
+ * delimiter.
196
+ */
197
+ replaceGroup(index, group) {
198
+ this.#native.replaceGroup(index, group.nativeHandle);
199
+ return this;
200
+ }
201
+ removeGroup(a, b) {
202
+ if (b === undefined) {
203
+ this.#native.removeGroup(a);
204
+ }
205
+ else {
206
+ this.#native.removeGroup(a, b);
207
+ }
77
208
  return this;
78
209
  }
210
+ hasGroup(a, b) {
211
+ return b === undefined ? this.#native.hasGroup(a) : this.#native.hasGroup(a, b);
212
+ }
213
+ /** The number of entries of the repeating group `tag` (0 if none). */
214
+ groupCount(tag) {
215
+ return this.#native.groupCount(tag);
216
+ }
79
217
  /**
80
218
  * Read a header field by tag.
81
219
  * @throws A `QuickFixError` (`fixError: 'FieldNotFound'`) if absent.
@@ -85,7 +223,7 @@ export class Message {
85
223
  }
86
224
  /** Set a header field. Returns `this` for chaining. */
87
225
  setHeaderField(tag, value) {
88
- this.#native.setHeaderField(tag, String(value));
226
+ this.#native.setHeaderField(tag, formatFieldValue(value));
89
227
  return this;
90
228
  }
91
229
  /**
@@ -97,7 +235,7 @@ export class Message {
97
235
  }
98
236
  /** Set a trailer field. Returns `this` for chaining. */
99
237
  setTrailerField(tag, value) {
100
- this.#native.setTrailerField(tag, String(value));
238
+ this.#native.setTrailerField(tag, formatFieldValue(value));
101
239
  return this;
102
240
  }
103
241
  /** The message type (tag 35), e.g. `'A'` for Logon. */
@@ -153,4 +291,3 @@ export function createMessage(fields) {
153
291
  export function parseMessage(raw, opts) {
154
292
  return Message.parse(raw, opts);
155
293
  }
156
- //# sourceMappingURL=message.js.map
@@ -1,14 +1,44 @@
1
1
  /**
2
- * Native `FIX::Message` wrapper. FIX is string-on-the-wire, so every field
3
- * value crosses the boundary as a string.
2
+ * The field and repeating-group operations every `FIX::FieldMap` exposes.
3
+ * Shared by the message body and by group entries. Group indices are 1-based,
4
+ * as in QuickFIX; `getGroup` returns a snapshot copy.
4
5
  */
5
- export interface NativeMessage {
6
+ export interface NativeFieldMap {
6
7
  getField(tag: number): string;
7
8
  setField(tag: number, value: string): void;
9
+ isSetField(tag: number): boolean;
10
+ removeField(tag: number): void;
11
+ getFieldIfSet(tag: number): string | undefined;
12
+ /** Own fields plus every field of every nested group entry. */
13
+ totalFields(): number;
14
+ isEmpty(): boolean;
15
+ clear(): void;
16
+ /** Own fields, in wire order, as `[tag, value]` pairs. */
17
+ fields(): [number, string][];
18
+ addGroup(group: NativeGroup): void;
19
+ /** @throws A {@link QuickFixError} (`FieldNotFound`) when absent / out of range. */
20
+ getGroup(index: number, tag: number): NativeGroup;
21
+ /** @throws A {@link QuickFixError} (`FieldNotFound`) when the slot does not exist. */
22
+ replaceGroup(index: number, group: NativeGroup): void;
23
+ removeGroup(tag: number): void;
24
+ removeGroup(index: number, tag: number): void;
25
+ hasGroup(tag: number): boolean;
26
+ hasGroup(index: number, tag: number): boolean;
27
+ groupCount(tag: number): number;
28
+ }
29
+ /**
30
+ * Native `FIX::Message` wrapper. FIX is string-on-the-wire, so every field
31
+ * value crosses the boundary as a string. The {@link NativeFieldMap} methods
32
+ * auto-route well-known header/trailer tags to the right section; `fields()`,
33
+ * `isEmpty()` and `totalFields()` read the body, `clear()` empties everything.
34
+ */
35
+ export interface NativeMessage extends NativeFieldMap {
8
36
  getHeaderField(tag: number): string;
9
37
  setHeaderField(tag: number, value: string): void;
10
38
  getTrailerField(tag: number): string;
11
39
  setTrailerField(tag: number, value: string): void;
40
+ headerFields(): [number, string][];
41
+ trailerFields(): [number, string][];
12
42
  getMsgType(): string;
13
43
  toString(): string;
14
44
  toPretty(): string;
@@ -16,7 +46,29 @@ export interface NativeMessage {
16
46
  /** Constructor shape for the native `MessageWrap` class. */
17
47
  export interface NativeMessageConstructor {
18
48
  new (): NativeMessage;
19
- new (raw: string, validate?: boolean): NativeMessage;
49
+ /**
50
+ * Parse `raw`. With a dictionary the parse is structural (repeating groups
51
+ * become nested groups); without one it is flat. Admin messages always use
52
+ * the session dictionary; application messages use the application one.
53
+ */
54
+ new (raw: string, validate?: boolean, sessionDictionary?: NativeDataDictionary | null, applicationDictionary?: NativeDataDictionary | null): NativeMessage;
55
+ }
56
+ /** Native `FIX::Group` wrapper: one entry of a repeating group. */
57
+ export interface NativeGroup extends NativeFieldMap {
58
+ /** The group's count tag (e.g. 453 NoPartyIDs). */
59
+ field(): number;
60
+ /** The first tag of every entry (e.g. 448 PartyID). */
61
+ delim(): number;
62
+ /** The entry's fields (and nested groups) as a SOH-delimited string, in wire order. */
63
+ toString(): string;
64
+ }
65
+ /** Constructor shape for the native `GroupWrap` class. */
66
+ export interface NativeGroupConstructor {
67
+ /**
68
+ * @param order Optional full field order of an entry, delimiter first, no
69
+ * zeros. Without it, fields sort delimiter-first then by tag number.
70
+ */
71
+ new (field: number, delim: number, order?: readonly number[] | null): NativeGroup;
20
72
  }
21
73
  /** Native `FIX::SessionID` wrapper. */
22
74
  export interface NativeSessionID {
@@ -77,8 +129,19 @@ export interface QuickFixError extends Error {
77
129
  }
78
130
  /** Native `FIX::DataDictionary` wrapper. */
79
131
  export interface NativeDataDictionary {
80
- /** Throws a {@link QuickFixError} if the message is invalid. */
81
- validate(message: NativeMessage): void;
132
+ /**
133
+ * Throws a {@link QuickFixError} if the message is invalid. With `bodyOnly`
134
+ * the BeginString version check and the tag/value validation of the
135
+ * header/trailer fields are skipped; required header/trailer fields are
136
+ * still checked.
137
+ */
138
+ validate(message: NativeMessage, bodyOnly?: boolean): void;
139
+ /** The BeginString the spec declares (e.g. `'FIX.4.4'`), `''` if none. */
140
+ getVersion(): string;
141
+ getFieldName(tag: number): string | undefined;
142
+ getFieldTag(name: string): number | undefined;
143
+ isField(tag: number): boolean;
144
+ isMsgType(msgType: string): boolean;
82
145
  }
83
146
  /**
84
147
  * Constructor shape for the native `DataDictionaryWrap` class. `fromString` /
@@ -111,18 +174,105 @@ export interface NativeEngineOptions {
111
174
  store?: 'file' | 'memory';
112
175
  log?: 'screen' | 'file' | 'none';
113
176
  }
177
+ /**
178
+ * Native handle on a live `FIX::Session`.
179
+ *
180
+ * The handle stores only the `FIX::SessionID`; every method re-resolves the
181
+ * engine-owned session via `FIX::Session::lookupSession` and throws a
182
+ * {@link QuickFixError} with `fixErrorName: 'SessionNotFound'` once the owning
183
+ * engine has been stopped.
184
+ *
185
+ * Every member is synchronous: none takes the session mutex QuickFIX holds
186
+ * across application callbacks, so they cannot deadlock the main thread
187
+ * (`logon()`/`logout()` write a plain flag the network thread reads without a
188
+ * lock, as QuickFIX's own `Initiator::stop()` does).
189
+ * `FIX::Session::disconnect()`/`reset()` are not exposed; they are only safe
190
+ * on the engine's own network thread (see `cpp/session_wrap.cpp`).
191
+ */
192
+ export interface NativeSession {
193
+ getSessionID(): NativeSessionID;
194
+ isLoggedOn(): boolean;
195
+ isEnabled(): boolean;
196
+ sentLogon(): boolean;
197
+ sentLogout(): boolean;
198
+ receivedLogon(): boolean;
199
+ isInitiator(): boolean;
200
+ isAcceptor(): boolean;
201
+ /** `nowMs` is an epoch-millisecond timestamp; omitted means "now". */
202
+ isSessionTime(nowMs?: number): boolean;
203
+ isLogonTime(nowMs?: number): boolean;
204
+ getExpectedSenderNum(): number;
205
+ getExpectedTargetNum(): number;
206
+ setNextSenderMsgSeqNum(seqNum: number): void;
207
+ setNextTargetMsgSeqNum(seqNum: number): void;
208
+ getResetOnLogon(): boolean;
209
+ setResetOnLogon(value: boolean): void;
210
+ getResetOnLogout(): boolean;
211
+ setResetOnLogout(value: boolean): void;
212
+ getResetOnDisconnect(): boolean;
213
+ setResetOnDisconnect(value: boolean): void;
214
+ getRefreshOnLogon(): boolean;
215
+ setRefreshOnLogon(value: boolean): void;
216
+ getCheckCompId(): boolean;
217
+ setCheckCompId(value: boolean): void;
218
+ getCheckLatency(): boolean;
219
+ setCheckLatency(value: boolean): void;
220
+ getPersistMessages(): boolean;
221
+ setPersistMessages(value: boolean): void;
222
+ getSendRedundantResendRequests(): boolean;
223
+ setSendRedundantResendRequests(value: boolean): void;
224
+ getValidateLengthAndChecksum(): boolean;
225
+ setValidateLengthAndChecksum(value: boolean): void;
226
+ getSendNextExpectedMsgSeqNum(): boolean;
227
+ setSendNextExpectedMsgSeqNum(value: boolean): void;
228
+ getIsNonStopSession(): boolean;
229
+ setIsNonStopSession(value: boolean): void;
230
+ getLogonTimeout(): number;
231
+ setLogonTimeout(seconds: number): void;
232
+ getLogoutTimeout(): number;
233
+ setLogoutTimeout(seconds: number): void;
234
+ getMaxLatency(): number;
235
+ setMaxLatency(seconds: number): void;
236
+ getTimestampPrecision(): number;
237
+ /** Throws a `RangeError` unless `precision` is an integer in `0..9`. */
238
+ setTimestampPrecision(precision: number): void;
239
+ logon(): void;
240
+ logout(reason?: string): void;
241
+ /** May throw a `QuickFixError` (`IOException`) from the message store. */
242
+ refresh(): void;
243
+ }
244
+ /**
245
+ * Constructor shape for the native `SessionWrap` class. Internal: JS obtains a
246
+ * session through {@link NativeModule.lookupSession} or
247
+ * {@link NativeEngine.getSession}, never by constructing one.
248
+ */
249
+ export interface NativeSessionConstructor {
250
+ new (sessionID: NativeSessionID): NativeSession;
251
+ }
114
252
  /**
115
253
  * Native engine (SocketInitiator / SocketAcceptor) wrapper.
116
254
  *
117
255
  * `start`/`stop` run OFF the main thread and resolve a Promise when the engine
118
256
  * has finished (re)configuring its sockets, so the Node.js event loop stays free
119
257
  * to service the application callbacks the engine fires. `isLoggedOn`/`ref`/
120
- * `unref` remain synchronous.
258
+ * `unref`/`getSessions`/`getSession` remain synchronous.
259
+ *
260
+ * Once `stop()` settles the engine is destroyed, which also destroys its
261
+ * `FIX::Session` objects: `getSession` then returns `undefined` and any
262
+ * previously obtained {@link NativeSession} throws `SessionNotFound`.
121
263
  */
122
264
  export interface NativeEngine {
123
265
  start(): Promise<void>;
124
266
  stop(force?: boolean): Promise<void>;
125
- isLoggedOn(): boolean;
267
+ /**
268
+ * No argument: whether ANY of this engine's sessions is logged on. With a
269
+ * SessionID: whether THAT session belongs to this engine and is logged on.
270
+ */
271
+ isLoggedOn(sessionID?: NativeSessionID): boolean;
272
+ /** The SessionIDs this engine was configured with (stable across stop()). */
273
+ getSessions(): NativeSessionID[];
274
+ /** `undefined` if the id is not one of this engine's sessions or stop() has settled. */
275
+ getSession(sessionID: NativeSessionID): NativeSession | undefined;
126
276
  ref(): void;
127
277
  unref(): void;
128
278
  }
@@ -131,22 +281,35 @@ export interface NativeEngineConstructor {
131
281
  new (options: NativeEngineOptions): NativeEngine;
132
282
  }
133
283
  /**
134
- * The nested FIX-constants object exported by the addon, e.g.
135
- * `enums.MsgType.Logon`, `enums.Side.Buy`. Field tags (`FIELD`) are not exported
136
- * by the addon; they are generated on the TS side (`src/generated/fields.ts`).
284
+ * The full surface of the native addon exported by `load-native.cjs`.
285
+ *
286
+ * FIX constants are not exported by the addon: field tags (`FIELD`) and value
287
+ * groups (`MsgType`, `Side`, ...) are generated on the TS side from the
288
+ * QuickFIX headers (`src/generated/fields.ts`, `src/generated/values.ts`).
137
289
  */
138
- export type NativeEnums = Readonly<Record<string, Readonly<Record<string, number | string>>>>;
139
- /** The full surface of the native addon exported by `load-native.cjs`. */
140
290
  export interface NativeModule {
141
291
  version(): string;
142
292
  quickfixParseMsgType(raw: string): string;
143
- /** Runs off the main thread; resolves `true` if accepted for sending. */
144
- sendToTarget(message: NativeMessage, sessionID: NativeSessionID): Promise<boolean>;
145
- enums: NativeEnums;
293
+ /**
294
+ * Runs off the main thread; resolves `true` if accepted for sending.
295
+ *
296
+ * Second argument: an explicit SessionID, or a session qualifier (possibly
297
+ * omitted/empty), in which case QuickFIX resolves the session from the
298
+ * message's own header (BeginString / SenderCompID / TargetCompID).
299
+ */
300
+ sendToTarget(message: NativeMessage, sessionIDOrQualifier?: NativeSessionID | string): Promise<boolean>;
301
+ /** `undefined` if no session with that id exists in this process. */
302
+ lookupSession(sessionID: NativeSessionID): NativeSession | undefined;
303
+ doesSessionExist(sessionID: NativeSessionID): boolean;
304
+ /** Every session registered in this process (all engines). */
305
+ getSessions(): NativeSessionID[];
306
+ numSessions(): number;
146
307
  MessageWrap: NativeMessageConstructor;
308
+ GroupWrap: NativeGroupConstructor;
147
309
  SessionIDWrap: NativeSessionIDConstructor;
148
310
  SessionSettingsWrap: NativeSessionSettingsConstructor;
149
311
  DataDictionaryWrap: NativeDataDictionaryConstructor;
312
+ SessionWrap: NativeSessionConstructor;
150
313
  InitiatorWrap: NativeEngineConstructor;
151
314
  AcceptorWrap: NativeEngineConstructor;
152
315
  sessionSettingsFromString?(cfg: string): NativeSessionSettings;
@@ -160,4 +323,3 @@ export interface NativeModule {
160
323
  * assert it to the contract here — the single localized cast in the codebase.
161
324
  */
162
325
  export declare const native: NativeModule;
163
- //# sourceMappingURL=native.d.ts.map
@@ -14,4 +14,3 @@ import nativeModule from './load-native.cjs';
14
14
  * assert it to the contract here — the single localized cast in the codebase.
15
15
  */
16
16
  export const native = nativeModule;
17
- //# sourceMappingURL=native.js.map
@@ -37,4 +37,15 @@ export declare class SessionID {
37
37
  /** The canonical string form, e.g. `FIX.4.4:CLIENT->BROKER`. */
38
38
  toString(): string;
39
39
  }
40
- //# sourceMappingURL=session-id.d.ts.map
40
+ /**
41
+ * @internal The native handle behind a {@link SessionID} argument.
42
+ *
43
+ * Duck-typed rather than `instanceof SessionID`: this package ships ESM and
44
+ * CJS builds, and a `SessionID` created by the other build is a different class
45
+ * with the same shape. Both builds load the same native addon, so its
46
+ * `SessionIDWrap` class is the one thing they share, and that is what the
47
+ * handle is checked against. Anything else -- `null`, `{}`, a `Session` -- is
48
+ * a `TypeError` naming the argument, rather than silently degrading to an
49
+ * `undefined` handle that the callee would read as "no session id given".
50
+ */
51
+ export declare function toNativeSessionID(value: unknown, name: string): NativeSessionID;
@@ -60,4 +60,23 @@ export class SessionID {
60
60
  return this.#native.toString();
61
61
  }
62
62
  }
63
- //# sourceMappingURL=session-id.js.map
63
+ /**
64
+ * @internal The native handle behind a {@link SessionID} argument.
65
+ *
66
+ * Duck-typed rather than `instanceof SessionID`: this package ships ESM and
67
+ * CJS builds, and a `SessionID` created by the other build is a different class
68
+ * with the same shape. Both builds load the same native addon, so its
69
+ * `SessionIDWrap` class is the one thing they share, and that is what the
70
+ * handle is checked against. Anything else -- `null`, `{}`, a `Session` -- is
71
+ * a `TypeError` naming the argument, rather than silently degrading to an
72
+ * `undefined` handle that the callee would read as "no session id given".
73
+ */
74
+ export function toNativeSessionID(value, name) {
75
+ const handle = typeof value === 'object' && value !== null
76
+ ? value.nativeHandle
77
+ : undefined;
78
+ if (!(handle instanceof native.SessionIDWrap)) {
79
+ throw new TypeError(`${name} must be a SessionID`);
80
+ }
81
+ return handle;
82
+ }
@@ -44,4 +44,3 @@ export declare class SessionSettings {
44
44
  /** The sessions declared by this configuration. */
45
45
  getSessions(): SessionID[];
46
46
  }
47
- //# sourceMappingURL=session-settings.d.ts.map
@@ -69,4 +69,3 @@ export class SessionSettings {
69
69
  return this.#native.getSessions().map((h) => SessionID.fromNative(h));
70
70
  }
71
71
  }
72
- //# sourceMappingURL=session-settings.js.map