@homaiohq/napi-quickfix 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +5 -1
- package/README.md +344 -18
- package/cpp/acceptor_wrap.cpp +253 -19
- package/cpp/acceptor_wrap.h +60 -1
- package/cpp/addon.cpp +10 -6
- package/cpp/application_bridge.cpp +114 -30
- package/cpp/application_bridge.h +57 -4
- package/cpp/data_dictionary_wrap.cpp +89 -3
- package/cpp/data_dictionary_wrap.h +27 -5
- package/cpp/engine_common.h +108 -0
- package/cpp/engine_workers.h +99 -18
- package/cpp/field_map_ops.h +492 -0
- package/cpp/group_wrap.cpp +221 -0
- package/cpp/group_wrap.h +76 -0
- package/cpp/initiator_wrap.cpp +262 -25
- package/cpp/initiator_wrap.h +37 -2
- package/cpp/message_wrap.cpp +171 -78
- package/cpp/message_wrap.h +47 -5
- package/cpp/session_op_gate.h +201 -0
- package/cpp/session_static.cpp +139 -12
- package/cpp/session_wrap.cpp +438 -0
- package/cpp/session_wrap.h +144 -0
- package/dist/cjs/acceptor.d.ts +0 -1
- package/dist/cjs/acceptor.js +0 -1
- package/dist/cjs/application.d.ts +0 -1
- package/dist/cjs/application.js +0 -1
- package/dist/cjs/data-dictionary.d.ts +21 -3
- package/dist/cjs/data-dictionary.js +32 -4
- package/dist/cjs/engine.d.ts +42 -4
- package/dist/cjs/engine.js +50 -5
- package/dist/cjs/enums.d.ts +48 -14
- package/dist/cjs/enums.js +20 -25
- package/dist/cjs/field-value.d.ts +12 -0
- package/dist/cjs/field-value.js +40 -0
- package/dist/cjs/generated/fields.d.ts +0 -1
- package/dist/cjs/generated/fields.js +0 -1
- package/dist/cjs/generated/values.d.ts +8548 -0
- package/dist/cjs/generated/values.js +8580 -0
- package/dist/cjs/group.d.ts +126 -0
- package/dist/cjs/group.js +185 -0
- package/dist/cjs/index.d.ts +28 -5
- package/dist/cjs/index.js +30 -28
- package/dist/cjs/initiator.d.ts +0 -1
- package/dist/cjs/initiator.js +0 -1
- package/dist/cjs/message.d.ts +129 -5
- package/dist/cjs/message.js +146 -9
- package/dist/cjs/native.d.ts +179 -17
- package/dist/cjs/native.js +0 -1
- package/dist/cjs/session-id.d.ts +12 -1
- package/dist/cjs/session-id.js +21 -1
- package/dist/cjs/session-settings.d.ts +0 -1
- package/dist/cjs/session-settings.js +0 -1
- package/dist/cjs/session.d.ts +213 -0
- package/dist/cjs/session.js +337 -0
- package/dist/cjs/tls-tunnel.d.ts +143 -0
- package/dist/cjs/tls-tunnel.js +261 -0
- package/dist/esm/acceptor.d.ts +0 -1
- package/dist/esm/acceptor.js +0 -1
- package/dist/esm/application.d.ts +0 -1
- package/dist/esm/application.js +0 -1
- package/dist/esm/data-dictionary.d.ts +21 -3
- package/dist/esm/data-dictionary.js +32 -4
- package/dist/esm/engine.d.ts +42 -4
- package/dist/esm/engine.js +51 -6
- package/dist/esm/enums.d.ts +48 -14
- package/dist/esm/enums.js +14 -24
- package/dist/esm/field-value.d.ts +12 -0
- package/dist/esm/field-value.js +37 -0
- package/dist/esm/generated/fields.d.ts +0 -1
- package/dist/esm/generated/fields.js +0 -1
- package/dist/esm/generated/values.d.ts +8548 -0
- package/dist/esm/generated/values.js +8564 -0
- package/dist/esm/group.d.ts +126 -0
- package/dist/esm/group.js +181 -0
- package/dist/esm/index.d.ts +28 -5
- package/dist/esm/index.js +18 -26
- package/dist/esm/initiator.d.ts +0 -1
- package/dist/esm/initiator.js +0 -1
- package/dist/esm/message.d.ts +129 -5
- package/dist/esm/message.js +146 -9
- package/dist/esm/native.d.ts +179 -17
- package/dist/esm/native.js +0 -1
- package/dist/esm/session-id.d.ts +12 -1
- package/dist/esm/session-id.js +20 -1
- package/dist/esm/session-settings.d.ts +0 -1
- package/dist/esm/session-settings.js +0 -1
- package/dist/esm/session.d.ts +213 -0
- package/dist/esm/session.js +329 -0
- package/dist/esm/tls-tunnel.d.ts +143 -0
- package/dist/esm/tls-tunnel.js +256 -0
- package/package.json +13 -1
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/cpp/enums.cpp +0 -66
- package/dist/cjs/acceptor.d.ts.map +0 -1
- package/dist/cjs/acceptor.js.map +0 -1
- package/dist/cjs/application.d.ts.map +0 -1
- package/dist/cjs/application.js.map +0 -1
- package/dist/cjs/data-dictionary.d.ts.map +0 -1
- package/dist/cjs/data-dictionary.js.map +0 -1
- package/dist/cjs/engine.d.ts.map +0 -1
- package/dist/cjs/engine.js.map +0 -1
- package/dist/cjs/enums.d.ts.map +0 -1
- package/dist/cjs/enums.js.map +0 -1
- package/dist/cjs/generated/fields.d.ts.map +0 -1
- package/dist/cjs/generated/fields.js.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/initiator.d.ts.map +0 -1
- package/dist/cjs/initiator.js.map +0 -1
- package/dist/cjs/message.d.ts.map +0 -1
- package/dist/cjs/message.js.map +0 -1
- package/dist/cjs/native.d.ts.map +0 -1
- package/dist/cjs/native.js.map +0 -1
- package/dist/cjs/session-id.d.ts.map +0 -1
- package/dist/cjs/session-id.js.map +0 -1
- package/dist/cjs/session-settings.d.ts.map +0 -1
- package/dist/cjs/session-settings.js.map +0 -1
- package/dist/esm/acceptor.d.ts.map +0 -1
- package/dist/esm/acceptor.js.map +0 -1
- package/dist/esm/application.d.ts.map +0 -1
- package/dist/esm/application.js.map +0 -1
- package/dist/esm/data-dictionary.d.ts.map +0 -1
- package/dist/esm/data-dictionary.js.map +0 -1
- package/dist/esm/engine.d.ts.map +0 -1
- package/dist/esm/engine.js.map +0 -1
- package/dist/esm/enums.d.ts.map +0 -1
- package/dist/esm/enums.js.map +0 -1
- package/dist/esm/generated/fields.d.ts.map +0 -1
- package/dist/esm/generated/fields.js.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/initiator.d.ts.map +0 -1
- package/dist/esm/initiator.js.map +0 -1
- package/dist/esm/message.d.ts.map +0 -1
- package/dist/esm/message.js.map +0 -1
- package/dist/esm/native.d.ts.map +0 -1
- package/dist/esm/native.js.map +0 -1
- package/dist/esm/session-id.d.ts.map +0 -1
- package/dist/esm/session-id.js.map +0 -1
- package/dist/esm/session-settings.d.ts.map +0 -1
- package/dist/esm/session-settings.js.map +0 -1
package/cpp/engine_common.h
CHANGED
|
@@ -1,10 +1,74 @@
|
|
|
1
1
|
#ifndef NAPI_QUICKFIX_ENGINE_COMMON_H
|
|
2
2
|
#define NAPI_QUICKFIX_ENGINE_COMMON_H
|
|
3
3
|
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <set>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
#include "quickfix/Exceptions.h"
|
|
4
10
|
#include "quickfix/Log.h"
|
|
11
|
+
#include "quickfix/Session.h"
|
|
12
|
+
#include "quickfix/SessionID.h"
|
|
13
|
+
#include "quickfix/SessionSettings.h"
|
|
5
14
|
|
|
6
15
|
namespace napi_quickfix {
|
|
7
16
|
|
|
17
|
+
// Throw ConfigError("Duplicate Session ...") if any [SESSION] of `settings`
|
|
18
|
+
// that an engine of `connectionType` ("initiator" / "acceptor") would create
|
|
19
|
+
// already has a live FIX::Session in this process. QuickFIX only rejects
|
|
20
|
+
// duplicates within ONE settings object (SessionSettings::set); across engines
|
|
21
|
+
// its Session constructor ignores addSession()'s result, so a second engine
|
|
22
|
+
// would silently shadow the first in the process-wide registry that
|
|
23
|
+
// lookupSession()/sendToTarget() read, and the per-engine SessionOpGate
|
|
24
|
+
// registry would couple operations with the wrong engine. Call it on the JS
|
|
25
|
+
// thread right before constructing the engine: engines are only created and
|
|
26
|
+
// destroyed on that thread, so the check cannot race another engine.
|
|
27
|
+
inline void RejectDuplicateSessions(const FIX::SessionSettings& settings,
|
|
28
|
+
const std::string& connectionType) {
|
|
29
|
+
for (const FIX::SessionID& id : settings.getSessions()) {
|
|
30
|
+
const FIX::Dictionary& dict = settings.get(id);
|
|
31
|
+
// Sessions of the other type are skipped by the engine (and a missing
|
|
32
|
+
// ConnectionType is its own ConfigError, raised by the engine itself).
|
|
33
|
+
if (!dict.has("ConnectionType") ||
|
|
34
|
+
dict.getString("ConnectionType") != connectionType) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (FIX::Session::doesSessionExist(id)) {
|
|
38
|
+
throw FIX::ConfigError("Duplicate Session " + id.toString());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Delete the FIX::Session objects a FAILED engine constructor left behind.
|
|
44
|
+
// FIX::Initiator/Acceptor::initialize() creates one FIX::Session per [SESSION]
|
|
45
|
+
// of its connection type, registering each in the process-wide registry, and
|
|
46
|
+
// if a later [SESSION] throws ConfigError the constructor unwinds WITHOUT the
|
|
47
|
+
// engine destructor that would delete them: they stay registered with no
|
|
48
|
+
// owner, so lookupSession() keeps resolving them and every later engine with
|
|
49
|
+
// one of those ids is rejected as a duplicate for the rest of the process.
|
|
50
|
+
// Call it right after the engine constructor threw, on the JS thread, with
|
|
51
|
+
// the same `settings`/`connectionType` RejectDuplicateSessions() was called
|
|
52
|
+
// with just before: that check guarantees every session of ours that exists
|
|
53
|
+
// now was created by the failed constructor, and each one is fully
|
|
54
|
+
// constructed: the only callback FIX::Session's constructor makes after
|
|
55
|
+
// registering itself is onCreate, which the bridge dispatches fire-and-forget
|
|
56
|
+
// (a throwing JS handler never unwinds the constructor). ~Session unregisters
|
|
57
|
+
// it and destroys its store/log through the factories the wrap still owns
|
|
58
|
+
// (public virtual destructor; the engine destructor does exactly this
|
|
59
|
+
// `delete`).
|
|
60
|
+
inline void DestroyOrphanedSessions(const FIX::SessionSettings& settings,
|
|
61
|
+
const std::string& connectionType) {
|
|
62
|
+
for (const FIX::SessionID& id : settings.getSessions()) {
|
|
63
|
+
const FIX::Dictionary& dict = settings.get(id);
|
|
64
|
+
if (!dict.has("ConnectionType") ||
|
|
65
|
+
dict.getString("ConnectionType") != connectionType) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
delete FIX::Session::lookupSession(id); // nullptr-safe
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
8
72
|
// A LogFactory that produces no-op NullLog instances, for log:'none'. QuickFIX
|
|
9
73
|
// ships NullLog but no public NullLogFactory, so we provide one.
|
|
10
74
|
class NullLogFactory : public FIX::LogFactory {
|
|
@@ -14,6 +78,50 @@ class NullLogFactory : public FIX::LogFactory {
|
|
|
14
78
|
void destroy(FIX::Log* log) override { delete log; }
|
|
15
79
|
};
|
|
16
80
|
|
|
81
|
+
// The wrap's LogFactory: forwards to the real one (File/Screen/Null) and
|
|
82
|
+
// remembers the ENGINE-level Log that FIX::Initiator/Acceptor's constructor
|
|
83
|
+
// requests with create() -- no SessionID -- before it runs initialize(). When
|
|
84
|
+
// initialize() throws ConfigError the constructor unwinds without the engine
|
|
85
|
+
// destructor that would hand that Log back (`m_pLogFactory->destroy(m_pLog)`),
|
|
86
|
+
// and QuickFIX's own factories do not reclaim it either (~FileLogFactory does
|
|
87
|
+
// not delete its m_globalLog): with log:'file' every failed construction left
|
|
88
|
+
// two open streams behind. The wrap calls DestroyOrphanedEngineLog() next to
|
|
89
|
+
// DestroyOrphanedSessions() when the engine constructor threw. The engine
|
|
90
|
+
// keeps a raw pointer to its LogFactory for its whole life (per-session logs,
|
|
91
|
+
// and the destroy() in its destructor), so this object must outlive it: it is
|
|
92
|
+
// the wrap's logFactory_ member, declared before the engine.
|
|
93
|
+
class TrackingLogFactory : public FIX::LogFactory {
|
|
94
|
+
public:
|
|
95
|
+
explicit TrackingLogFactory(std::unique_ptr<FIX::LogFactory> inner)
|
|
96
|
+
: inner_(std::move(inner)) {}
|
|
97
|
+
|
|
98
|
+
FIX::Log* create() override {
|
|
99
|
+
FIX::Log* log = inner_->create(); // may throw: nothing recorded then
|
|
100
|
+
engineLog_ = log;
|
|
101
|
+
return log;
|
|
102
|
+
}
|
|
103
|
+
FIX::Log* create(const FIX::SessionID& id) override {
|
|
104
|
+
return inner_->create(id);
|
|
105
|
+
}
|
|
106
|
+
void destroy(FIX::Log* log) override {
|
|
107
|
+
if (log == engineLog_) engineLog_ = nullptr;
|
|
108
|
+
inner_->destroy(log);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Destroy the engine-level Log if the engine constructor requested one and
|
|
112
|
+
// then threw, so no engine will ever destroy() it. No-op otherwise.
|
|
113
|
+
void DestroyOrphanedEngineLog() {
|
|
114
|
+
if (engineLog_ == nullptr) return;
|
|
115
|
+
FIX::Log* log = engineLog_;
|
|
116
|
+
engineLog_ = nullptr;
|
|
117
|
+
inner_->destroy(log);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private:
|
|
121
|
+
std::unique_ptr<FIX::LogFactory> inner_;
|
|
122
|
+
FIX::Log* engineLog_ = nullptr;
|
|
123
|
+
};
|
|
124
|
+
|
|
17
125
|
} // namespace napi_quickfix
|
|
18
126
|
|
|
19
127
|
#endif // NAPI_QUICKFIX_ENGINE_COMMON_H
|
package/cpp/engine_workers.h
CHANGED
|
@@ -3,15 +3,43 @@
|
|
|
3
3
|
|
|
4
4
|
#include <napi.h>
|
|
5
5
|
|
|
6
|
+
#include <condition_variable>
|
|
6
7
|
#include <functional>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <mutex>
|
|
7
10
|
#include <utility>
|
|
8
11
|
|
|
9
12
|
#include "errors.h"
|
|
10
13
|
|
|
11
14
|
namespace napi_quickfix {
|
|
12
15
|
|
|
16
|
+
// One-shot completion signal from a libuv worker to the JS thread. The stop
|
|
17
|
+
// worker signals it once its operation (FIX stop() + gate freeze) has
|
|
18
|
+
// returned, success or not; the wrap's Teardown() waits on it rather than
|
|
19
|
+
// destroying the engine under an operation that may still be running it.
|
|
20
|
+
class StopCompletion {
|
|
21
|
+
public:
|
|
22
|
+
void Signal() {
|
|
23
|
+
{
|
|
24
|
+
std::lock_guard<std::mutex> lock(mtx_);
|
|
25
|
+
done_ = true;
|
|
26
|
+
}
|
|
27
|
+
cv_.notify_all();
|
|
28
|
+
}
|
|
29
|
+
void Wait() {
|
|
30
|
+
std::unique_lock<std::mutex> lock(mtx_);
|
|
31
|
+
cv_.wait(lock, [this] { return done_; });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
std::mutex mtx_;
|
|
36
|
+
std::condition_variable cv_;
|
|
37
|
+
bool done_ = false;
|
|
38
|
+
};
|
|
39
|
+
|
|
13
40
|
// A generic AsyncWorker that runs a blocking QuickFIX engine operation
|
|
14
41
|
// (start/stop) on a libuv worker thread and resolves/rejects a JS Promise.
|
|
42
|
+
// (The per-message SendToTargetWorker lives in session_static.cpp.)
|
|
15
43
|
//
|
|
16
44
|
// Why off-thread: start()/stop() synchronously invoke FIX::Application callbacks
|
|
17
45
|
// (toAdmin/toApp during send-on-logon, onLogout during stop) which the
|
|
@@ -22,12 +50,61 @@ namespace napi_quickfix {
|
|
|
22
50
|
//
|
|
23
51
|
// Lifetime: the worker holds a Napi::ObjectReference to the owning wrap object so
|
|
24
52
|
// it (and its bridge / engine / factories) can't be GC'd while the op is in
|
|
25
|
-
// flight. The
|
|
53
|
+
// flight. The reference is released when the promise is settled -- through
|
|
54
|
+
// the Settlement handed to onSettled, which the wrap may settle later than
|
|
55
|
+
// OnOK (on the JS thread) when the promise has to wait for deferred work,
|
|
56
|
+
// e.g. an engine destruction queued behind the bridge's pending callbacks.
|
|
26
57
|
class EngineOpWorker : public Napi::AsyncWorker {
|
|
27
58
|
public:
|
|
28
|
-
//
|
|
59
|
+
// The promise plus the owner reference, moved out of the worker (which
|
|
60
|
+
// deletes itself once OnOK/OnError returns) so settling can outlive it.
|
|
61
|
+
class Settlement {
|
|
62
|
+
public:
|
|
63
|
+
Settlement(Napi::Promise::Deferred deferred, Napi::ObjectReference ownerRef,
|
|
64
|
+
CapturedError err)
|
|
65
|
+
: deferred_(std::move(deferred)),
|
|
66
|
+
ownerRef_(std::move(ownerRef)),
|
|
67
|
+
err_(std::move(err)) {}
|
|
68
|
+
|
|
69
|
+
// Resolve, or reject with the error the op threw, and release the owner
|
|
70
|
+
// reference. JS thread only; idempotent.
|
|
71
|
+
void Settle() {
|
|
72
|
+
if (done_) return;
|
|
73
|
+
done_ = true;
|
|
74
|
+
Napi::Env env = deferred_.Env();
|
|
75
|
+
Napi::HandleScope scope(env);
|
|
76
|
+
if (err_.has) {
|
|
77
|
+
deferred_.Reject(err_.ToError(env).Value());
|
|
78
|
+
} else {
|
|
79
|
+
deferred_.Resolve(env.Undefined());
|
|
80
|
+
}
|
|
81
|
+
ownerRef_.Reset();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The environment is going away before the promise could be settled:
|
|
85
|
+
// leave the promise pending and the reference to the environment's own
|
|
86
|
+
// cleanup, touching nothing that may already be torn down.
|
|
87
|
+
void Abandon() {
|
|
88
|
+
if (done_) return;
|
|
89
|
+
done_ = true;
|
|
90
|
+
ownerRef_.SuppressDestruct();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private:
|
|
94
|
+
Napi::Promise::Deferred deferred_;
|
|
95
|
+
Napi::ObjectReference ownerRef_;
|
|
96
|
+
CapturedError err_;
|
|
97
|
+
bool done_ = false;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// onSettled(ok, settlement) runs on the JS thread once op() has returned
|
|
101
|
+
// (ok) or thrown (!ok). It must eventually Settle() (or Abandon()) the
|
|
102
|
+
// settlement -- right away, or later on the JS thread.
|
|
103
|
+
using OnSettled =
|
|
104
|
+
std::function<void(bool, const std::shared_ptr<Settlement>&)>;
|
|
105
|
+
|
|
29
106
|
EngineOpWorker(Napi::Env env, Napi::Object owner, std::function<void()> op,
|
|
30
|
-
|
|
107
|
+
OnSettled onSettled)
|
|
31
108
|
: Napi::AsyncWorker(env),
|
|
32
109
|
deferred_(Napi::Promise::Deferred::New(env)),
|
|
33
110
|
op_(std::move(op)),
|
|
@@ -52,32 +129,36 @@ class EngineOpWorker : public Napi::AsyncWorker {
|
|
|
52
129
|
|
|
53
130
|
// Runs on the JS thread.
|
|
54
131
|
void OnOK() override {
|
|
55
|
-
Napi::
|
|
56
|
-
|
|
57
|
-
if (onSettled_) onSettled_(!err_.has);
|
|
58
|
-
if (err_.has) {
|
|
59
|
-
deferred_.Reject(err_.ToError(env).Value());
|
|
60
|
-
} else {
|
|
61
|
-
deferred_.Resolve(env.Undefined());
|
|
62
|
-
}
|
|
63
|
-
ownerRef_.Reset();
|
|
132
|
+
Napi::HandleScope scope(Env());
|
|
133
|
+
Dispatch();
|
|
64
134
|
}
|
|
65
135
|
|
|
66
136
|
// Runs on the JS thread (only if AsyncWorker::SetError was used; we don't, so
|
|
67
137
|
// errors flow through OnOK — but provide OnError for completeness/safety).
|
|
68
138
|
void OnError(const Napi::Error& e) override {
|
|
69
|
-
Napi::
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
139
|
+
Napi::HandleScope scope(Env());
|
|
140
|
+
err_.has = true;
|
|
141
|
+
err_.message = e.Message();
|
|
142
|
+
err_.fixErrorName = "Error";
|
|
143
|
+
Dispatch();
|
|
74
144
|
}
|
|
75
145
|
|
|
76
146
|
private:
|
|
147
|
+
void Dispatch() {
|
|
148
|
+
const bool ok = !err_.has;
|
|
149
|
+
auto settlement = std::make_shared<Settlement>(
|
|
150
|
+
std::move(deferred_), std::move(ownerRef_), std::move(err_));
|
|
151
|
+
if (onSettled_) {
|
|
152
|
+
onSettled_(ok, settlement);
|
|
153
|
+
} else {
|
|
154
|
+
settlement->Settle();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
77
158
|
Napi::Promise::Deferred deferred_;
|
|
78
159
|
Napi::ObjectReference ownerRef_;
|
|
79
160
|
std::function<void()> op_;
|
|
80
|
-
|
|
161
|
+
OnSettled onSettled_;
|
|
81
162
|
CapturedError err_;
|
|
82
163
|
};
|
|
83
164
|
|