@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.
- package/CMakeLists.txt +131 -0
- package/LICENSE +21 -0
- package/README.md +421 -2
- package/THIRD_PARTY_LICENSES +48 -0
- package/cpp/acceptor_wrap.cpp +234 -0
- package/cpp/acceptor_wrap.h +57 -0
- package/cpp/addon.cpp +83 -0
- package/cpp/application_bridge.cpp +382 -0
- package/cpp/application_bridge.h +151 -0
- package/cpp/data_dictionary_wrap.cpp +79 -0
- package/cpp/data_dictionary_wrap.h +37 -0
- package/cpp/engine_common.cpp +5 -0
- package/cpp/engine_common.h +19 -0
- package/cpp/engine_workers.h +86 -0
- package/cpp/enums.cpp +66 -0
- package/cpp/errors.h +142 -0
- package/cpp/initiator_wrap.cpp +255 -0
- package/cpp/initiator_wrap.h +69 -0
- package/cpp/message_wrap.cpp +195 -0
- package/cpp/message_wrap.h +53 -0
- package/cpp/session_id_wrap.cpp +91 -0
- package/cpp/session_id_wrap.h +46 -0
- package/cpp/session_settings_wrap.cpp +98 -0
- package/cpp/session_settings_wrap.h +40 -0
- package/cpp/session_static.cpp +93 -0
- package/dist/cjs/acceptor.d.ts +30 -0
- package/dist/cjs/acceptor.d.ts.map +1 -0
- package/dist/cjs/acceptor.js +39 -0
- package/dist/cjs/acceptor.js.map +1 -0
- package/dist/cjs/application.d.ts +116 -0
- package/dist/cjs/application.d.ts.map +1 -0
- package/dist/cjs/application.js +63 -0
- package/dist/cjs/application.js.map +1 -0
- package/dist/cjs/data-dictionary.d.ts +49 -0
- package/dist/cjs/data-dictionary.d.ts.map +1 -0
- package/dist/cjs/data-dictionary.js +77 -0
- package/dist/cjs/data-dictionary.js.map +1 -0
- package/dist/cjs/engine.d.ts +114 -0
- package/dist/cjs/engine.d.ts.map +1 -0
- package/dist/cjs/engine.js +138 -0
- package/dist/cjs/engine.js.map +1 -0
- package/dist/cjs/enums.d.ts +22 -0
- package/dist/cjs/enums.d.ts.map +1 -0
- package/dist/cjs/enums.js +49 -0
- package/dist/cjs/enums.js.map +1 -0
- package/dist/cjs/generated/fields.d.ts +6123 -0
- package/dist/cjs/generated/fields.d.ts.map +1 -0
- package/dist/cjs/generated/fields.js +6128 -0
- package/dist/cjs/generated/fields.js.map +1 -0
- package/dist/cjs/index.d.ts +47 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +73 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/initiator.d.ts +30 -0
- package/dist/cjs/initiator.d.ts.map +1 -0
- package/dist/cjs/initiator.js +39 -0
- package/dist/cjs/initiator.js.map +1 -0
- package/dist/cjs/load-native.cjs +5 -0
- package/dist/cjs/message.d.ts +114 -0
- package/dist/cjs/message.d.ts.map +1 -0
- package/dist/cjs/message.js +162 -0
- package/dist/cjs/message.js.map +1 -0
- package/dist/cjs/native.d.ts +163 -0
- package/dist/cjs/native.d.ts.map +1 -0
- package/dist/cjs/native.js +23 -0
- package/dist/cjs/native.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/session-id.d.ts +40 -0
- package/dist/cjs/session-id.d.ts.map +1 -0
- package/dist/cjs/session-id.js +67 -0
- package/dist/cjs/session-id.js.map +1 -0
- package/dist/cjs/session-settings.d.ts +47 -0
- package/dist/cjs/session-settings.d.ts.map +1 -0
- package/dist/cjs/session-settings.js +76 -0
- package/dist/cjs/session-settings.js.map +1 -0
- package/dist/esm/acceptor.d.ts +30 -0
- package/dist/esm/acceptor.d.ts.map +1 -0
- package/dist/esm/acceptor.js +35 -0
- package/dist/esm/acceptor.js.map +1 -0
- package/dist/esm/application.d.ts +116 -0
- package/dist/esm/application.d.ts.map +1 -0
- package/dist/esm/application.js +58 -0
- package/dist/esm/application.js.map +1 -0
- package/dist/esm/data-dictionary.d.ts +49 -0
- package/dist/esm/data-dictionary.d.ts.map +1 -0
- package/dist/esm/data-dictionary.js +73 -0
- package/dist/esm/data-dictionary.js.map +1 -0
- package/dist/esm/engine.d.ts +114 -0
- package/dist/esm/engine.d.ts.map +1 -0
- package/dist/esm/engine.js +134 -0
- package/dist/esm/engine.js.map +1 -0
- package/dist/esm/enums.d.ts +22 -0
- package/dist/esm/enums.d.ts.map +1 -0
- package/dist/esm/enums.js +45 -0
- package/dist/esm/enums.js.map +1 -0
- package/dist/esm/generated/fields.d.ts +6123 -0
- package/dist/esm/generated/fields.d.ts.map +1 -0
- package/dist/esm/generated/fields.js +6125 -0
- package/dist/esm/generated/fields.js.map +1 -0
- package/dist/esm/index.d.ts +47 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +52 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/initiator.d.ts +30 -0
- package/dist/esm/initiator.d.ts.map +1 -0
- package/dist/esm/initiator.js +35 -0
- package/dist/esm/initiator.js.map +1 -0
- package/dist/esm/load-native.cjs +5 -0
- package/dist/esm/message.d.ts +114 -0
- package/dist/esm/message.d.ts.map +1 -0
- package/dist/esm/message.js +156 -0
- package/dist/esm/message.js.map +1 -0
- package/dist/esm/native.d.ts +163 -0
- package/dist/esm/native.d.ts.map +1 -0
- package/dist/esm/native.js +17 -0
- package/dist/esm/native.js.map +1 -0
- package/dist/esm/session-id.d.ts +40 -0
- package/dist/esm/session-id.d.ts.map +1 -0
- package/dist/esm/session-id.js +63 -0
- package/dist/esm/session-id.js.map +1 -0
- package/dist/esm/session-settings.d.ts +47 -0
- package/dist/esm/session-settings.d.ts.map +1 -0
- package/dist/esm/session-settings.js +72 -0
- package/dist/esm/session-settings.js.map +1 -0
- package/package.json +83 -4
- 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/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
|