@homaiohq/napi-quickfix 0.0.0-stage → 0.1.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 (131) hide show
  1. package/CMakeLists.txt +131 -0
  2. package/LICENSE +21 -0
  3. package/README.md +421 -2
  4. package/THIRD_PARTY_LICENSES +48 -0
  5. package/cpp/acceptor_wrap.cpp +234 -0
  6. package/cpp/acceptor_wrap.h +57 -0
  7. package/cpp/addon.cpp +83 -0
  8. package/cpp/application_bridge.cpp +382 -0
  9. package/cpp/application_bridge.h +151 -0
  10. package/cpp/data_dictionary_wrap.cpp +79 -0
  11. package/cpp/data_dictionary_wrap.h +37 -0
  12. package/cpp/engine_common.cpp +5 -0
  13. package/cpp/engine_common.h +19 -0
  14. package/cpp/engine_workers.h +86 -0
  15. package/cpp/enums.cpp +66 -0
  16. package/cpp/errors.h +142 -0
  17. package/cpp/initiator_wrap.cpp +255 -0
  18. package/cpp/initiator_wrap.h +69 -0
  19. package/cpp/message_wrap.cpp +195 -0
  20. package/cpp/message_wrap.h +53 -0
  21. package/cpp/session_id_wrap.cpp +91 -0
  22. package/cpp/session_id_wrap.h +46 -0
  23. package/cpp/session_settings_wrap.cpp +98 -0
  24. package/cpp/session_settings_wrap.h +40 -0
  25. package/cpp/session_static.cpp +93 -0
  26. package/dist/cjs/acceptor.d.ts +30 -0
  27. package/dist/cjs/acceptor.d.ts.map +1 -0
  28. package/dist/cjs/acceptor.js +39 -0
  29. package/dist/cjs/acceptor.js.map +1 -0
  30. package/dist/cjs/application.d.ts +116 -0
  31. package/dist/cjs/application.d.ts.map +1 -0
  32. package/dist/cjs/application.js +63 -0
  33. package/dist/cjs/application.js.map +1 -0
  34. package/dist/cjs/data-dictionary.d.ts +49 -0
  35. package/dist/cjs/data-dictionary.d.ts.map +1 -0
  36. package/dist/cjs/data-dictionary.js +77 -0
  37. package/dist/cjs/data-dictionary.js.map +1 -0
  38. package/dist/cjs/engine.d.ts +114 -0
  39. package/dist/cjs/engine.d.ts.map +1 -0
  40. package/dist/cjs/engine.js +138 -0
  41. package/dist/cjs/engine.js.map +1 -0
  42. package/dist/cjs/enums.d.ts +22 -0
  43. package/dist/cjs/enums.d.ts.map +1 -0
  44. package/dist/cjs/enums.js +49 -0
  45. package/dist/cjs/enums.js.map +1 -0
  46. package/dist/cjs/generated/fields.d.ts +6123 -0
  47. package/dist/cjs/generated/fields.d.ts.map +1 -0
  48. package/dist/cjs/generated/fields.js +6128 -0
  49. package/dist/cjs/generated/fields.js.map +1 -0
  50. package/dist/cjs/index.d.ts +47 -0
  51. package/dist/cjs/index.d.ts.map +1 -0
  52. package/dist/cjs/index.js +73 -0
  53. package/dist/cjs/index.js.map +1 -0
  54. package/dist/cjs/initiator.d.ts +30 -0
  55. package/dist/cjs/initiator.d.ts.map +1 -0
  56. package/dist/cjs/initiator.js +39 -0
  57. package/dist/cjs/initiator.js.map +1 -0
  58. package/dist/cjs/load-native.cjs +5 -0
  59. package/dist/cjs/message.d.ts +114 -0
  60. package/dist/cjs/message.d.ts.map +1 -0
  61. package/dist/cjs/message.js +162 -0
  62. package/dist/cjs/message.js.map +1 -0
  63. package/dist/cjs/native.d.ts +163 -0
  64. package/dist/cjs/native.d.ts.map +1 -0
  65. package/dist/cjs/native.js +23 -0
  66. package/dist/cjs/native.js.map +1 -0
  67. package/dist/cjs/package.json +1 -0
  68. package/dist/cjs/session-id.d.ts +40 -0
  69. package/dist/cjs/session-id.d.ts.map +1 -0
  70. package/dist/cjs/session-id.js +67 -0
  71. package/dist/cjs/session-id.js.map +1 -0
  72. package/dist/cjs/session-settings.d.ts +47 -0
  73. package/dist/cjs/session-settings.d.ts.map +1 -0
  74. package/dist/cjs/session-settings.js +76 -0
  75. package/dist/cjs/session-settings.js.map +1 -0
  76. package/dist/esm/acceptor.d.ts +30 -0
  77. package/dist/esm/acceptor.d.ts.map +1 -0
  78. package/dist/esm/acceptor.js +35 -0
  79. package/dist/esm/acceptor.js.map +1 -0
  80. package/dist/esm/application.d.ts +116 -0
  81. package/dist/esm/application.d.ts.map +1 -0
  82. package/dist/esm/application.js +58 -0
  83. package/dist/esm/application.js.map +1 -0
  84. package/dist/esm/data-dictionary.d.ts +49 -0
  85. package/dist/esm/data-dictionary.d.ts.map +1 -0
  86. package/dist/esm/data-dictionary.js +73 -0
  87. package/dist/esm/data-dictionary.js.map +1 -0
  88. package/dist/esm/engine.d.ts +114 -0
  89. package/dist/esm/engine.d.ts.map +1 -0
  90. package/dist/esm/engine.js +134 -0
  91. package/dist/esm/engine.js.map +1 -0
  92. package/dist/esm/enums.d.ts +22 -0
  93. package/dist/esm/enums.d.ts.map +1 -0
  94. package/dist/esm/enums.js +45 -0
  95. package/dist/esm/enums.js.map +1 -0
  96. package/dist/esm/generated/fields.d.ts +6123 -0
  97. package/dist/esm/generated/fields.d.ts.map +1 -0
  98. package/dist/esm/generated/fields.js +6125 -0
  99. package/dist/esm/generated/fields.js.map +1 -0
  100. package/dist/esm/index.d.ts +47 -0
  101. package/dist/esm/index.d.ts.map +1 -0
  102. package/dist/esm/index.js +52 -0
  103. package/dist/esm/index.js.map +1 -0
  104. package/dist/esm/initiator.d.ts +30 -0
  105. package/dist/esm/initiator.d.ts.map +1 -0
  106. package/dist/esm/initiator.js +35 -0
  107. package/dist/esm/initiator.js.map +1 -0
  108. package/dist/esm/load-native.cjs +5 -0
  109. package/dist/esm/message.d.ts +114 -0
  110. package/dist/esm/message.d.ts.map +1 -0
  111. package/dist/esm/message.js +156 -0
  112. package/dist/esm/message.js.map +1 -0
  113. package/dist/esm/native.d.ts +163 -0
  114. package/dist/esm/native.d.ts.map +1 -0
  115. package/dist/esm/native.js +17 -0
  116. package/dist/esm/native.js.map +1 -0
  117. package/dist/esm/session-id.d.ts +40 -0
  118. package/dist/esm/session-id.d.ts.map +1 -0
  119. package/dist/esm/session-id.js +63 -0
  120. package/dist/esm/session-id.js.map +1 -0
  121. package/dist/esm/session-settings.d.ts +47 -0
  122. package/dist/esm/session-settings.d.ts.map +1 -0
  123. package/dist/esm/session-settings.js +72 -0
  124. package/dist/esm/session-settings.js.map +1 -0
  125. package/package.json +83 -4
  126. package/prebuilds/darwin-arm64/node.napi.node +0 -0
  127. package/prebuilds/darwin-x64/node.napi.node +0 -0
  128. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  129. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  130. package/prebuilds/win32-x64/node.napi.node +0 -0
  131. package/src/load-native.cjs +5 -0
@@ -0,0 +1,195 @@
1
+ #include "message_wrap.h"
2
+
3
+ #include <algorithm>
4
+ #include <string>
5
+
6
+ #include "errors.h"
7
+ #include "quickfix/FieldNumbers.h"
8
+
9
+ namespace napi_quickfix {
10
+
11
+ Napi::FunctionReference MessageWrap::constructor_;
12
+
13
+ namespace {
14
+
15
+ // Coerce a JS arg to a FIX field value string. Accepts string or number
16
+ // (numbers are stringified without trailing ".0"). Throws TypeError otherwise.
17
+ std::string CoerceValue(Napi::Env env, Napi::Value v) {
18
+ if (v.IsString()) {
19
+ return v.As<Napi::String>().Utf8Value();
20
+ }
21
+ if (v.IsNumber()) {
22
+ double d = v.As<Napi::Number>().DoubleValue();
23
+ // Integers -> plain integer string; otherwise a plain double string.
24
+ if (d == static_cast<double>(static_cast<long long>(d))) {
25
+ return std::to_string(static_cast<long long>(d));
26
+ }
27
+ return std::to_string(d);
28
+ }
29
+ throw Napi::TypeError::New(env, "field value must be a string or number");
30
+ }
31
+
32
+ int CoerceTag(Napi::Env env, Napi::Value v) {
33
+ if (!v.IsNumber()) {
34
+ throw Napi::TypeError::New(env, "field tag must be a number");
35
+ }
36
+ return v.As<Napi::Number>().Int32Value();
37
+ }
38
+
39
+ } // namespace
40
+
41
+ Napi::Object MessageWrap::Init(Napi::Env env, Napi::Object exports) {
42
+ Napi::Function func = DefineClass(
43
+ env, "Message",
44
+ {
45
+ InstanceMethod("getField", &MessageWrap::GetField),
46
+ InstanceMethod("setField", &MessageWrap::SetField),
47
+ InstanceMethod("getHeaderField", &MessageWrap::GetHeaderField),
48
+ InstanceMethod("setHeaderField", &MessageWrap::SetHeaderField),
49
+ InstanceMethod("getTrailerField", &MessageWrap::GetTrailerField),
50
+ InstanceMethod("setTrailerField", &MessageWrap::SetTrailerField),
51
+ InstanceMethod("toString", &MessageWrap::ToString),
52
+ InstanceMethod("toPretty", &MessageWrap::ToPretty),
53
+ InstanceMethod("getMsgType", &MessageWrap::GetMsgType),
54
+ });
55
+
56
+ constructor_ = Napi::Persistent(func);
57
+ constructor_.SuppressDestruct();
58
+
59
+ exports.Set("MessageWrap", func);
60
+ return exports;
61
+ }
62
+
63
+ Napi::Object MessageWrap::NewInstance(Napi::Env env, const FIX::Message& msg) {
64
+ // Construct an empty MessageWrap then overwrite its FIX::Message with a copy.
65
+ Napi::Object obj = constructor_.New({});
66
+ MessageWrap* wrap = Napi::ObjectWrap<MessageWrap>::Unwrap(obj);
67
+ wrap->message_ = msg;
68
+ return obj;
69
+ }
70
+
71
+ MessageWrap* MessageWrap::UnwrapArg(Napi::Env env, Napi::Value value,
72
+ const char* argName) {
73
+ if (!value.IsObject() ||
74
+ !value.As<Napi::Object>().InstanceOf(constructor_.Value())) {
75
+ throw Napi::TypeError::New(
76
+ env, std::string(argName) + " must be a Message instance");
77
+ }
78
+ return Napi::ObjectWrap<MessageWrap>::Unwrap(value.As<Napi::Object>());
79
+ }
80
+
81
+ MessageWrap::MessageWrap(const Napi::CallbackInfo& info)
82
+ : Napi::ObjectWrap<MessageWrap>(info) {
83
+ Napi::Env env = info.Env();
84
+ if (info.Length() == 0) {
85
+ // Empty message.
86
+ return;
87
+ }
88
+ if (!info[0].IsString()) {
89
+ throw Napi::TypeError::New(
90
+ env, "new Message(raw: string, validate?: boolean)");
91
+ }
92
+ const std::string raw = info[0].As<Napi::String>().Utf8Value();
93
+ bool validate = false;
94
+ if (info.Length() >= 2 && !info[1].IsUndefined() && !info[1].IsNull()) {
95
+ validate = info[1].ToBoolean().Value();
96
+ }
97
+ NQ_TRY(env) {
98
+ message_.setString(raw, validate);
99
+ } NQ_CATCH(env)
100
+ }
101
+
102
+ Napi::Value MessageWrap::GetField(const Napi::CallbackInfo& info) {
103
+ Napi::Env env = info.Env();
104
+ int tag = CoerceTag(env, info[0]);
105
+ NQ_TRY(env) {
106
+ // Auto-route standard header/trailer fields (MsgType, SenderCompID,
107
+ // CheckSum, ...) to the right section so callers don't have to know which
108
+ // section a well-known tag belongs to. Everything else is a body field.
109
+ const std::string& value =
110
+ FIX::Message::isHeaderField(tag) ? message_.getHeader().getField(tag)
111
+ : FIX::Message::isTrailerField(tag) ? message_.getTrailer().getField(tag)
112
+ : message_.getField(tag);
113
+ return Napi::String::New(env, value);
114
+ } NQ_CATCH(env)
115
+ }
116
+
117
+ Napi::Value MessageWrap::SetField(const Napi::CallbackInfo& info) {
118
+ Napi::Env env = info.Env();
119
+ int tag = CoerceTag(env, info[0]);
120
+ std::string value = CoerceValue(env, info[1]);
121
+ NQ_TRY(env) {
122
+ if (FIX::Message::isHeaderField(tag)) {
123
+ message_.getHeader().setField(tag, value);
124
+ } else if (FIX::Message::isTrailerField(tag)) {
125
+ message_.getTrailer().setField(tag, value);
126
+ } else {
127
+ message_.setField(tag, value);
128
+ }
129
+ } NQ_CATCH(env)
130
+ return env.Undefined();
131
+ }
132
+
133
+ Napi::Value MessageWrap::GetHeaderField(const Napi::CallbackInfo& info) {
134
+ Napi::Env env = info.Env();
135
+ int tag = CoerceTag(env, info[0]);
136
+ NQ_TRY(env) {
137
+ const std::string& value = message_.getHeader().getField(tag);
138
+ return Napi::String::New(env, value);
139
+ } NQ_CATCH(env)
140
+ }
141
+
142
+ Napi::Value MessageWrap::SetHeaderField(const Napi::CallbackInfo& info) {
143
+ Napi::Env env = info.Env();
144
+ int tag = CoerceTag(env, info[0]);
145
+ std::string value = CoerceValue(env, info[1]);
146
+ NQ_TRY(env) {
147
+ message_.getHeader().setField(tag, value);
148
+ } NQ_CATCH(env)
149
+ return env.Undefined();
150
+ }
151
+
152
+ Napi::Value MessageWrap::GetTrailerField(const Napi::CallbackInfo& info) {
153
+ Napi::Env env = info.Env();
154
+ int tag = CoerceTag(env, info[0]);
155
+ NQ_TRY(env) {
156
+ const std::string& value = message_.getTrailer().getField(tag);
157
+ return Napi::String::New(env, value);
158
+ } NQ_CATCH(env)
159
+ }
160
+
161
+ Napi::Value MessageWrap::SetTrailerField(const Napi::CallbackInfo& info) {
162
+ Napi::Env env = info.Env();
163
+ int tag = CoerceTag(env, info[0]);
164
+ std::string value = CoerceValue(env, info[1]);
165
+ NQ_TRY(env) {
166
+ message_.getTrailer().setField(tag, value);
167
+ } NQ_CATCH(env)
168
+ return env.Undefined();
169
+ }
170
+
171
+ Napi::Value MessageWrap::ToString(const Napi::CallbackInfo& info) {
172
+ Napi::Env env = info.Env();
173
+ NQ_TRY(env) {
174
+ return Napi::String::New(env, message_.toString());
175
+ } NQ_CATCH(env)
176
+ }
177
+
178
+ Napi::Value MessageWrap::ToPretty(const Napi::CallbackInfo& info) {
179
+ Napi::Env env = info.Env();
180
+ NQ_TRY(env) {
181
+ std::string s = message_.toString();
182
+ std::replace(s.begin(), s.end(), '\x01', '|');
183
+ return Napi::String::New(env, s);
184
+ } NQ_CATCH(env)
185
+ }
186
+
187
+ Napi::Value MessageWrap::GetMsgType(const Napi::CallbackInfo& info) {
188
+ Napi::Env env = info.Env();
189
+ NQ_TRY(env) {
190
+ const std::string& msgType = message_.getHeader().getField(FIX::FIELD::MsgType);
191
+ return Napi::String::New(env, msgType);
192
+ } NQ_CATCH(env)
193
+ }
194
+
195
+ } // namespace napi_quickfix
@@ -0,0 +1,53 @@
1
+ #ifndef NAPI_QUICKFIX_MESSAGE_WRAP_H
2
+ #define NAPI_QUICKFIX_MESSAGE_WRAP_H
3
+
4
+ #include <napi.h>
5
+
6
+ #include "quickfix/Message.h"
7
+
8
+ namespace napi_quickfix {
9
+
10
+ // Napi::ObjectWrap over FIX::Message.
11
+ //
12
+ // new MessageWrap()
13
+ // new MessageWrap(raw: string, validate?: boolean = false)
14
+ //
15
+ // Methods: getField / setField / getHeaderField / setHeaderField /
16
+ // getTrailerField / setTrailerField / toString / toPretty / getMsgType.
17
+ class MessageWrap : public Napi::ObjectWrap<MessageWrap> {
18
+ public:
19
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
20
+
21
+ // Build a JS MessageWrap wrapping a COPY of the given FIX::Message. Used by
22
+ // the ApplicationBridge trampoline to hand messages to JS handlers.
23
+ static Napi::Object NewInstance(Napi::Env env, const FIX::Message& msg);
24
+
25
+ explicit MessageWrap(const Napi::CallbackInfo& info);
26
+
27
+ // Internal accessor for other wrappers (validate, sendToTarget, bridge).
28
+ FIX::Message& Message() { return message_; }
29
+ const FIX::Message& Message() const { return message_; }
30
+
31
+ // Unwrap a JS value that must be a MessageWrap; throws Napi::TypeError if not.
32
+ static MessageWrap* UnwrapArg(Napi::Env env, Napi::Value value,
33
+ const char* argName);
34
+
35
+ private:
36
+ static Napi::FunctionReference constructor_;
37
+
38
+ Napi::Value GetField(const Napi::CallbackInfo& info);
39
+ Napi::Value SetField(const Napi::CallbackInfo& info);
40
+ Napi::Value GetHeaderField(const Napi::CallbackInfo& info);
41
+ Napi::Value SetHeaderField(const Napi::CallbackInfo& info);
42
+ Napi::Value GetTrailerField(const Napi::CallbackInfo& info);
43
+ Napi::Value SetTrailerField(const Napi::CallbackInfo& info);
44
+ Napi::Value ToString(const Napi::CallbackInfo& info);
45
+ Napi::Value ToPretty(const Napi::CallbackInfo& info);
46
+ Napi::Value GetMsgType(const Napi::CallbackInfo& info);
47
+
48
+ FIX::Message message_;
49
+ };
50
+
51
+ } // namespace napi_quickfix
52
+
53
+ #endif // NAPI_QUICKFIX_MESSAGE_WRAP_H
@@ -0,0 +1,91 @@
1
+ #include "session_id_wrap.h"
2
+
3
+ #include <string>
4
+
5
+ #include "errors.h"
6
+
7
+ namespace napi_quickfix {
8
+
9
+ Napi::FunctionReference SessionIDWrap::constructor_;
10
+
11
+ Napi::Object SessionIDWrap::Init(Napi::Env env, Napi::Object exports) {
12
+ Napi::Function func = DefineClass(
13
+ env, "SessionID",
14
+ {
15
+ InstanceMethod("getBeginString", &SessionIDWrap::GetBeginString),
16
+ InstanceMethod("getSenderCompID", &SessionIDWrap::GetSenderCompID),
17
+ InstanceMethod("getTargetCompID", &SessionIDWrap::GetTargetCompID),
18
+ InstanceMethod("getSessionQualifier",
19
+ &SessionIDWrap::GetSessionQualifier),
20
+ InstanceMethod("toString", &SessionIDWrap::ToString),
21
+ });
22
+
23
+ constructor_ = Napi::Persistent(func);
24
+ constructor_.SuppressDestruct();
25
+
26
+ exports.Set("SessionIDWrap", func);
27
+ return exports;
28
+ }
29
+
30
+ Napi::Object SessionIDWrap::NewInstance(Napi::Env env,
31
+ const FIX::SessionID& id) {
32
+ Napi::Object obj = constructor_.New(
33
+ {Napi::String::New(env, id.getBeginString().getValue()),
34
+ Napi::String::New(env, id.getSenderCompID().getValue()),
35
+ Napi::String::New(env, id.getTargetCompID().getValue()),
36
+ Napi::String::New(env, id.getSessionQualifier())});
37
+ return obj;
38
+ }
39
+
40
+ SessionIDWrap* SessionIDWrap::UnwrapArg(Napi::Env env, Napi::Value value,
41
+ const char* argName) {
42
+ if (!value.IsObject() ||
43
+ !value.As<Napi::Object>().InstanceOf(constructor_.Value())) {
44
+ throw Napi::TypeError::New(
45
+ env, std::string(argName) + " must be a SessionID instance");
46
+ }
47
+ return Napi::ObjectWrap<SessionIDWrap>::Unwrap(value.As<Napi::Object>());
48
+ }
49
+
50
+ SessionIDWrap::SessionIDWrap(const Napi::CallbackInfo& info)
51
+ : Napi::ObjectWrap<SessionIDWrap>(info) {
52
+ Napi::Env env = info.Env();
53
+ if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() ||
54
+ !info[2].IsString()) {
55
+ throw Napi::TypeError::New(
56
+ env,
57
+ "new SessionID(beginString, senderCompID, targetCompID, qualifier?)");
58
+ }
59
+ std::string begin = info[0].As<Napi::String>().Utf8Value();
60
+ std::string sender = info[1].As<Napi::String>().Utf8Value();
61
+ std::string target = info[2].As<Napi::String>().Utf8Value();
62
+ std::string qualifier;
63
+ if (info.Length() >= 4 && info[3].IsString()) {
64
+ qualifier = info[3].As<Napi::String>().Utf8Value();
65
+ }
66
+ session_id_ = FIX::SessionID(begin, sender, target, qualifier);
67
+ }
68
+
69
+ Napi::Value SessionIDWrap::GetBeginString(const Napi::CallbackInfo& info) {
70
+ return Napi::String::New(info.Env(), session_id_.getBeginString().getValue());
71
+ }
72
+
73
+ Napi::Value SessionIDWrap::GetSenderCompID(const Napi::CallbackInfo& info) {
74
+ return Napi::String::New(info.Env(),
75
+ session_id_.getSenderCompID().getValue());
76
+ }
77
+
78
+ Napi::Value SessionIDWrap::GetTargetCompID(const Napi::CallbackInfo& info) {
79
+ return Napi::String::New(info.Env(),
80
+ session_id_.getTargetCompID().getValue());
81
+ }
82
+
83
+ Napi::Value SessionIDWrap::GetSessionQualifier(const Napi::CallbackInfo& info) {
84
+ return Napi::String::New(info.Env(), session_id_.getSessionQualifier());
85
+ }
86
+
87
+ Napi::Value SessionIDWrap::ToString(const Napi::CallbackInfo& info) {
88
+ return Napi::String::New(info.Env(), session_id_.toString());
89
+ }
90
+
91
+ } // namespace napi_quickfix
@@ -0,0 +1,46 @@
1
+ #ifndef NAPI_QUICKFIX_SESSION_ID_WRAP_H
2
+ #define NAPI_QUICKFIX_SESSION_ID_WRAP_H
3
+
4
+ #include <napi.h>
5
+
6
+ #include "quickfix/SessionID.h"
7
+
8
+ namespace napi_quickfix {
9
+
10
+ // Napi::ObjectWrap over FIX::SessionID.
11
+ //
12
+ // new SessionID(beginString, senderCompID, targetCompID, qualifier?)
13
+ //
14
+ // Getters: getBeginString / getSenderCompID / getTargetCompID /
15
+ // getSessionQualifier; plus toString.
16
+ class SessionIDWrap : public Napi::ObjectWrap<SessionIDWrap> {
17
+ public:
18
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
19
+
20
+ // Build a JS SessionID wrapping a copy of the given FIX::SessionID.
21
+ static Napi::Object NewInstance(Napi::Env env, const FIX::SessionID& id);
22
+
23
+ explicit SessionIDWrap(const Napi::CallbackInfo& info);
24
+
25
+ FIX::SessionID& SessionID() { return session_id_; }
26
+ const FIX::SessionID& SessionID() const { return session_id_; }
27
+
28
+ // Unwrap a JS value that must be a SessionID; throws Napi::TypeError if not.
29
+ static SessionIDWrap* UnwrapArg(Napi::Env env, Napi::Value value,
30
+ const char* argName);
31
+
32
+ private:
33
+ static Napi::FunctionReference constructor_;
34
+
35
+ Napi::Value GetBeginString(const Napi::CallbackInfo& info);
36
+ Napi::Value GetSenderCompID(const Napi::CallbackInfo& info);
37
+ Napi::Value GetTargetCompID(const Napi::CallbackInfo& info);
38
+ Napi::Value GetSessionQualifier(const Napi::CallbackInfo& info);
39
+ Napi::Value ToString(const Napi::CallbackInfo& info);
40
+
41
+ FIX::SessionID session_id_;
42
+ };
43
+
44
+ } // namespace napi_quickfix
45
+
46
+ #endif // NAPI_QUICKFIX_SESSION_ID_WRAP_H
@@ -0,0 +1,98 @@
1
+ #include "session_settings_wrap.h"
2
+
3
+ #include <set>
4
+ #include <sstream>
5
+ #include <string>
6
+
7
+ #include "errors.h"
8
+ #include "quickfix/SessionID.h"
9
+ #include "session_id_wrap.h"
10
+
11
+ namespace napi_quickfix {
12
+
13
+ Napi::FunctionReference SessionSettingsWrap::constructor_;
14
+
15
+ Napi::Object SessionSettingsWrap::Init(Napi::Env env, Napi::Object exports) {
16
+ Napi::Function func = DefineClass(
17
+ env, "SessionSettings",
18
+ {
19
+ StaticMethod("fromString", &SessionSettingsWrap::FromString),
20
+ StaticMethod("fromFile", &SessionSettingsWrap::FromFile),
21
+ InstanceMethod("getSessions", &SessionSettingsWrap::GetSessions),
22
+ });
23
+
24
+ constructor_ = Napi::Persistent(func);
25
+ constructor_.SuppressDestruct();
26
+
27
+ exports.Set("SessionSettingsWrap", func);
28
+ return exports;
29
+ }
30
+
31
+ SessionSettingsWrap* SessionSettingsWrap::UnwrapArg(Napi::Env env,
32
+ Napi::Value value,
33
+ const char* argName) {
34
+ if (!value.IsObject() ||
35
+ !value.As<Napi::Object>().InstanceOf(constructor_.Value())) {
36
+ throw Napi::TypeError::New(
37
+ env, std::string(argName) + " must be a SessionSettings instance");
38
+ }
39
+ return Napi::ObjectWrap<SessionSettingsWrap>::Unwrap(
40
+ value.As<Napi::Object>());
41
+ }
42
+
43
+ // Constructor is internal: (kind: "string"|"file", payload: string).
44
+ // Users go through the static fromString/fromFile factories.
45
+ SessionSettingsWrap::SessionSettingsWrap(const Napi::CallbackInfo& info)
46
+ : Napi::ObjectWrap<SessionSettingsWrap>(info) {
47
+ Napi::Env env = info.Env();
48
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
49
+ throw Napi::TypeError::New(
50
+ env,
51
+ "SessionSettings cannot be constructed directly; use "
52
+ "SessionSettings.fromString() or SessionSettings.fromFile()");
53
+ }
54
+ std::string kind = info[0].As<Napi::String>().Utf8Value();
55
+ std::string payload = info[1].As<Napi::String>().Utf8Value();
56
+
57
+ NQ_TRY(env) {
58
+ if (kind == "string") {
59
+ std::istringstream stream(payload);
60
+ settings_ = FIX::SessionSettings(stream);
61
+ } else { // "file"
62
+ settings_ = FIX::SessionSettings(payload);
63
+ }
64
+ } NQ_CATCH(env)
65
+ }
66
+
67
+ Napi::Value SessionSettingsWrap::FromString(const Napi::CallbackInfo& info) {
68
+ Napi::Env env = info.Env();
69
+ if (info.Length() < 1 || !info[0].IsString()) {
70
+ throw Napi::TypeError::New(env, "fromString(text: string)");
71
+ }
72
+ return constructor_.New(
73
+ {Napi::String::New(env, "string"), info[0].As<Napi::String>()});
74
+ }
75
+
76
+ Napi::Value SessionSettingsWrap::FromFile(const Napi::CallbackInfo& info) {
77
+ Napi::Env env = info.Env();
78
+ if (info.Length() < 1 || !info[0].IsString()) {
79
+ throw Napi::TypeError::New(env, "fromFile(path: string)");
80
+ }
81
+ return constructor_.New(
82
+ {Napi::String::New(env, "file"), info[0].As<Napi::String>()});
83
+ }
84
+
85
+ Napi::Value SessionSettingsWrap::GetSessions(const Napi::CallbackInfo& info) {
86
+ Napi::Env env = info.Env();
87
+ NQ_TRY(env) {
88
+ std::set<FIX::SessionID> sessions = settings_.getSessions();
89
+ Napi::Array arr = Napi::Array::New(env, sessions.size());
90
+ uint32_t i = 0;
91
+ for (const auto& id : sessions) {
92
+ arr.Set(i++, SessionIDWrap::NewInstance(env, id));
93
+ }
94
+ return arr;
95
+ } NQ_CATCH(env)
96
+ }
97
+
98
+ } // namespace napi_quickfix
@@ -0,0 +1,40 @@
1
+ #ifndef NAPI_QUICKFIX_SESSION_SETTINGS_WRAP_H
2
+ #define NAPI_QUICKFIX_SESSION_SETTINGS_WRAP_H
3
+
4
+ #include <napi.h>
5
+
6
+ #include "quickfix/SessionSettings.h"
7
+
8
+ namespace napi_quickfix {
9
+
10
+ // Napi::ObjectWrap over FIX::SessionSettings.
11
+ //
12
+ // SessionSettings.fromString(text): SessionSettings
13
+ // SessionSettings.fromFile(path): SessionSettings
14
+ // settings.getSessions(): SessionID[]
15
+ class SessionSettingsWrap : public Napi::ObjectWrap<SessionSettingsWrap> {
16
+ public:
17
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
18
+
19
+ explicit SessionSettingsWrap(const Napi::CallbackInfo& info);
20
+
21
+ FIX::SessionSettings& Settings() { return settings_; }
22
+ const FIX::SessionSettings& Settings() const { return settings_; }
23
+
24
+ static SessionSettingsWrap* UnwrapArg(Napi::Env env, Napi::Value value,
25
+ const char* argName);
26
+
27
+ private:
28
+ static Napi::FunctionReference constructor_;
29
+
30
+ static Napi::Value FromString(const Napi::CallbackInfo& info);
31
+ static Napi::Value FromFile(const Napi::CallbackInfo& info);
32
+
33
+ Napi::Value GetSessions(const Napi::CallbackInfo& info);
34
+
35
+ FIX::SessionSettings settings_;
36
+ };
37
+
38
+ } // namespace napi_quickfix
39
+
40
+ #endif // NAPI_QUICKFIX_SESSION_SETTINGS_WRAP_H
@@ -0,0 +1,93 @@
1
+ #include <napi.h>
2
+
3
+ #include <utility>
4
+
5
+ #include "errors.h"
6
+ #include "message_wrap.h"
7
+ #include "quickfix/Message.h"
8
+ #include "quickfix/Session.h"
9
+ #include "quickfix/SessionID.h"
10
+ #include "session_id_wrap.h"
11
+
12
+ namespace napi_quickfix {
13
+
14
+ namespace {
15
+
16
+ // AsyncWorker for FIX::Session::sendToTarget. Runs OFF the JS main thread so the
17
+ // event loop stays free to service the ApplicationBridge TSFN BlockingCall(s)
18
+ // that toApp/toAdmin trigger during the send. Running on the main thread would
19
+ // deadlock (main blocked in sendToTarget -> toApp -> BlockingCall -> waits for
20
+ // main to run CallJs, which it can't).
21
+ //
22
+ // The FIX::Message and FIX::SessionID are COPIED BY VALUE on the JS thread
23
+ // before Queue()ing, so Execute() never touches any JS object. toApp may mutate
24
+ // the worker's private copy during send — that's fine, it's QuickFIX's business.
25
+ class SendToTargetWorker : public Napi::AsyncWorker {
26
+ public:
27
+ SendToTargetWorker(Napi::Env env, FIX::Message message, FIX::SessionID id)
28
+ : Napi::AsyncWorker(env),
29
+ deferred_(Napi::Promise::Deferred::New(env)),
30
+ message_(std::move(message)),
31
+ id_(std::move(id)) {}
32
+
33
+ Napi::Promise Promise() { return deferred_.Promise(); }
34
+
35
+ // Worker thread. NO Napi/JS access.
36
+ void Execute() override {
37
+ try {
38
+ result_ = FIX::Session::sendToTarget(message_, id_);
39
+ } catch (const std::exception& e) {
40
+ err_.Capture(e);
41
+ } catch (...) {
42
+ err_.has = true;
43
+ err_.message = "unknown native error";
44
+ err_.fixErrorName = "Error";
45
+ }
46
+ }
47
+
48
+ void OnOK() override {
49
+ Napi::Env env = Env();
50
+ Napi::HandleScope scope(env);
51
+ if (err_.has) {
52
+ deferred_.Reject(err_.ToError(env).Value());
53
+ } else {
54
+ deferred_.Resolve(Napi::Boolean::New(env, result_));
55
+ }
56
+ }
57
+
58
+ void OnError(const Napi::Error& e) override {
59
+ Napi::HandleScope scope(Env());
60
+ deferred_.Reject(e.Value());
61
+ }
62
+
63
+ private:
64
+ Napi::Promise::Deferred deferred_;
65
+ FIX::Message message_;
66
+ FIX::SessionID id_;
67
+ bool result_ = false;
68
+ CapturedError err_;
69
+ };
70
+
71
+ } // namespace
72
+
73
+ // sendToTarget(message: Message, sessionID: SessionID): Promise<boolean>
74
+ static Napi::Value SendToTarget(const Napi::CallbackInfo& info) {
75
+ Napi::Env env = info.Env();
76
+ MessageWrap* msg = MessageWrap::UnwrapArg(env, info[0], "message");
77
+ SessionIDWrap* id = SessionIDWrap::UnwrapArg(env, info[1], "sessionID");
78
+
79
+ // Copy the message and session id by value on the JS thread so the worker
80
+ // never touches JS-owned objects.
81
+ auto* worker =
82
+ new SendToTargetWorker(env, msg->Message(), id->SessionID());
83
+ Napi::Promise promise = worker->Promise();
84
+ worker->Queue();
85
+ return promise;
86
+ }
87
+
88
+ void RegisterSessionStatic(Napi::Env env, Napi::Object exports) {
89
+ exports.Set("sendToTarget",
90
+ Napi::Function::New(env, SendToTarget, "sendToTarget"));
91
+ }
92
+
93
+ } // namespace napi_quickfix
@@ -0,0 +1,30 @@
1
+ import { Engine, type EngineOptions } from './engine.js';
2
+ export type { EngineOptions } from './engine.js';
3
+ /**
4
+ * A FIX session acceptor (wraps `FIX::SocketAcceptor`). Acceptors listen for
5
+ * inbound connections from initiators as configured in
6
+ * {@link EngineOptions.settings}.
7
+ *
8
+ * Extends {@link Engine} (an {@link import('node:events').EventEmitter}), so you
9
+ * can observe lifecycle/message events in addition to supplying `handlers`.
10
+ * Remember: only `handlers` may mutate outbound messages or reject inbound ones;
11
+ * events are observe-only.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const acceptor = new Acceptor({
16
+ * settings: SessionSettings.fromString(cfg),
17
+ * handlers: {
18
+ * fromApp: (msg, id) => { void sendToTarget(makeReply(msg), id); },
19
+ * },
20
+ * });
21
+ * await acceptor.start();
22
+ * // ... later
23
+ * await acceptor.stop();
24
+ * ```
25
+ */
26
+ export declare class Acceptor extends Engine {
27
+ /** @param options Engine options. */
28
+ constructor(options: EngineOptions);
29
+ }
30
+ //# sourceMappingURL=acceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptor.d.ts","sourceRoot":"","sources":["../../src/acceptor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,QAAS,SAAQ,MAAM;IAClC,qCAAqC;IACrC,YAAY,OAAO,EAAE,aAAa,EAEjC;CACF"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Acceptor = void 0;
4
+ /**
5
+ * FIX session acceptor (server side — listens for initiators).
6
+ */
7
+ const native_js_1 = require("./native.js");
8
+ const engine_js_1 = require("./engine.js");
9
+ /**
10
+ * A FIX session acceptor (wraps `FIX::SocketAcceptor`). Acceptors listen for
11
+ * inbound connections from initiators as configured in
12
+ * {@link EngineOptions.settings}.
13
+ *
14
+ * Extends {@link Engine} (an {@link import('node:events').EventEmitter}), so you
15
+ * can observe lifecycle/message events in addition to supplying `handlers`.
16
+ * Remember: only `handlers` may mutate outbound messages or reject inbound ones;
17
+ * events are observe-only.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const acceptor = new Acceptor({
22
+ * settings: SessionSettings.fromString(cfg),
23
+ * handlers: {
24
+ * fromApp: (msg, id) => { void sendToTarget(makeReply(msg), id); },
25
+ * },
26
+ * });
27
+ * await acceptor.start();
28
+ * // ... later
29
+ * await acceptor.stop();
30
+ * ```
31
+ */
32
+ class Acceptor extends engine_js_1.Engine {
33
+ /** @param options Engine options. */
34
+ constructor(options) {
35
+ super(native_js_1.native.AcceptorWrap, options);
36
+ }
37
+ }
38
+ exports.Acceptor = Acceptor;
39
+ //# sourceMappingURL=acceptor.js.map