librats 0.3.1 → 0.5.1

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 (69) hide show
  1. package/README.md +405 -405
  2. package/binding.gyp +96 -95
  3. package/lib/index.d.ts +522 -522
  4. package/lib/index.js +82 -82
  5. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  7. package/native-src/CMakeLists.txt +360 -0
  8. package/native-src/LICENSE +21 -0
  9. package/native-src/src/bencode.cpp +485 -0
  10. package/native-src/src/bencode.h +145 -0
  11. package/native-src/src/bittorrent.cpp +3682 -0
  12. package/native-src/src/bittorrent.h +731 -0
  13. package/native-src/src/dht.cpp +2342 -0
  14. package/native-src/src/dht.h +501 -0
  15. package/native-src/src/encrypted_socket.cpp +817 -0
  16. package/native-src/src/encrypted_socket.h +239 -0
  17. package/native-src/src/file_transfer.cpp +1808 -0
  18. package/native-src/src/file_transfer.h +567 -0
  19. package/native-src/src/fs.cpp +639 -0
  20. package/native-src/src/fs.h +108 -0
  21. package/native-src/src/gossipsub.cpp +1137 -0
  22. package/native-src/src/gossipsub.h +403 -0
  23. package/native-src/src/ice.cpp +1386 -0
  24. package/native-src/src/ice.h +328 -0
  25. package/native-src/src/json.hpp +25526 -0
  26. package/native-src/src/krpc.cpp +558 -0
  27. package/native-src/src/krpc.h +145 -0
  28. package/native-src/src/librats.cpp +2715 -0
  29. package/native-src/src/librats.h +1729 -0
  30. package/native-src/src/librats_bittorrent.cpp +167 -0
  31. package/native-src/src/librats_c.cpp +1317 -0
  32. package/native-src/src/librats_c.h +237 -0
  33. package/native-src/src/librats_encryption.cpp +123 -0
  34. package/native-src/src/librats_file_transfer.cpp +226 -0
  35. package/native-src/src/librats_gossipsub.cpp +293 -0
  36. package/native-src/src/librats_ice.cpp +515 -0
  37. package/native-src/src/librats_logging.cpp +158 -0
  38. package/native-src/src/librats_mdns.cpp +171 -0
  39. package/native-src/src/librats_nat.cpp +571 -0
  40. package/native-src/src/librats_persistence.cpp +815 -0
  41. package/native-src/src/logger.h +412 -0
  42. package/native-src/src/mdns.cpp +1178 -0
  43. package/native-src/src/mdns.h +253 -0
  44. package/native-src/src/network_utils.cpp +598 -0
  45. package/native-src/src/network_utils.h +162 -0
  46. package/native-src/src/noise.cpp +981 -0
  47. package/native-src/src/noise.h +227 -0
  48. package/native-src/src/os.cpp +371 -0
  49. package/native-src/src/os.h +40 -0
  50. package/native-src/src/rats_export.h +17 -0
  51. package/native-src/src/sha1.cpp +163 -0
  52. package/native-src/src/sha1.h +42 -0
  53. package/native-src/src/socket.cpp +1376 -0
  54. package/native-src/src/socket.h +309 -0
  55. package/native-src/src/stun.cpp +484 -0
  56. package/native-src/src/stun.h +349 -0
  57. package/native-src/src/threadmanager.cpp +105 -0
  58. package/native-src/src/threadmanager.h +53 -0
  59. package/native-src/src/tracker.cpp +1110 -0
  60. package/native-src/src/tracker.h +268 -0
  61. package/native-src/src/version.cpp +24 -0
  62. package/native-src/src/version.h.in +45 -0
  63. package/native-src/version.rc.in +31 -0
  64. package/package.json +62 -68
  65. package/scripts/build-librats.js +241 -194
  66. package/scripts/postinstall.js +52 -52
  67. package/scripts/prepare-package.js +187 -91
  68. package/scripts/verify-installation.js +119 -119
  69. package/src/librats_node.cpp +1174 -1174
@@ -1,1174 +1,1174 @@
1
- #include <napi.h>
2
- #include <iostream>
3
- #include <string>
4
- #include <memory>
5
- #include <unordered_map>
6
- #include "librats_c.h"
7
-
8
- using namespace Napi;
9
-
10
- // Forward declarations
11
- class RatsClient;
12
-
13
- // Global callback storage
14
- struct CallbackData {
15
- Napi::FunctionReference callback;
16
- Napi::Env env;
17
-
18
- CallbackData(Napi::Env environment) : env(environment) {}
19
- };
20
-
21
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> connection_callbacks;
22
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> string_callbacks;
23
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> binary_callbacks;
24
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> json_callbacks;
25
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> disconnect_callbacks;
26
- std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> file_progress_callbacks;
27
-
28
- // C callback wrappers
29
- void connection_callback_wrapper(void* user_data, const char* peer_id) {
30
- rats_client_t client = static_cast<rats_client_t>(user_data);
31
- auto it = connection_callbacks.find(client);
32
- if (it != connection_callbacks.end() && it->second) {
33
- auto callback_data = it->second;
34
- callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
35
- }
36
- }
37
-
38
- void string_callback_wrapper(void* user_data, const char* peer_id, const char* message) {
39
- rats_client_t client = static_cast<rats_client_t>(user_data);
40
- auto it = string_callbacks.find(client);
41
- if (it != string_callbacks.end() && it->second) {
42
- auto callback_data = it->second;
43
- callback_data->callback.Call({
44
- Napi::String::New(callback_data->env, peer_id),
45
- Napi::String::New(callback_data->env, message)
46
- });
47
- }
48
- }
49
-
50
- void binary_callback_wrapper(void* user_data, const char* peer_id, const void* data, size_t size) {
51
- rats_client_t client = static_cast<rats_client_t>(user_data);
52
- auto it = binary_callbacks.find(client);
53
- if (it != binary_callbacks.end() && it->second) {
54
- auto callback_data = it->second;
55
- auto buffer = Napi::Buffer<uint8_t>::New(callback_data->env, size);
56
- memcpy(buffer.Data(), data, size);
57
- callback_data->callback.Call({
58
- Napi::String::New(callback_data->env, peer_id),
59
- buffer
60
- });
61
- }
62
- }
63
-
64
- void json_callback_wrapper(void* user_data, const char* peer_id, const char* json_str) {
65
- rats_client_t client = static_cast<rats_client_t>(user_data);
66
- auto it = json_callbacks.find(client);
67
- if (it != json_callbacks.end() && it->second) {
68
- auto callback_data = it->second;
69
- callback_data->callback.Call({
70
- Napi::String::New(callback_data->env, peer_id),
71
- Napi::String::New(callback_data->env, json_str)
72
- });
73
- }
74
- }
75
-
76
- void disconnect_callback_wrapper(void* user_data, const char* peer_id) {
77
- rats_client_t client = static_cast<rats_client_t>(user_data);
78
- auto it = disconnect_callbacks.find(client);
79
- if (it != disconnect_callbacks.end() && it->second) {
80
- auto callback_data = it->second;
81
- callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
82
- }
83
- }
84
-
85
- void file_progress_callback_wrapper(void* user_data, const char* transfer_id, int progress_percent, const char* status) {
86
- rats_client_t client = static_cast<rats_client_t>(user_data);
87
- auto it = file_progress_callbacks.find(client);
88
- if (it != file_progress_callbacks.end() && it->second) {
89
- auto callback_data = it->second;
90
- callback_data->callback.Call({
91
- Napi::String::New(callback_data->env, transfer_id),
92
- Napi::Number::New(callback_data->env, progress_percent),
93
- Napi::String::New(callback_data->env, status)
94
- });
95
- }
96
- }
97
-
98
- class RatsClient : public Napi::ObjectWrap<RatsClient> {
99
- public:
100
- static Napi::Object Init(Napi::Env env, Napi::Object exports) {
101
- Napi::Function func = DefineClass(env, "RatsClient", {
102
- InstanceMethod("start", &RatsClient::Start),
103
- InstanceMethod("stop", &RatsClient::Stop),
104
- InstanceMethod("connect", &RatsClient::Connect),
105
- InstanceMethod("connectWithStrategy", &RatsClient::ConnectWithStrategy),
106
- InstanceMethod("disconnect", &RatsClient::Disconnect),
107
- InstanceMethod("broadcastString", &RatsClient::BroadcastString),
108
- InstanceMethod("sendString", &RatsClient::SendString),
109
- InstanceMethod("broadcastBinary", &RatsClient::BroadcastBinary),
110
- InstanceMethod("sendBinary", &RatsClient::SendBinary),
111
- InstanceMethod("broadcastJson", &RatsClient::BroadcastJson),
112
- InstanceMethod("sendJson", &RatsClient::SendJson),
113
- InstanceMethod("getPeerCount", &RatsClient::GetPeerCount),
114
- InstanceMethod("getListenPort", &RatsClient::GetListenPort),
115
- InstanceMethod("getOurPeerId", &RatsClient::GetOurPeerId),
116
- InstanceMethod("getPeerIds", &RatsClient::GetPeerIds),
117
- InstanceMethod("getConnectionStatistics", &RatsClient::GetConnectionStatistics),
118
- InstanceMethod("setMaxPeers", &RatsClient::SetMaxPeers),
119
- InstanceMethod("getMaxPeers", &RatsClient::GetMaxPeers),
120
- InstanceMethod("isPeerLimitReached", &RatsClient::IsPeerLimitReached),
121
-
122
- // DHT methods
123
- InstanceMethod("startDhtDiscovery", &RatsClient::StartDhtDiscovery),
124
- InstanceMethod("stopDhtDiscovery", &RatsClient::StopDhtDiscovery),
125
- InstanceMethod("isDhtRunning", &RatsClient::IsDhtRunning),
126
- InstanceMethod("announceForHash", &RatsClient::AnnounceForHash),
127
- InstanceMethod("getDhtRoutingTableSize", &RatsClient::GetDhtRoutingTableSize),
128
-
129
- // mDNS methods
130
- InstanceMethod("startMdnsDiscovery", &RatsClient::StartMdnsDiscovery),
131
- InstanceMethod("stopMdnsDiscovery", &RatsClient::StopMdnsDiscovery),
132
- InstanceMethod("isMdnsRunning", &RatsClient::IsMdnsRunning),
133
- InstanceMethod("queryMdnsServices", &RatsClient::QueryMdnsServices),
134
-
135
- // Encryption methods
136
- InstanceMethod("setEncryptionEnabled", &RatsClient::SetEncryptionEnabled),
137
- InstanceMethod("isEncryptionEnabled", &RatsClient::IsEncryptionEnabled),
138
- InstanceMethod("getEncryptionKey", &RatsClient::GetEncryptionKey),
139
- InstanceMethod("setEncryptionKey", &RatsClient::SetEncryptionKey),
140
- InstanceMethod("generateEncryptionKey", &RatsClient::GenerateEncryptionKey),
141
-
142
- // GossipSub methods
143
- InstanceMethod("isGossipsubAvailable", &RatsClient::IsGossipsubAvailable),
144
- InstanceMethod("isGossipsubRunning", &RatsClient::IsGossipsubRunning),
145
- InstanceMethod("subscribeToTopic", &RatsClient::SubscribeToTopic),
146
- InstanceMethod("unsubscribeFromTopic", &RatsClient::UnsubscribeFromTopic),
147
- InstanceMethod("isSubscribedToTopic", &RatsClient::IsSubscribedToTopic),
148
- InstanceMethod("getSubscribedTopics", &RatsClient::GetSubscribedTopics),
149
- InstanceMethod("publishToTopic", &RatsClient::PublishToTopic),
150
- InstanceMethod("publishJsonToTopic", &RatsClient::PublishJsonToTopic),
151
- InstanceMethod("getTopicPeers", &RatsClient::GetTopicPeers),
152
- InstanceMethod("getGossipsubStatistics", &RatsClient::GetGossipsubStatistics),
153
-
154
- // File Transfer methods
155
- InstanceMethod("sendFile", &RatsClient::SendFile),
156
- InstanceMethod("sendDirectory", &RatsClient::SendDirectory),
157
- InstanceMethod("requestFile", &RatsClient::RequestFile),
158
- InstanceMethod("requestDirectory", &RatsClient::RequestDirectory),
159
- InstanceMethod("acceptFileTransfer", &RatsClient::AcceptFileTransfer),
160
- InstanceMethod("rejectFileTransfer", &RatsClient::RejectFileTransfer),
161
- InstanceMethod("cancelFileTransfer", &RatsClient::CancelFileTransfer),
162
- InstanceMethod("pauseFileTransfer", &RatsClient::PauseFileTransfer),
163
- InstanceMethod("resumeFileTransfer", &RatsClient::ResumeFileTransfer),
164
- InstanceMethod("getFileTransferProgress", &RatsClient::GetFileTransferProgress),
165
- InstanceMethod("getFileTransferStatistics", &RatsClient::GetFileTransferStatistics),
166
-
167
- // NAT Traversal methods
168
- InstanceMethod("discoverPublicIp", &RatsClient::DiscoverPublicIp),
169
- InstanceMethod("getPublicIp", &RatsClient::GetPublicIp),
170
- InstanceMethod("detectNatType", &RatsClient::DetectNatType),
171
- InstanceMethod("getNatCharacteristics", &RatsClient::GetNatCharacteristics),
172
- InstanceMethod("addIgnoredAddress", &RatsClient::AddIgnoredAddress),
173
-
174
- // Callback methods
175
- InstanceMethod("onConnection", &RatsClient::OnConnection),
176
- InstanceMethod("onString", &RatsClient::OnString),
177
- InstanceMethod("onBinary", &RatsClient::OnBinary),
178
- InstanceMethod("onJson", &RatsClient::OnJson),
179
- InstanceMethod("onDisconnect", &RatsClient::OnDisconnect),
180
- InstanceMethod("onFileProgress", &RatsClient::OnFileProgress),
181
-
182
- // Configuration persistence
183
- InstanceMethod("loadConfiguration", &RatsClient::LoadConfiguration),
184
- InstanceMethod("saveConfiguration", &RatsClient::SaveConfiguration),
185
- InstanceMethod("setDataDirectory", &RatsClient::SetDataDirectory),
186
- InstanceMethod("getDataDirectory", &RatsClient::GetDataDirectory)
187
- });
188
-
189
- exports.Set("RatsClient", func);
190
- return exports;
191
- }
192
-
193
- RatsClient(const Napi::CallbackInfo& info) : Napi::ObjectWrap<RatsClient>(info) {
194
- Napi::Env env = info.Env();
195
-
196
- if (info.Length() < 1 || !info[0].IsNumber()) {
197
- Napi::TypeError::New(env, "Expected port number").ThrowAsJavaScriptException();
198
- return;
199
- }
200
-
201
- int port = info[0].As<Napi::Number>().Int32Value();
202
- client_ = rats_create(port);
203
-
204
- if (!client_) {
205
- Napi::Error::New(env, "Failed to create RatsClient").ThrowAsJavaScriptException();
206
- return;
207
- }
208
- }
209
-
210
- ~RatsClient() {
211
- if (client_) {
212
- // Clean up callbacks
213
- connection_callbacks.erase(client_);
214
- string_callbacks.erase(client_);
215
- binary_callbacks.erase(client_);
216
- json_callbacks.erase(client_);
217
- disconnect_callbacks.erase(client_);
218
- file_progress_callbacks.erase(client_);
219
-
220
- rats_destroy(client_);
221
- }
222
- }
223
-
224
- private:
225
- rats_client_t client_;
226
-
227
- // Basic operations
228
- Napi::Value Start(const Napi::CallbackInfo& info) {
229
- Napi::Env env = info.Env();
230
- int result = rats_start(client_);
231
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
232
- }
233
-
234
- void Stop(const Napi::CallbackInfo& info) {
235
- rats_stop(client_);
236
- }
237
-
238
- Napi::Value Connect(const Napi::CallbackInfo& info) {
239
- Napi::Env env = info.Env();
240
-
241
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
242
- Napi::TypeError::New(env, "Expected host (string) and port (number)").ThrowAsJavaScriptException();
243
- return env.Null();
244
- }
245
-
246
- std::string host = info[0].As<Napi::String>().Utf8Value();
247
- int port = info[1].As<Napi::Number>().Int32Value();
248
-
249
- int result = rats_connect(client_, host.c_str(), port);
250
- return Napi::Boolean::New(env, result == 1);
251
- }
252
-
253
- Napi::Value ConnectWithStrategy(const Napi::CallbackInfo& info) {
254
- Napi::Env env = info.Env();
255
-
256
- if (info.Length() < 3 || !info[0].IsString() || !info[1].IsNumber() || !info[2].IsNumber()) {
257
- Napi::TypeError::New(env, "Expected host (string), port (number), and strategy (number)").ThrowAsJavaScriptException();
258
- return env.Null();
259
- }
260
-
261
- std::string host = info[0].As<Napi::String>().Utf8Value();
262
- int port = info[1].As<Napi::Number>().Int32Value();
263
- rats_connection_strategy_t strategy = static_cast<rats_connection_strategy_t>(info[2].As<Napi::Number>().Int32Value());
264
-
265
- rats_error_t result = rats_connect_with_strategy(client_, host.c_str(), port, strategy);
266
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
267
- }
268
-
269
- void Disconnect(const Napi::CallbackInfo& info) {
270
- if (info.Length() < 1 || !info[0].IsString()) {
271
- Napi::TypeError::New(info.Env(), "Expected peer_id (string)").ThrowAsJavaScriptException();
272
- return;
273
- }
274
-
275
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
276
- rats_disconnect_peer_by_id(client_, peer_id.c_str());
277
- }
278
-
279
- Napi::Value BroadcastString(const Napi::CallbackInfo& info) {
280
- Napi::Env env = info.Env();
281
-
282
- if (info.Length() < 1 || !info[0].IsString()) {
283
- Napi::TypeError::New(env, "Expected message (string)").ThrowAsJavaScriptException();
284
- return env.Null();
285
- }
286
-
287
- std::string message = info[0].As<Napi::String>().Utf8Value();
288
- int result = rats_broadcast_string(client_, message.c_str());
289
- return Napi::Number::New(env, result);
290
- }
291
-
292
- Napi::Value SendString(const Napi::CallbackInfo& info) {
293
- Napi::Env env = info.Env();
294
-
295
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
296
- Napi::TypeError::New(env, "Expected peer_id (string) and message (string)").ThrowAsJavaScriptException();
297
- return env.Null();
298
- }
299
-
300
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
301
- std::string message = info[1].As<Napi::String>().Utf8Value();
302
-
303
- int result = rats_send_string(client_, peer_id.c_str(), message.c_str());
304
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
305
- }
306
-
307
- Napi::Value BroadcastBinary(const Napi::CallbackInfo& info) {
308
- Napi::Env env = info.Env();
309
-
310
- if (info.Length() < 1 || !info[0].IsBuffer()) {
311
- Napi::TypeError::New(env, "Expected data (Buffer)").ThrowAsJavaScriptException();
312
- return env.Null();
313
- }
314
-
315
- Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
316
- int result = rats_broadcast_binary(client_, buffer.Data(), buffer.Length());
317
- return Napi::Number::New(env, result);
318
- }
319
-
320
- Napi::Value SendBinary(const Napi::CallbackInfo& info) {
321
- Napi::Env env = info.Env();
322
-
323
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsBuffer()) {
324
- Napi::TypeError::New(env, "Expected peer_id (string) and data (Buffer)").ThrowAsJavaScriptException();
325
- return env.Null();
326
- }
327
-
328
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
329
- Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
330
-
331
- rats_error_t result = rats_send_binary(client_, peer_id.c_str(), buffer.Data(), buffer.Length());
332
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
333
- }
334
-
335
- Napi::Value BroadcastJson(const Napi::CallbackInfo& info) {
336
- Napi::Env env = info.Env();
337
-
338
- if (info.Length() < 1 || !info[0].IsString()) {
339
- Napi::TypeError::New(env, "Expected json_str (string)").ThrowAsJavaScriptException();
340
- return env.Null();
341
- }
342
-
343
- std::string json_str = info[0].As<Napi::String>().Utf8Value();
344
- int result = rats_broadcast_json(client_, json_str.c_str());
345
- return Napi::Number::New(env, result);
346
- }
347
-
348
- Napi::Value SendJson(const Napi::CallbackInfo& info) {
349
- Napi::Env env = info.Env();
350
-
351
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
352
- Napi::TypeError::New(env, "Expected peer_id (string) and json_str (string)").ThrowAsJavaScriptException();
353
- return env.Null();
354
- }
355
-
356
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
357
- std::string json_str = info[1].As<Napi::String>().Utf8Value();
358
-
359
- rats_error_t result = rats_send_json(client_, peer_id.c_str(), json_str.c_str());
360
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
361
- }
362
-
363
- // Info methods
364
- Napi::Value GetPeerCount(const Napi::CallbackInfo& info) {
365
- Napi::Env env = info.Env();
366
- int count = rats_get_peer_count(client_);
367
- return Napi::Number::New(env, count);
368
- }
369
-
370
- Napi::Value GetListenPort(const Napi::CallbackInfo& info) {
371
- Napi::Env env = info.Env();
372
- int port = rats_get_listen_port(client_);
373
- return Napi::Number::New(env, port);
374
- }
375
-
376
- Napi::Value GetOurPeerId(const Napi::CallbackInfo& info) {
377
- Napi::Env env = info.Env();
378
- char* peer_id = rats_get_our_peer_id(client_);
379
- if (!peer_id) return env.Null();
380
-
381
- Napi::String result = Napi::String::New(env, peer_id);
382
- rats_string_free(peer_id);
383
- return result;
384
- }
385
-
386
- Napi::Value GetPeerIds(const Napi::CallbackInfo& info) {
387
- Napi::Env env = info.Env();
388
- int count = 0;
389
- char** peer_ids = rats_get_peer_ids(client_, &count);
390
-
391
- if (!peer_ids || count == 0) {
392
- return Napi::Array::New(env, 0);
393
- }
394
-
395
- Napi::Array result = Napi::Array::New(env, count);
396
- for (int i = 0; i < count; i++) {
397
- result[i] = Napi::String::New(env, peer_ids[i]);
398
- rats_string_free(peer_ids[i]);
399
- }
400
- free(peer_ids);
401
-
402
- return result;
403
- }
404
-
405
- Napi::Value GetConnectionStatistics(const Napi::CallbackInfo& info) {
406
- Napi::Env env = info.Env();
407
- char* stats_json = rats_get_connection_statistics_json(client_);
408
- if (!stats_json) return env.Null();
409
-
410
- Napi::String result = Napi::String::New(env, stats_json);
411
- rats_string_free(stats_json);
412
- return result;
413
- }
414
-
415
- // Peer configuration
416
- Napi::Value SetMaxPeers(const Napi::CallbackInfo& info) {
417
- Napi::Env env = info.Env();
418
-
419
- if (info.Length() < 1 || !info[0].IsNumber()) {
420
- Napi::TypeError::New(env, "Expected max_peers (number)").ThrowAsJavaScriptException();
421
- return env.Null();
422
- }
423
-
424
- int max_peers = info[0].As<Napi::Number>().Int32Value();
425
- rats_error_t result = rats_set_max_peers(client_, max_peers);
426
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
427
- }
428
-
429
- Napi::Value GetMaxPeers(const Napi::CallbackInfo& info) {
430
- Napi::Env env = info.Env();
431
- int max_peers = rats_get_max_peers(client_);
432
- return Napi::Number::New(env, max_peers);
433
- }
434
-
435
- Napi::Value IsPeerLimitReached(const Napi::CallbackInfo& info) {
436
- Napi::Env env = info.Env();
437
- int reached = rats_is_peer_limit_reached(client_);
438
- return Napi::Boolean::New(env, reached != 0);
439
- }
440
-
441
- // DHT methods
442
- Napi::Value StartDhtDiscovery(const Napi::CallbackInfo& info) {
443
- Napi::Env env = info.Env();
444
-
445
- if (info.Length() < 1 || !info[0].IsNumber()) {
446
- Napi::TypeError::New(env, "Expected dht_port (number)").ThrowAsJavaScriptException();
447
- return env.Null();
448
- }
449
-
450
- int dht_port = info[0].As<Napi::Number>().Int32Value();
451
- rats_error_t result = rats_start_dht_discovery(client_, dht_port);
452
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
453
- }
454
-
455
- void StopDhtDiscovery(const Napi::CallbackInfo& info) {
456
- rats_stop_dht_discovery(client_);
457
- }
458
-
459
- Napi::Value IsDhtRunning(const Napi::CallbackInfo& info) {
460
- Napi::Env env = info.Env();
461
- int running = rats_is_dht_running(client_);
462
- return Napi::Boolean::New(env, running != 0);
463
- }
464
-
465
- Napi::Value AnnounceForHash(const Napi::CallbackInfo& info) {
466
- Napi::Env env = info.Env();
467
-
468
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
469
- Napi::TypeError::New(env, "Expected content_hash (string) and port (number)").ThrowAsJavaScriptException();
470
- return env.Null();
471
- }
472
-
473
- std::string content_hash = info[0].As<Napi::String>().Utf8Value();
474
- int port = info[1].As<Napi::Number>().Int32Value();
475
-
476
- rats_error_t result = rats_announce_for_hash(client_, content_hash.c_str(), port);
477
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
478
- }
479
-
480
- Napi::Value GetDhtRoutingTableSize(const Napi::CallbackInfo& info) {
481
- Napi::Env env = info.Env();
482
- size_t size = rats_get_dht_routing_table_size(client_);
483
- return Napi::Number::New(env, static_cast<double>(size));
484
- }
485
-
486
- // mDNS methods
487
- Napi::Value StartMdnsDiscovery(const Napi::CallbackInfo& info) {
488
- Napi::Env env = info.Env();
489
-
490
- const char* service_name = nullptr;
491
- if (info.Length() > 0 && info[0].IsString()) {
492
- std::string service = info[0].As<Napi::String>().Utf8Value();
493
- service_name = service.c_str();
494
- }
495
-
496
- rats_error_t result = rats_start_mdns_discovery(client_, service_name);
497
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
498
- }
499
-
500
- void StopMdnsDiscovery(const Napi::CallbackInfo& info) {
501
- rats_stop_mdns_discovery(client_);
502
- }
503
-
504
- Napi::Value IsMdnsRunning(const Napi::CallbackInfo& info) {
505
- Napi::Env env = info.Env();
506
- int running = rats_is_mdns_running(client_);
507
- return Napi::Boolean::New(env, running != 0);
508
- }
509
-
510
- Napi::Value QueryMdnsServices(const Napi::CallbackInfo& info) {
511
- Napi::Env env = info.Env();
512
- rats_error_t result = rats_query_mdns_services(client_);
513
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
514
- }
515
-
516
- // Encryption methods
517
- Napi::Value SetEncryptionEnabled(const Napi::CallbackInfo& info) {
518
- Napi::Env env = info.Env();
519
-
520
- if (info.Length() < 1 || !info[0].IsBoolean()) {
521
- Napi::TypeError::New(env, "Expected enabled (boolean)").ThrowAsJavaScriptException();
522
- return env.Null();
523
- }
524
-
525
- bool enabled = info[0].As<Napi::Boolean>().Value();
526
- rats_error_t result = rats_set_encryption_enabled(client_, enabled ? 1 : 0);
527
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
528
- }
529
-
530
- Napi::Value IsEncryptionEnabled(const Napi::CallbackInfo& info) {
531
- Napi::Env env = info.Env();
532
- int enabled = rats_is_encryption_enabled(client_);
533
- return Napi::Boolean::New(env, enabled != 0);
534
- }
535
-
536
- Napi::Value GetEncryptionKey(const Napi::CallbackInfo& info) {
537
- Napi::Env env = info.Env();
538
- char* key = rats_get_encryption_key(client_);
539
- if (!key) return env.Null();
540
-
541
- Napi::String result = Napi::String::New(env, key);
542
- rats_string_free(key);
543
- return result;
544
- }
545
-
546
- Napi::Value SetEncryptionKey(const Napi::CallbackInfo& info) {
547
- Napi::Env env = info.Env();
548
-
549
- if (info.Length() < 1 || !info[0].IsString()) {
550
- Napi::TypeError::New(env, "Expected key_hex (string)").ThrowAsJavaScriptException();
551
- return env.Null();
552
- }
553
-
554
- std::string key_hex = info[0].As<Napi::String>().Utf8Value();
555
- rats_error_t result = rats_set_encryption_key(client_, key_hex.c_str());
556
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
557
- }
558
-
559
- Napi::Value GenerateEncryptionKey(const Napi::CallbackInfo& info) {
560
- Napi::Env env = info.Env();
561
- char* key = rats_generate_encryption_key(client_);
562
- if (!key) return env.Null();
563
-
564
- Napi::String result = Napi::String::New(env, key);
565
- rats_string_free(key);
566
- return result;
567
- }
568
-
569
- // GossipSub methods
570
- Napi::Value IsGossipsubAvailable(const Napi::CallbackInfo& info) {
571
- Napi::Env env = info.Env();
572
- int available = rats_is_gossipsub_available(client_);
573
- return Napi::Boolean::New(env, available != 0);
574
- }
575
-
576
- Napi::Value IsGossipsubRunning(const Napi::CallbackInfo& info) {
577
- Napi::Env env = info.Env();
578
- int running = rats_is_gossipsub_running(client_);
579
- return Napi::Boolean::New(env, running != 0);
580
- }
581
-
582
- Napi::Value SubscribeToTopic(const Napi::CallbackInfo& info) {
583
- Napi::Env env = info.Env();
584
-
585
- if (info.Length() < 1 || !info[0].IsString()) {
586
- Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
587
- return env.Null();
588
- }
589
-
590
- std::string topic = info[0].As<Napi::String>().Utf8Value();
591
- rats_error_t result = rats_subscribe_to_topic(client_, topic.c_str());
592
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
593
- }
594
-
595
- Napi::Value UnsubscribeFromTopic(const Napi::CallbackInfo& info) {
596
- Napi::Env env = info.Env();
597
-
598
- if (info.Length() < 1 || !info[0].IsString()) {
599
- Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
600
- return env.Null();
601
- }
602
-
603
- std::string topic = info[0].As<Napi::String>().Utf8Value();
604
- rats_error_t result = rats_unsubscribe_from_topic(client_, topic.c_str());
605
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
606
- }
607
-
608
- Napi::Value IsSubscribedToTopic(const Napi::CallbackInfo& info) {
609
- Napi::Env env = info.Env();
610
-
611
- if (info.Length() < 1 || !info[0].IsString()) {
612
- Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
613
- return env.Null();
614
- }
615
-
616
- std::string topic = info[0].As<Napi::String>().Utf8Value();
617
- int subscribed = rats_is_subscribed_to_topic(client_, topic.c_str());
618
- return Napi::Boolean::New(env, subscribed != 0);
619
- }
620
-
621
- Napi::Value GetSubscribedTopics(const Napi::CallbackInfo& info) {
622
- Napi::Env env = info.Env();
623
- int count = 0;
624
- char** topics = rats_get_subscribed_topics(client_, &count);
625
-
626
- if (!topics || count == 0) {
627
- return Napi::Array::New(env, 0);
628
- }
629
-
630
- Napi::Array result = Napi::Array::New(env, count);
631
- for (int i = 0; i < count; i++) {
632
- result[i] = Napi::String::New(env, topics[i]);
633
- rats_string_free(topics[i]);
634
- }
635
- free(topics);
636
-
637
- return result;
638
- }
639
-
640
- Napi::Value PublishToTopic(const Napi::CallbackInfo& info) {
641
- Napi::Env env = info.Env();
642
-
643
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
644
- Napi::TypeError::New(env, "Expected topic (string) and message (string)").ThrowAsJavaScriptException();
645
- return env.Null();
646
- }
647
-
648
- std::string topic = info[0].As<Napi::String>().Utf8Value();
649
- std::string message = info[1].As<Napi::String>().Utf8Value();
650
-
651
- rats_error_t result = rats_publish_to_topic(client_, topic.c_str(), message.c_str());
652
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
653
- }
654
-
655
- Napi::Value PublishJsonToTopic(const Napi::CallbackInfo& info) {
656
- Napi::Env env = info.Env();
657
-
658
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
659
- Napi::TypeError::New(env, "Expected topic (string) and json_str (string)").ThrowAsJavaScriptException();
660
- return env.Null();
661
- }
662
-
663
- std::string topic = info[0].As<Napi::String>().Utf8Value();
664
- std::string json_str = info[1].As<Napi::String>().Utf8Value();
665
-
666
- rats_error_t result = rats_publish_json_to_topic(client_, topic.c_str(), json_str.c_str());
667
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
668
- }
669
-
670
- Napi::Value GetTopicPeers(const Napi::CallbackInfo& info) {
671
- Napi::Env env = info.Env();
672
-
673
- if (info.Length() < 1 || !info[0].IsString()) {
674
- Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
675
- return env.Null();
676
- }
677
-
678
- std::string topic = info[0].As<Napi::String>().Utf8Value();
679
- int count = 0;
680
- char** peers = rats_get_topic_peers(client_, topic.c_str(), &count);
681
-
682
- if (!peers || count == 0) {
683
- return Napi::Array::New(env, 0);
684
- }
685
-
686
- Napi::Array result = Napi::Array::New(env, count);
687
- for (int i = 0; i < count; i++) {
688
- result[i] = Napi::String::New(env, peers[i]);
689
- rats_string_free(peers[i]);
690
- }
691
- free(peers);
692
-
693
- return result;
694
- }
695
-
696
- Napi::Value GetGossipsubStatistics(const Napi::CallbackInfo& info) {
697
- Napi::Env env = info.Env();
698
- char* stats_json = rats_get_gossipsub_statistics_json(client_);
699
- if (!stats_json) return env.Null();
700
-
701
- Napi::String result = Napi::String::New(env, stats_json);
702
- rats_string_free(stats_json);
703
- return result;
704
- }
705
-
706
- // File Transfer methods
707
- Napi::Value SendFile(const Napi::CallbackInfo& info) {
708
- Napi::Env env = info.Env();
709
-
710
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
711
- Napi::TypeError::New(env, "Expected peer_id (string) and file_path (string)").ThrowAsJavaScriptException();
712
- return env.Null();
713
- }
714
-
715
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
716
- std::string file_path = info[1].As<Napi::String>().Utf8Value();
717
-
718
- const char* remote_filename = nullptr;
719
- if (info.Length() > 2 && info[2].IsString()) {
720
- std::string remote_name = info[2].As<Napi::String>().Utf8Value();
721
- remote_filename = remote_name.c_str();
722
- }
723
-
724
- char* transfer_id = rats_send_file(client_, peer_id.c_str(), file_path.c_str(), remote_filename);
725
- if (!transfer_id) return env.Null();
726
-
727
- Napi::String result = Napi::String::New(env, transfer_id);
728
- rats_string_free(transfer_id);
729
- return result;
730
- }
731
-
732
- Napi::Value SendDirectory(const Napi::CallbackInfo& info) {
733
- Napi::Env env = info.Env();
734
-
735
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
736
- Napi::TypeError::New(env, "Expected peer_id (string) and directory_path (string)").ThrowAsJavaScriptException();
737
- return env.Null();
738
- }
739
-
740
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
741
- std::string directory_path = info[1].As<Napi::String>().Utf8Value();
742
-
743
- const char* remote_directory_name = nullptr;
744
- if (info.Length() > 2 && info[2].IsString()) {
745
- std::string remote_name = info[2].As<Napi::String>().Utf8Value();
746
- remote_directory_name = remote_name.c_str();
747
- }
748
-
749
- int recursive = 1;
750
- if (info.Length() > 3 && info[3].IsBoolean()) {
751
- recursive = info[3].As<Napi::Boolean>().Value() ? 1 : 0;
752
- }
753
-
754
- char* transfer_id = rats_send_directory(client_, peer_id.c_str(), directory_path.c_str(), remote_directory_name, recursive);
755
- if (!transfer_id) return env.Null();
756
-
757
- Napi::String result = Napi::String::New(env, transfer_id);
758
- rats_string_free(transfer_id);
759
- return result;
760
- }
761
-
762
- Napi::Value RequestFile(const Napi::CallbackInfo& info) {
763
- Napi::Env env = info.Env();
764
-
765
- if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString()) {
766
- Napi::TypeError::New(env, "Expected peer_id (string), remote_file_path (string), and local_path (string)").ThrowAsJavaScriptException();
767
- return env.Null();
768
- }
769
-
770
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
771
- std::string remote_file_path = info[1].As<Napi::String>().Utf8Value();
772
- std::string local_path = info[2].As<Napi::String>().Utf8Value();
773
-
774
- char* transfer_id = rats_request_file(client_, peer_id.c_str(), remote_file_path.c_str(), local_path.c_str());
775
- if (!transfer_id) return env.Null();
776
-
777
- Napi::String result = Napi::String::New(env, transfer_id);
778
- rats_string_free(transfer_id);
779
- return result;
780
- }
781
-
782
- Napi::Value RequestDirectory(const Napi::CallbackInfo& info) {
783
- Napi::Env env = info.Env();
784
-
785
- if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString()) {
786
- Napi::TypeError::New(env, "Expected peer_id (string), remote_directory_path (string), and local_directory_path (string)").ThrowAsJavaScriptException();
787
- return env.Null();
788
- }
789
-
790
- std::string peer_id = info[0].As<Napi::String>().Utf8Value();
791
- std::string remote_directory_path = info[1].As<Napi::String>().Utf8Value();
792
- std::string local_directory_path = info[2].As<Napi::String>().Utf8Value();
793
-
794
- int recursive = 1;
795
- if (info.Length() > 3 && info[3].IsBoolean()) {
796
- recursive = info[3].As<Napi::Boolean>().Value() ? 1 : 0;
797
- }
798
-
799
- char* transfer_id = rats_request_directory(client_, peer_id.c_str(), remote_directory_path.c_str(), local_directory_path.c_str(), recursive);
800
- if (!transfer_id) return env.Null();
801
-
802
- Napi::String result = Napi::String::New(env, transfer_id);
803
- rats_string_free(transfer_id);
804
- return result;
805
- }
806
-
807
- Napi::Value AcceptFileTransfer(const Napi::CallbackInfo& info) {
808
- Napi::Env env = info.Env();
809
-
810
- if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
811
- Napi::TypeError::New(env, "Expected transfer_id (string) and local_path (string)").ThrowAsJavaScriptException();
812
- return env.Null();
813
- }
814
-
815
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
816
- std::string local_path = info[1].As<Napi::String>().Utf8Value();
817
-
818
- rats_error_t result = rats_accept_file_transfer(client_, transfer_id.c_str(), local_path.c_str());
819
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
820
- }
821
-
822
- Napi::Value RejectFileTransfer(const Napi::CallbackInfo& info) {
823
- Napi::Env env = info.Env();
824
-
825
- if (info.Length() < 1 || !info[0].IsString()) {
826
- Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
827
- return env.Null();
828
- }
829
-
830
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
831
-
832
- const char* reason = nullptr;
833
- if (info.Length() > 1 && info[1].IsString()) {
834
- std::string reason_str = info[1].As<Napi::String>().Utf8Value();
835
- reason = reason_str.c_str();
836
- }
837
-
838
- rats_error_t result = rats_reject_file_transfer(client_, transfer_id.c_str(), reason);
839
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
840
- }
841
-
842
- Napi::Value CancelFileTransfer(const Napi::CallbackInfo& info) {
843
- Napi::Env env = info.Env();
844
-
845
- if (info.Length() < 1 || !info[0].IsString()) {
846
- Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
847
- return env.Null();
848
- }
849
-
850
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
851
-
852
- rats_error_t result = rats_cancel_file_transfer(client_, transfer_id.c_str());
853
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
854
- }
855
-
856
- Napi::Value PauseFileTransfer(const Napi::CallbackInfo& info) {
857
- Napi::Env env = info.Env();
858
-
859
- if (info.Length() < 1 || !info[0].IsString()) {
860
- Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
861
- return env.Null();
862
- }
863
-
864
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
865
-
866
- rats_error_t result = rats_pause_file_transfer(client_, transfer_id.c_str());
867
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
868
- }
869
-
870
- Napi::Value ResumeFileTransfer(const Napi::CallbackInfo& info) {
871
- Napi::Env env = info.Env();
872
-
873
- if (info.Length() < 1 || !info[0].IsString()) {
874
- Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
875
- return env.Null();
876
- }
877
-
878
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
879
-
880
- rats_error_t result = rats_resume_file_transfer(client_, transfer_id.c_str());
881
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
882
- }
883
-
884
- Napi::Value GetFileTransferProgress(const Napi::CallbackInfo& info) {
885
- Napi::Env env = info.Env();
886
-
887
- if (info.Length() < 1 || !info[0].IsString()) {
888
- Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
889
- return env.Null();
890
- }
891
-
892
- std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
893
-
894
- char* progress_json = rats_get_file_transfer_progress_json(client_, transfer_id.c_str());
895
- if (!progress_json) return env.Null();
896
-
897
- Napi::String result = Napi::String::New(env, progress_json);
898
- rats_string_free(progress_json);
899
- return result;
900
- }
901
-
902
- Napi::Value GetFileTransferStatistics(const Napi::CallbackInfo& info) {
903
- Napi::Env env = info.Env();
904
- char* stats_json = rats_get_file_transfer_statistics_json(client_);
905
- if (!stats_json) return env.Null();
906
-
907
- Napi::String result = Napi::String::New(env, stats_json);
908
- rats_string_free(stats_json);
909
- return result;
910
- }
911
-
912
- // NAT Traversal methods
913
- Napi::Value DiscoverPublicIp(const Napi::CallbackInfo& info) {
914
- Napi::Env env = info.Env();
915
-
916
- const char* stun_server = "stun.l.google.com";
917
- int stun_port = 19302;
918
-
919
- if (info.Length() > 0 && info[0].IsString()) {
920
- std::string server = info[0].As<Napi::String>().Utf8Value();
921
- stun_server = server.c_str();
922
- }
923
-
924
- if (info.Length() > 1 && info[1].IsNumber()) {
925
- stun_port = info[1].As<Napi::Number>().Int32Value();
926
- }
927
-
928
- rats_error_t result = rats_discover_and_ignore_public_ip(client_, stun_server, stun_port);
929
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
930
- }
931
-
932
- Napi::Value GetPublicIp(const Napi::CallbackInfo& info) {
933
- Napi::Env env = info.Env();
934
- char* public_ip = rats_get_public_ip(client_);
935
- if (!public_ip) return env.Null();
936
-
937
- Napi::String result = Napi::String::New(env, public_ip);
938
- rats_string_free(public_ip);
939
- return result;
940
- }
941
-
942
- Napi::Value DetectNatType(const Napi::CallbackInfo& info) {
943
- Napi::Env env = info.Env();
944
- int nat_type = rats_detect_nat_type(client_);
945
- return Napi::Number::New(env, nat_type);
946
- }
947
-
948
- Napi::Value GetNatCharacteristics(const Napi::CallbackInfo& info) {
949
- Napi::Env env = info.Env();
950
- char* characteristics_json = rats_get_nat_characteristics_json(client_);
951
- if (!characteristics_json) return env.Null();
952
-
953
- Napi::String result = Napi::String::New(env, characteristics_json);
954
- rats_string_free(characteristics_json);
955
- return result;
956
- }
957
-
958
- void AddIgnoredAddress(const Napi::CallbackInfo& info) {
959
- if (info.Length() < 1 || !info[0].IsString()) {
960
- Napi::TypeError::New(info.Env(), "Expected ip_address (string)").ThrowAsJavaScriptException();
961
- return;
962
- }
963
-
964
- std::string ip_address = info[0].As<Napi::String>().Utf8Value();
965
- rats_add_ignored_address(client_, ip_address.c_str());
966
- }
967
-
968
- // Callback methods
969
- void OnConnection(const Napi::CallbackInfo& info) {
970
- Napi::Env env = info.Env();
971
-
972
- if (info.Length() < 1 || !info[0].IsFunction()) {
973
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
974
- return;
975
- }
976
-
977
- auto callback_data = std::make_shared<CallbackData>(env);
978
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
979
-
980
- connection_callbacks[client_] = callback_data;
981
- rats_set_connection_callback(client_, connection_callback_wrapper, client_);
982
- }
983
-
984
- void OnString(const Napi::CallbackInfo& info) {
985
- Napi::Env env = info.Env();
986
-
987
- if (info.Length() < 1 || !info[0].IsFunction()) {
988
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
989
- return;
990
- }
991
-
992
- auto callback_data = std::make_shared<CallbackData>(env);
993
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
994
-
995
- string_callbacks[client_] = callback_data;
996
- rats_set_string_callback(client_, string_callback_wrapper, client_);
997
- }
998
-
999
- void OnBinary(const Napi::CallbackInfo& info) {
1000
- Napi::Env env = info.Env();
1001
-
1002
- if (info.Length() < 1 || !info[0].IsFunction()) {
1003
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1004
- return;
1005
- }
1006
-
1007
- auto callback_data = std::make_shared<CallbackData>(env);
1008
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1009
-
1010
- binary_callbacks[client_] = callback_data;
1011
- rats_set_binary_callback(client_, binary_callback_wrapper, client_);
1012
- }
1013
-
1014
- void OnJson(const Napi::CallbackInfo& info) {
1015
- Napi::Env env = info.Env();
1016
-
1017
- if (info.Length() < 1 || !info[0].IsFunction()) {
1018
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1019
- return;
1020
- }
1021
-
1022
- auto callback_data = std::make_shared<CallbackData>(env);
1023
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1024
-
1025
- json_callbacks[client_] = callback_data;
1026
- rats_set_json_callback(client_, json_callback_wrapper, client_);
1027
- }
1028
-
1029
- void OnDisconnect(const Napi::CallbackInfo& info) {
1030
- Napi::Env env = info.Env();
1031
-
1032
- if (info.Length() < 1 || !info[0].IsFunction()) {
1033
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1034
- return;
1035
- }
1036
-
1037
- auto callback_data = std::make_shared<CallbackData>(env);
1038
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1039
-
1040
- disconnect_callbacks[client_] = callback_data;
1041
- rats_set_disconnect_callback(client_, disconnect_callback_wrapper, client_);
1042
- }
1043
-
1044
- void OnFileProgress(const Napi::CallbackInfo& info) {
1045
- Napi::Env env = info.Env();
1046
-
1047
- if (info.Length() < 1 || !info[0].IsFunction()) {
1048
- Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1049
- return;
1050
- }
1051
-
1052
- auto callback_data = std::make_shared<CallbackData>(env);
1053
- callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1054
-
1055
- file_progress_callbacks[client_] = callback_data;
1056
- rats_set_file_progress_callback(client_, file_progress_callback_wrapper, client_);
1057
- }
1058
-
1059
- // Configuration persistence
1060
- Napi::Value LoadConfiguration(const Napi::CallbackInfo& info) {
1061
- Napi::Env env = info.Env();
1062
- rats_error_t result = rats_load_configuration(client_);
1063
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
1064
- }
1065
-
1066
- Napi::Value SaveConfiguration(const Napi::CallbackInfo& info) {
1067
- Napi::Env env = info.Env();
1068
- rats_error_t result = rats_save_configuration(client_);
1069
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
1070
- }
1071
-
1072
- Napi::Value SetDataDirectory(const Napi::CallbackInfo& info) {
1073
- Napi::Env env = info.Env();
1074
-
1075
- if (info.Length() < 1 || !info[0].IsString()) {
1076
- Napi::TypeError::New(env, "Expected directory_path (string)").ThrowAsJavaScriptException();
1077
- return env.Null();
1078
- }
1079
-
1080
- std::string directory_path = info[0].As<Napi::String>().Utf8Value();
1081
- rats_error_t result = rats_set_data_directory(client_, directory_path.c_str());
1082
- return Napi::Boolean::New(env, result == RATS_SUCCESS);
1083
- }
1084
-
1085
- Napi::Value GetDataDirectory(const Napi::CallbackInfo& info) {
1086
- Napi::Env env = info.Env();
1087
- char* directory = rats_get_data_directory(client_);
1088
- if (!directory) return env.Null();
1089
-
1090
- Napi::String result = Napi::String::New(env, directory);
1091
- rats_string_free(directory);
1092
- return result;
1093
- }
1094
- };
1095
-
1096
- // Library utility functions
1097
- Napi::Value GetVersionString(const Napi::CallbackInfo& info) {
1098
- Napi::Env env = info.Env();
1099
- const char* version = rats_get_version_string();
1100
- return Napi::String::New(env, version);
1101
- }
1102
-
1103
- Napi::Value GetVersion(const Napi::CallbackInfo& info) {
1104
- Napi::Env env = info.Env();
1105
- int major, minor, patch, build;
1106
- rats_get_version(&major, &minor, &patch, &build);
1107
-
1108
- Napi::Object version = Napi::Object::New(env);
1109
- version.Set("major", Napi::Number::New(env, major));
1110
- version.Set("minor", Napi::Number::New(env, minor));
1111
- version.Set("patch", Napi::Number::New(env, patch));
1112
- version.Set("build", Napi::Number::New(env, build));
1113
-
1114
- return version;
1115
- }
1116
-
1117
- Napi::Value GetGitDescribe(const Napi::CallbackInfo& info) {
1118
- Napi::Env env = info.Env();
1119
- const char* git_describe = rats_get_git_describe();
1120
- return Napi::String::New(env, git_describe);
1121
- }
1122
-
1123
- Napi::Value GetAbi(const Napi::CallbackInfo& info) {
1124
- Napi::Env env = info.Env();
1125
- uint32_t abi = rats_get_abi();
1126
- return Napi::Number::New(env, abi);
1127
- }
1128
-
1129
- // Constants
1130
- Napi::Object InitConstants(Napi::Env env) {
1131
- Napi::Object constants = Napi::Object::New(env);
1132
-
1133
- // Error codes
1134
- Napi::Object errors = Napi::Object::New(env);
1135
- errors.Set("SUCCESS", Napi::Number::New(env, RATS_SUCCESS));
1136
- errors.Set("INVALID_HANDLE", Napi::Number::New(env, RATS_ERROR_INVALID_HANDLE));
1137
- errors.Set("INVALID_PARAMETER", Napi::Number::New(env, RATS_ERROR_INVALID_PARAMETER));
1138
- errors.Set("NOT_RUNNING", Napi::Number::New(env, RATS_ERROR_NOT_RUNNING));
1139
- errors.Set("OPERATION_FAILED", Napi::Number::New(env, RATS_ERROR_OPERATION_FAILED));
1140
- errors.Set("PEER_NOT_FOUND", Napi::Number::New(env, RATS_ERROR_PEER_NOT_FOUND));
1141
- errors.Set("MEMORY_ALLOCATION", Napi::Number::New(env, RATS_ERROR_MEMORY_ALLOCATION));
1142
- errors.Set("JSON_PARSE", Napi::Number::New(env, RATS_ERROR_JSON_PARSE));
1143
- constants.Set("ERRORS", errors);
1144
-
1145
- // Connection strategies
1146
- Napi::Object strategies = Napi::Object::New(env);
1147
- strategies.Set("DIRECT_ONLY", Napi::Number::New(env, RATS_STRATEGY_DIRECT_ONLY));
1148
- strategies.Set("STUN_ASSISTED", Napi::Number::New(env, RATS_STRATEGY_STUN_ASSISTED));
1149
- strategies.Set("ICE_FULL", Napi::Number::New(env, RATS_STRATEGY_ICE_FULL));
1150
- strategies.Set("TURN_RELAY", Napi::Number::New(env, RATS_STRATEGY_TURN_RELAY));
1151
- strategies.Set("AUTO_ADAPTIVE", Napi::Number::New(env, RATS_STRATEGY_AUTO_ADAPTIVE));
1152
- constants.Set("CONNECTION_STRATEGIES", strategies);
1153
-
1154
- return constants;
1155
- }
1156
-
1157
- // Module initialization
1158
- Napi::Object Init(Napi::Env env, Napi::Object exports) {
1159
- // Export the RatsClient class
1160
- RatsClient::Init(env, exports);
1161
-
1162
- // Export utility functions
1163
- exports.Set("getVersionString", Napi::Function::New(env, GetVersionString));
1164
- exports.Set("getVersion", Napi::Function::New(env, GetVersion));
1165
- exports.Set("getGitDescribe", Napi::Function::New(env, GetGitDescribe));
1166
- exports.Set("getAbi", Napi::Function::New(env, GetAbi));
1167
-
1168
- // Export constants
1169
- exports.Set("constants", InitConstants(env));
1170
-
1171
- return exports;
1172
- }
1173
-
1174
- NODE_API_MODULE(librats, Init)
1
+ #include <napi.h>
2
+ #include <iostream>
3
+ #include <string>
4
+ #include <memory>
5
+ #include <unordered_map>
6
+ #include "librats_c.h"
7
+
8
+ using namespace Napi;
9
+
10
+ // Forward declarations
11
+ class RatsClient;
12
+
13
+ // Global callback storage
14
+ struct CallbackData {
15
+ Napi::FunctionReference callback;
16
+ Napi::Env env;
17
+
18
+ CallbackData(Napi::Env environment) : env(environment) {}
19
+ };
20
+
21
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> connection_callbacks;
22
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> string_callbacks;
23
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> binary_callbacks;
24
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> json_callbacks;
25
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> disconnect_callbacks;
26
+ std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> file_progress_callbacks;
27
+
28
+ // C callback wrappers
29
+ void connection_callback_wrapper(void* user_data, const char* peer_id) {
30
+ rats_client_t client = static_cast<rats_client_t>(user_data);
31
+ auto it = connection_callbacks.find(client);
32
+ if (it != connection_callbacks.end() && it->second) {
33
+ auto callback_data = it->second;
34
+ callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
35
+ }
36
+ }
37
+
38
+ void string_callback_wrapper(void* user_data, const char* peer_id, const char* message) {
39
+ rats_client_t client = static_cast<rats_client_t>(user_data);
40
+ auto it = string_callbacks.find(client);
41
+ if (it != string_callbacks.end() && it->second) {
42
+ auto callback_data = it->second;
43
+ callback_data->callback.Call({
44
+ Napi::String::New(callback_data->env, peer_id),
45
+ Napi::String::New(callback_data->env, message)
46
+ });
47
+ }
48
+ }
49
+
50
+ void binary_callback_wrapper(void* user_data, const char* peer_id, const void* data, size_t size) {
51
+ rats_client_t client = static_cast<rats_client_t>(user_data);
52
+ auto it = binary_callbacks.find(client);
53
+ if (it != binary_callbacks.end() && it->second) {
54
+ auto callback_data = it->second;
55
+ auto buffer = Napi::Buffer<uint8_t>::New(callback_data->env, size);
56
+ memcpy(buffer.Data(), data, size);
57
+ callback_data->callback.Call({
58
+ Napi::String::New(callback_data->env, peer_id),
59
+ buffer
60
+ });
61
+ }
62
+ }
63
+
64
+ void json_callback_wrapper(void* user_data, const char* peer_id, const char* json_str) {
65
+ rats_client_t client = static_cast<rats_client_t>(user_data);
66
+ auto it = json_callbacks.find(client);
67
+ if (it != json_callbacks.end() && it->second) {
68
+ auto callback_data = it->second;
69
+ callback_data->callback.Call({
70
+ Napi::String::New(callback_data->env, peer_id),
71
+ Napi::String::New(callback_data->env, json_str)
72
+ });
73
+ }
74
+ }
75
+
76
+ void disconnect_callback_wrapper(void* user_data, const char* peer_id) {
77
+ rats_client_t client = static_cast<rats_client_t>(user_data);
78
+ auto it = disconnect_callbacks.find(client);
79
+ if (it != disconnect_callbacks.end() && it->second) {
80
+ auto callback_data = it->second;
81
+ callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
82
+ }
83
+ }
84
+
85
+ void file_progress_callback_wrapper(void* user_data, const char* transfer_id, int progress_percent, const char* status) {
86
+ rats_client_t client = static_cast<rats_client_t>(user_data);
87
+ auto it = file_progress_callbacks.find(client);
88
+ if (it != file_progress_callbacks.end() && it->second) {
89
+ auto callback_data = it->second;
90
+ callback_data->callback.Call({
91
+ Napi::String::New(callback_data->env, transfer_id),
92
+ Napi::Number::New(callback_data->env, progress_percent),
93
+ Napi::String::New(callback_data->env, status)
94
+ });
95
+ }
96
+ }
97
+
98
+ class RatsClient : public Napi::ObjectWrap<RatsClient> {
99
+ public:
100
+ static Napi::Object Init(Napi::Env env, Napi::Object exports) {
101
+ Napi::Function func = DefineClass(env, "RatsClient", {
102
+ InstanceMethod("start", &RatsClient::Start),
103
+ InstanceMethod("stop", &RatsClient::Stop),
104
+ InstanceMethod("connect", &RatsClient::Connect),
105
+ InstanceMethod("connectWithStrategy", &RatsClient::ConnectWithStrategy),
106
+ InstanceMethod("disconnect", &RatsClient::Disconnect),
107
+ InstanceMethod("broadcastString", &RatsClient::BroadcastString),
108
+ InstanceMethod("sendString", &RatsClient::SendString),
109
+ InstanceMethod("broadcastBinary", &RatsClient::BroadcastBinary),
110
+ InstanceMethod("sendBinary", &RatsClient::SendBinary),
111
+ InstanceMethod("broadcastJson", &RatsClient::BroadcastJson),
112
+ InstanceMethod("sendJson", &RatsClient::SendJson),
113
+ InstanceMethod("getPeerCount", &RatsClient::GetPeerCount),
114
+ InstanceMethod("getListenPort", &RatsClient::GetListenPort),
115
+ InstanceMethod("getOurPeerId", &RatsClient::GetOurPeerId),
116
+ InstanceMethod("getPeerIds", &RatsClient::GetPeerIds),
117
+ InstanceMethod("getConnectionStatistics", &RatsClient::GetConnectionStatistics),
118
+ InstanceMethod("setMaxPeers", &RatsClient::SetMaxPeers),
119
+ InstanceMethod("getMaxPeers", &RatsClient::GetMaxPeers),
120
+ InstanceMethod("isPeerLimitReached", &RatsClient::IsPeerLimitReached),
121
+
122
+ // DHT methods
123
+ InstanceMethod("startDhtDiscovery", &RatsClient::StartDhtDiscovery),
124
+ InstanceMethod("stopDhtDiscovery", &RatsClient::StopDhtDiscovery),
125
+ InstanceMethod("isDhtRunning", &RatsClient::IsDhtRunning),
126
+ InstanceMethod("announceForHash", &RatsClient::AnnounceForHash),
127
+ InstanceMethod("getDhtRoutingTableSize", &RatsClient::GetDhtRoutingTableSize),
128
+
129
+ // mDNS methods
130
+ InstanceMethod("startMdnsDiscovery", &RatsClient::StartMdnsDiscovery),
131
+ InstanceMethod("stopMdnsDiscovery", &RatsClient::StopMdnsDiscovery),
132
+ InstanceMethod("isMdnsRunning", &RatsClient::IsMdnsRunning),
133
+ InstanceMethod("queryMdnsServices", &RatsClient::QueryMdnsServices),
134
+
135
+ // Encryption methods
136
+ InstanceMethod("setEncryptionEnabled", &RatsClient::SetEncryptionEnabled),
137
+ InstanceMethod("isEncryptionEnabled", &RatsClient::IsEncryptionEnabled),
138
+ InstanceMethod("getEncryptionKey", &RatsClient::GetEncryptionKey),
139
+ InstanceMethod("setEncryptionKey", &RatsClient::SetEncryptionKey),
140
+ InstanceMethod("generateEncryptionKey", &RatsClient::GenerateEncryptionKey),
141
+
142
+ // GossipSub methods
143
+ InstanceMethod("isGossipsubAvailable", &RatsClient::IsGossipsubAvailable),
144
+ InstanceMethod("isGossipsubRunning", &RatsClient::IsGossipsubRunning),
145
+ InstanceMethod("subscribeToTopic", &RatsClient::SubscribeToTopic),
146
+ InstanceMethod("unsubscribeFromTopic", &RatsClient::UnsubscribeFromTopic),
147
+ InstanceMethod("isSubscribedToTopic", &RatsClient::IsSubscribedToTopic),
148
+ InstanceMethod("getSubscribedTopics", &RatsClient::GetSubscribedTopics),
149
+ InstanceMethod("publishToTopic", &RatsClient::PublishToTopic),
150
+ InstanceMethod("publishJsonToTopic", &RatsClient::PublishJsonToTopic),
151
+ InstanceMethod("getTopicPeers", &RatsClient::GetTopicPeers),
152
+ InstanceMethod("getGossipsubStatistics", &RatsClient::GetGossipsubStatistics),
153
+
154
+ // File Transfer methods
155
+ InstanceMethod("sendFile", &RatsClient::SendFile),
156
+ InstanceMethod("sendDirectory", &RatsClient::SendDirectory),
157
+ InstanceMethod("requestFile", &RatsClient::RequestFile),
158
+ InstanceMethod("requestDirectory", &RatsClient::RequestDirectory),
159
+ InstanceMethod("acceptFileTransfer", &RatsClient::AcceptFileTransfer),
160
+ InstanceMethod("rejectFileTransfer", &RatsClient::RejectFileTransfer),
161
+ InstanceMethod("cancelFileTransfer", &RatsClient::CancelFileTransfer),
162
+ InstanceMethod("pauseFileTransfer", &RatsClient::PauseFileTransfer),
163
+ InstanceMethod("resumeFileTransfer", &RatsClient::ResumeFileTransfer),
164
+ InstanceMethod("getFileTransferProgress", &RatsClient::GetFileTransferProgress),
165
+ InstanceMethod("getFileTransferStatistics", &RatsClient::GetFileTransferStatistics),
166
+
167
+ // NAT Traversal methods
168
+ InstanceMethod("discoverPublicIp", &RatsClient::DiscoverPublicIp),
169
+ InstanceMethod("getPublicIp", &RatsClient::GetPublicIp),
170
+ InstanceMethod("detectNatType", &RatsClient::DetectNatType),
171
+ InstanceMethod("getNatCharacteristics", &RatsClient::GetNatCharacteristics),
172
+ InstanceMethod("addIgnoredAddress", &RatsClient::AddIgnoredAddress),
173
+
174
+ // Callback methods
175
+ InstanceMethod("onConnection", &RatsClient::OnConnection),
176
+ InstanceMethod("onString", &RatsClient::OnString),
177
+ InstanceMethod("onBinary", &RatsClient::OnBinary),
178
+ InstanceMethod("onJson", &RatsClient::OnJson),
179
+ InstanceMethod("onDisconnect", &RatsClient::OnDisconnect),
180
+ InstanceMethod("onFileProgress", &RatsClient::OnFileProgress),
181
+
182
+ // Configuration persistence
183
+ InstanceMethod("loadConfiguration", &RatsClient::LoadConfiguration),
184
+ InstanceMethod("saveConfiguration", &RatsClient::SaveConfiguration),
185
+ InstanceMethod("setDataDirectory", &RatsClient::SetDataDirectory),
186
+ InstanceMethod("getDataDirectory", &RatsClient::GetDataDirectory)
187
+ });
188
+
189
+ exports.Set("RatsClient", func);
190
+ return exports;
191
+ }
192
+
193
+ RatsClient(const Napi::CallbackInfo& info) : Napi::ObjectWrap<RatsClient>(info) {
194
+ Napi::Env env = info.Env();
195
+
196
+ if (info.Length() < 1 || !info[0].IsNumber()) {
197
+ Napi::TypeError::New(env, "Expected port number").ThrowAsJavaScriptException();
198
+ return;
199
+ }
200
+
201
+ int port = info[0].As<Napi::Number>().Int32Value();
202
+ client_ = rats_create(port);
203
+
204
+ if (!client_) {
205
+ Napi::Error::New(env, "Failed to create RatsClient").ThrowAsJavaScriptException();
206
+ return;
207
+ }
208
+ }
209
+
210
+ ~RatsClient() {
211
+ if (client_) {
212
+ // Clean up callbacks
213
+ connection_callbacks.erase(client_);
214
+ string_callbacks.erase(client_);
215
+ binary_callbacks.erase(client_);
216
+ json_callbacks.erase(client_);
217
+ disconnect_callbacks.erase(client_);
218
+ file_progress_callbacks.erase(client_);
219
+
220
+ rats_destroy(client_);
221
+ }
222
+ }
223
+
224
+ private:
225
+ rats_client_t client_;
226
+
227
+ // Basic operations
228
+ Napi::Value Start(const Napi::CallbackInfo& info) {
229
+ Napi::Env env = info.Env();
230
+ int result = rats_start(client_);
231
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
232
+ }
233
+
234
+ void Stop(const Napi::CallbackInfo& info) {
235
+ rats_stop(client_);
236
+ }
237
+
238
+ Napi::Value Connect(const Napi::CallbackInfo& info) {
239
+ Napi::Env env = info.Env();
240
+
241
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
242
+ Napi::TypeError::New(env, "Expected host (string) and port (number)").ThrowAsJavaScriptException();
243
+ return env.Null();
244
+ }
245
+
246
+ std::string host = info[0].As<Napi::String>().Utf8Value();
247
+ int port = info[1].As<Napi::Number>().Int32Value();
248
+
249
+ int result = rats_connect(client_, host.c_str(), port);
250
+ return Napi::Boolean::New(env, result == 1);
251
+ }
252
+
253
+ Napi::Value ConnectWithStrategy(const Napi::CallbackInfo& info) {
254
+ Napi::Env env = info.Env();
255
+
256
+ if (info.Length() < 3 || !info[0].IsString() || !info[1].IsNumber() || !info[2].IsNumber()) {
257
+ Napi::TypeError::New(env, "Expected host (string), port (number), and strategy (number)").ThrowAsJavaScriptException();
258
+ return env.Null();
259
+ }
260
+
261
+ std::string host = info[0].As<Napi::String>().Utf8Value();
262
+ int port = info[1].As<Napi::Number>().Int32Value();
263
+ rats_connection_strategy_t strategy = static_cast<rats_connection_strategy_t>(info[2].As<Napi::Number>().Int32Value());
264
+
265
+ rats_error_t result = rats_connect_with_strategy(client_, host.c_str(), port, strategy);
266
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
267
+ }
268
+
269
+ void Disconnect(const Napi::CallbackInfo& info) {
270
+ if (info.Length() < 1 || !info[0].IsString()) {
271
+ Napi::TypeError::New(info.Env(), "Expected peer_id (string)").ThrowAsJavaScriptException();
272
+ return;
273
+ }
274
+
275
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
276
+ rats_disconnect_peer_by_id(client_, peer_id.c_str());
277
+ }
278
+
279
+ Napi::Value BroadcastString(const Napi::CallbackInfo& info) {
280
+ Napi::Env env = info.Env();
281
+
282
+ if (info.Length() < 1 || !info[0].IsString()) {
283
+ Napi::TypeError::New(env, "Expected message (string)").ThrowAsJavaScriptException();
284
+ return env.Null();
285
+ }
286
+
287
+ std::string message = info[0].As<Napi::String>().Utf8Value();
288
+ int result = rats_broadcast_string(client_, message.c_str());
289
+ return Napi::Number::New(env, result);
290
+ }
291
+
292
+ Napi::Value SendString(const Napi::CallbackInfo& info) {
293
+ Napi::Env env = info.Env();
294
+
295
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
296
+ Napi::TypeError::New(env, "Expected peer_id (string) and message (string)").ThrowAsJavaScriptException();
297
+ return env.Null();
298
+ }
299
+
300
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
301
+ std::string message = info[1].As<Napi::String>().Utf8Value();
302
+
303
+ int result = rats_send_string(client_, peer_id.c_str(), message.c_str());
304
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
305
+ }
306
+
307
+ Napi::Value BroadcastBinary(const Napi::CallbackInfo& info) {
308
+ Napi::Env env = info.Env();
309
+
310
+ if (info.Length() < 1 || !info[0].IsBuffer()) {
311
+ Napi::TypeError::New(env, "Expected data (Buffer)").ThrowAsJavaScriptException();
312
+ return env.Null();
313
+ }
314
+
315
+ Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
316
+ int result = rats_broadcast_binary(client_, buffer.Data(), buffer.Length());
317
+ return Napi::Number::New(env, result);
318
+ }
319
+
320
+ Napi::Value SendBinary(const Napi::CallbackInfo& info) {
321
+ Napi::Env env = info.Env();
322
+
323
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsBuffer()) {
324
+ Napi::TypeError::New(env, "Expected peer_id (string) and data (Buffer)").ThrowAsJavaScriptException();
325
+ return env.Null();
326
+ }
327
+
328
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
329
+ Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
330
+
331
+ rats_error_t result = rats_send_binary(client_, peer_id.c_str(), buffer.Data(), buffer.Length());
332
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
333
+ }
334
+
335
+ Napi::Value BroadcastJson(const Napi::CallbackInfo& info) {
336
+ Napi::Env env = info.Env();
337
+
338
+ if (info.Length() < 1 || !info[0].IsString()) {
339
+ Napi::TypeError::New(env, "Expected json_str (string)").ThrowAsJavaScriptException();
340
+ return env.Null();
341
+ }
342
+
343
+ std::string json_str = info[0].As<Napi::String>().Utf8Value();
344
+ int result = rats_broadcast_json(client_, json_str.c_str());
345
+ return Napi::Number::New(env, result);
346
+ }
347
+
348
+ Napi::Value SendJson(const Napi::CallbackInfo& info) {
349
+ Napi::Env env = info.Env();
350
+
351
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
352
+ Napi::TypeError::New(env, "Expected peer_id (string) and json_str (string)").ThrowAsJavaScriptException();
353
+ return env.Null();
354
+ }
355
+
356
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
357
+ std::string json_str = info[1].As<Napi::String>().Utf8Value();
358
+
359
+ rats_error_t result = rats_send_json(client_, peer_id.c_str(), json_str.c_str());
360
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
361
+ }
362
+
363
+ // Info methods
364
+ Napi::Value GetPeerCount(const Napi::CallbackInfo& info) {
365
+ Napi::Env env = info.Env();
366
+ int count = rats_get_peer_count(client_);
367
+ return Napi::Number::New(env, count);
368
+ }
369
+
370
+ Napi::Value GetListenPort(const Napi::CallbackInfo& info) {
371
+ Napi::Env env = info.Env();
372
+ int port = rats_get_listen_port(client_);
373
+ return Napi::Number::New(env, port);
374
+ }
375
+
376
+ Napi::Value GetOurPeerId(const Napi::CallbackInfo& info) {
377
+ Napi::Env env = info.Env();
378
+ char* peer_id = rats_get_our_peer_id(client_);
379
+ if (!peer_id) return env.Null();
380
+
381
+ Napi::String result = Napi::String::New(env, peer_id);
382
+ rats_string_free(peer_id);
383
+ return result;
384
+ }
385
+
386
+ Napi::Value GetPeerIds(const Napi::CallbackInfo& info) {
387
+ Napi::Env env = info.Env();
388
+ int count = 0;
389
+ char** peer_ids = rats_get_peer_ids(client_, &count);
390
+
391
+ if (!peer_ids || count == 0) {
392
+ return Napi::Array::New(env, 0);
393
+ }
394
+
395
+ Napi::Array result = Napi::Array::New(env, count);
396
+ for (int i = 0; i < count; i++) {
397
+ result[i] = Napi::String::New(env, peer_ids[i]);
398
+ rats_string_free(peer_ids[i]);
399
+ }
400
+ free(peer_ids);
401
+
402
+ return result;
403
+ }
404
+
405
+ Napi::Value GetConnectionStatistics(const Napi::CallbackInfo& info) {
406
+ Napi::Env env = info.Env();
407
+ char* stats_json = rats_get_connection_statistics_json(client_);
408
+ if (!stats_json) return env.Null();
409
+
410
+ Napi::String result = Napi::String::New(env, stats_json);
411
+ rats_string_free(stats_json);
412
+ return result;
413
+ }
414
+
415
+ // Peer configuration
416
+ Napi::Value SetMaxPeers(const Napi::CallbackInfo& info) {
417
+ Napi::Env env = info.Env();
418
+
419
+ if (info.Length() < 1 || !info[0].IsNumber()) {
420
+ Napi::TypeError::New(env, "Expected max_peers (number)").ThrowAsJavaScriptException();
421
+ return env.Null();
422
+ }
423
+
424
+ int max_peers = info[0].As<Napi::Number>().Int32Value();
425
+ rats_error_t result = rats_set_max_peers(client_, max_peers);
426
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
427
+ }
428
+
429
+ Napi::Value GetMaxPeers(const Napi::CallbackInfo& info) {
430
+ Napi::Env env = info.Env();
431
+ int max_peers = rats_get_max_peers(client_);
432
+ return Napi::Number::New(env, max_peers);
433
+ }
434
+
435
+ Napi::Value IsPeerLimitReached(const Napi::CallbackInfo& info) {
436
+ Napi::Env env = info.Env();
437
+ int reached = rats_is_peer_limit_reached(client_);
438
+ return Napi::Boolean::New(env, reached != 0);
439
+ }
440
+
441
+ // DHT methods
442
+ Napi::Value StartDhtDiscovery(const Napi::CallbackInfo& info) {
443
+ Napi::Env env = info.Env();
444
+
445
+ if (info.Length() < 1 || !info[0].IsNumber()) {
446
+ Napi::TypeError::New(env, "Expected dht_port (number)").ThrowAsJavaScriptException();
447
+ return env.Null();
448
+ }
449
+
450
+ int dht_port = info[0].As<Napi::Number>().Int32Value();
451
+ rats_error_t result = rats_start_dht_discovery(client_, dht_port);
452
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
453
+ }
454
+
455
+ void StopDhtDiscovery(const Napi::CallbackInfo& info) {
456
+ rats_stop_dht_discovery(client_);
457
+ }
458
+
459
+ Napi::Value IsDhtRunning(const Napi::CallbackInfo& info) {
460
+ Napi::Env env = info.Env();
461
+ int running = rats_is_dht_running(client_);
462
+ return Napi::Boolean::New(env, running != 0);
463
+ }
464
+
465
+ Napi::Value AnnounceForHash(const Napi::CallbackInfo& info) {
466
+ Napi::Env env = info.Env();
467
+
468
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
469
+ Napi::TypeError::New(env, "Expected content_hash (string) and port (number)").ThrowAsJavaScriptException();
470
+ return env.Null();
471
+ }
472
+
473
+ std::string content_hash = info[0].As<Napi::String>().Utf8Value();
474
+ int port = info[1].As<Napi::Number>().Int32Value();
475
+
476
+ rats_error_t result = rats_announce_for_hash(client_, content_hash.c_str(), port);
477
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
478
+ }
479
+
480
+ Napi::Value GetDhtRoutingTableSize(const Napi::CallbackInfo& info) {
481
+ Napi::Env env = info.Env();
482
+ size_t size = rats_get_dht_routing_table_size(client_);
483
+ return Napi::Number::New(env, static_cast<double>(size));
484
+ }
485
+
486
+ // mDNS methods
487
+ Napi::Value StartMdnsDiscovery(const Napi::CallbackInfo& info) {
488
+ Napi::Env env = info.Env();
489
+
490
+ const char* service_name = nullptr;
491
+ if (info.Length() > 0 && info[0].IsString()) {
492
+ std::string service = info[0].As<Napi::String>().Utf8Value();
493
+ service_name = service.c_str();
494
+ }
495
+
496
+ rats_error_t result = rats_start_mdns_discovery(client_, service_name);
497
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
498
+ }
499
+
500
+ void StopMdnsDiscovery(const Napi::CallbackInfo& info) {
501
+ rats_stop_mdns_discovery(client_);
502
+ }
503
+
504
+ Napi::Value IsMdnsRunning(const Napi::CallbackInfo& info) {
505
+ Napi::Env env = info.Env();
506
+ int running = rats_is_mdns_running(client_);
507
+ return Napi::Boolean::New(env, running != 0);
508
+ }
509
+
510
+ Napi::Value QueryMdnsServices(const Napi::CallbackInfo& info) {
511
+ Napi::Env env = info.Env();
512
+ rats_error_t result = rats_query_mdns_services(client_);
513
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
514
+ }
515
+
516
+ // Encryption methods
517
+ Napi::Value SetEncryptionEnabled(const Napi::CallbackInfo& info) {
518
+ Napi::Env env = info.Env();
519
+
520
+ if (info.Length() < 1 || !info[0].IsBoolean()) {
521
+ Napi::TypeError::New(env, "Expected enabled (boolean)").ThrowAsJavaScriptException();
522
+ return env.Null();
523
+ }
524
+
525
+ bool enabled = info[0].As<Napi::Boolean>().Value();
526
+ rats_error_t result = rats_set_encryption_enabled(client_, enabled ? 1 : 0);
527
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
528
+ }
529
+
530
+ Napi::Value IsEncryptionEnabled(const Napi::CallbackInfo& info) {
531
+ Napi::Env env = info.Env();
532
+ int enabled = rats_is_encryption_enabled(client_);
533
+ return Napi::Boolean::New(env, enabled != 0);
534
+ }
535
+
536
+ Napi::Value GetEncryptionKey(const Napi::CallbackInfo& info) {
537
+ Napi::Env env = info.Env();
538
+ char* key = rats_get_encryption_key(client_);
539
+ if (!key) return env.Null();
540
+
541
+ Napi::String result = Napi::String::New(env, key);
542
+ rats_string_free(key);
543
+ return result;
544
+ }
545
+
546
+ Napi::Value SetEncryptionKey(const Napi::CallbackInfo& info) {
547
+ Napi::Env env = info.Env();
548
+
549
+ if (info.Length() < 1 || !info[0].IsString()) {
550
+ Napi::TypeError::New(env, "Expected key_hex (string)").ThrowAsJavaScriptException();
551
+ return env.Null();
552
+ }
553
+
554
+ std::string key_hex = info[0].As<Napi::String>().Utf8Value();
555
+ rats_error_t result = rats_set_encryption_key(client_, key_hex.c_str());
556
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
557
+ }
558
+
559
+ Napi::Value GenerateEncryptionKey(const Napi::CallbackInfo& info) {
560
+ Napi::Env env = info.Env();
561
+ char* key = rats_generate_encryption_key(client_);
562
+ if (!key) return env.Null();
563
+
564
+ Napi::String result = Napi::String::New(env, key);
565
+ rats_string_free(key);
566
+ return result;
567
+ }
568
+
569
+ // GossipSub methods
570
+ Napi::Value IsGossipsubAvailable(const Napi::CallbackInfo& info) {
571
+ Napi::Env env = info.Env();
572
+ int available = rats_is_gossipsub_available(client_);
573
+ return Napi::Boolean::New(env, available != 0);
574
+ }
575
+
576
+ Napi::Value IsGossipsubRunning(const Napi::CallbackInfo& info) {
577
+ Napi::Env env = info.Env();
578
+ int running = rats_is_gossipsub_running(client_);
579
+ return Napi::Boolean::New(env, running != 0);
580
+ }
581
+
582
+ Napi::Value SubscribeToTopic(const Napi::CallbackInfo& info) {
583
+ Napi::Env env = info.Env();
584
+
585
+ if (info.Length() < 1 || !info[0].IsString()) {
586
+ Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
587
+ return env.Null();
588
+ }
589
+
590
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
591
+ rats_error_t result = rats_subscribe_to_topic(client_, topic.c_str());
592
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
593
+ }
594
+
595
+ Napi::Value UnsubscribeFromTopic(const Napi::CallbackInfo& info) {
596
+ Napi::Env env = info.Env();
597
+
598
+ if (info.Length() < 1 || !info[0].IsString()) {
599
+ Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
600
+ return env.Null();
601
+ }
602
+
603
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
604
+ rats_error_t result = rats_unsubscribe_from_topic(client_, topic.c_str());
605
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
606
+ }
607
+
608
+ Napi::Value IsSubscribedToTopic(const Napi::CallbackInfo& info) {
609
+ Napi::Env env = info.Env();
610
+
611
+ if (info.Length() < 1 || !info[0].IsString()) {
612
+ Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
613
+ return env.Null();
614
+ }
615
+
616
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
617
+ int subscribed = rats_is_subscribed_to_topic(client_, topic.c_str());
618
+ return Napi::Boolean::New(env, subscribed != 0);
619
+ }
620
+
621
+ Napi::Value GetSubscribedTopics(const Napi::CallbackInfo& info) {
622
+ Napi::Env env = info.Env();
623
+ int count = 0;
624
+ char** topics = rats_get_subscribed_topics(client_, &count);
625
+
626
+ if (!topics || count == 0) {
627
+ return Napi::Array::New(env, 0);
628
+ }
629
+
630
+ Napi::Array result = Napi::Array::New(env, count);
631
+ for (int i = 0; i < count; i++) {
632
+ result[i] = Napi::String::New(env, topics[i]);
633
+ rats_string_free(topics[i]);
634
+ }
635
+ free(topics);
636
+
637
+ return result;
638
+ }
639
+
640
+ Napi::Value PublishToTopic(const Napi::CallbackInfo& info) {
641
+ Napi::Env env = info.Env();
642
+
643
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
644
+ Napi::TypeError::New(env, "Expected topic (string) and message (string)").ThrowAsJavaScriptException();
645
+ return env.Null();
646
+ }
647
+
648
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
649
+ std::string message = info[1].As<Napi::String>().Utf8Value();
650
+
651
+ rats_error_t result = rats_publish_to_topic(client_, topic.c_str(), message.c_str());
652
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
653
+ }
654
+
655
+ Napi::Value PublishJsonToTopic(const Napi::CallbackInfo& info) {
656
+ Napi::Env env = info.Env();
657
+
658
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
659
+ Napi::TypeError::New(env, "Expected topic (string) and json_str (string)").ThrowAsJavaScriptException();
660
+ return env.Null();
661
+ }
662
+
663
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
664
+ std::string json_str = info[1].As<Napi::String>().Utf8Value();
665
+
666
+ rats_error_t result = rats_publish_json_to_topic(client_, topic.c_str(), json_str.c_str());
667
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
668
+ }
669
+
670
+ Napi::Value GetTopicPeers(const Napi::CallbackInfo& info) {
671
+ Napi::Env env = info.Env();
672
+
673
+ if (info.Length() < 1 || !info[0].IsString()) {
674
+ Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
675
+ return env.Null();
676
+ }
677
+
678
+ std::string topic = info[0].As<Napi::String>().Utf8Value();
679
+ int count = 0;
680
+ char** peers = rats_get_topic_peers(client_, topic.c_str(), &count);
681
+
682
+ if (!peers || count == 0) {
683
+ return Napi::Array::New(env, 0);
684
+ }
685
+
686
+ Napi::Array result = Napi::Array::New(env, count);
687
+ for (int i = 0; i < count; i++) {
688
+ result[i] = Napi::String::New(env, peers[i]);
689
+ rats_string_free(peers[i]);
690
+ }
691
+ free(peers);
692
+
693
+ return result;
694
+ }
695
+
696
+ Napi::Value GetGossipsubStatistics(const Napi::CallbackInfo& info) {
697
+ Napi::Env env = info.Env();
698
+ char* stats_json = rats_get_gossipsub_statistics_json(client_);
699
+ if (!stats_json) return env.Null();
700
+
701
+ Napi::String result = Napi::String::New(env, stats_json);
702
+ rats_string_free(stats_json);
703
+ return result;
704
+ }
705
+
706
+ // File Transfer methods
707
+ Napi::Value SendFile(const Napi::CallbackInfo& info) {
708
+ Napi::Env env = info.Env();
709
+
710
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
711
+ Napi::TypeError::New(env, "Expected peer_id (string) and file_path (string)").ThrowAsJavaScriptException();
712
+ return env.Null();
713
+ }
714
+
715
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
716
+ std::string file_path = info[1].As<Napi::String>().Utf8Value();
717
+
718
+ const char* remote_filename = nullptr;
719
+ if (info.Length() > 2 && info[2].IsString()) {
720
+ std::string remote_name = info[2].As<Napi::String>().Utf8Value();
721
+ remote_filename = remote_name.c_str();
722
+ }
723
+
724
+ char* transfer_id = rats_send_file(client_, peer_id.c_str(), file_path.c_str(), remote_filename);
725
+ if (!transfer_id) return env.Null();
726
+
727
+ Napi::String result = Napi::String::New(env, transfer_id);
728
+ rats_string_free(transfer_id);
729
+ return result;
730
+ }
731
+
732
+ Napi::Value SendDirectory(const Napi::CallbackInfo& info) {
733
+ Napi::Env env = info.Env();
734
+
735
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
736
+ Napi::TypeError::New(env, "Expected peer_id (string) and directory_path (string)").ThrowAsJavaScriptException();
737
+ return env.Null();
738
+ }
739
+
740
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
741
+ std::string directory_path = info[1].As<Napi::String>().Utf8Value();
742
+
743
+ const char* remote_directory_name = nullptr;
744
+ if (info.Length() > 2 && info[2].IsString()) {
745
+ std::string remote_name = info[2].As<Napi::String>().Utf8Value();
746
+ remote_directory_name = remote_name.c_str();
747
+ }
748
+
749
+ int recursive = 1;
750
+ if (info.Length() > 3 && info[3].IsBoolean()) {
751
+ recursive = info[3].As<Napi::Boolean>().Value() ? 1 : 0;
752
+ }
753
+
754
+ char* transfer_id = rats_send_directory(client_, peer_id.c_str(), directory_path.c_str(), remote_directory_name, recursive);
755
+ if (!transfer_id) return env.Null();
756
+
757
+ Napi::String result = Napi::String::New(env, transfer_id);
758
+ rats_string_free(transfer_id);
759
+ return result;
760
+ }
761
+
762
+ Napi::Value RequestFile(const Napi::CallbackInfo& info) {
763
+ Napi::Env env = info.Env();
764
+
765
+ if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString()) {
766
+ Napi::TypeError::New(env, "Expected peer_id (string), remote_file_path (string), and local_path (string)").ThrowAsJavaScriptException();
767
+ return env.Null();
768
+ }
769
+
770
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
771
+ std::string remote_file_path = info[1].As<Napi::String>().Utf8Value();
772
+ std::string local_path = info[2].As<Napi::String>().Utf8Value();
773
+
774
+ char* transfer_id = rats_request_file(client_, peer_id.c_str(), remote_file_path.c_str(), local_path.c_str());
775
+ if (!transfer_id) return env.Null();
776
+
777
+ Napi::String result = Napi::String::New(env, transfer_id);
778
+ rats_string_free(transfer_id);
779
+ return result;
780
+ }
781
+
782
+ Napi::Value RequestDirectory(const Napi::CallbackInfo& info) {
783
+ Napi::Env env = info.Env();
784
+
785
+ if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString()) {
786
+ Napi::TypeError::New(env, "Expected peer_id (string), remote_directory_path (string), and local_directory_path (string)").ThrowAsJavaScriptException();
787
+ return env.Null();
788
+ }
789
+
790
+ std::string peer_id = info[0].As<Napi::String>().Utf8Value();
791
+ std::string remote_directory_path = info[1].As<Napi::String>().Utf8Value();
792
+ std::string local_directory_path = info[2].As<Napi::String>().Utf8Value();
793
+
794
+ int recursive = 1;
795
+ if (info.Length() > 3 && info[3].IsBoolean()) {
796
+ recursive = info[3].As<Napi::Boolean>().Value() ? 1 : 0;
797
+ }
798
+
799
+ char* transfer_id = rats_request_directory(client_, peer_id.c_str(), remote_directory_path.c_str(), local_directory_path.c_str(), recursive);
800
+ if (!transfer_id) return env.Null();
801
+
802
+ Napi::String result = Napi::String::New(env, transfer_id);
803
+ rats_string_free(transfer_id);
804
+ return result;
805
+ }
806
+
807
+ Napi::Value AcceptFileTransfer(const Napi::CallbackInfo& info) {
808
+ Napi::Env env = info.Env();
809
+
810
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
811
+ Napi::TypeError::New(env, "Expected transfer_id (string) and local_path (string)").ThrowAsJavaScriptException();
812
+ return env.Null();
813
+ }
814
+
815
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
816
+ std::string local_path = info[1].As<Napi::String>().Utf8Value();
817
+
818
+ rats_error_t result = rats_accept_file_transfer(client_, transfer_id.c_str(), local_path.c_str());
819
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
820
+ }
821
+
822
+ Napi::Value RejectFileTransfer(const Napi::CallbackInfo& info) {
823
+ Napi::Env env = info.Env();
824
+
825
+ if (info.Length() < 1 || !info[0].IsString()) {
826
+ Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
827
+ return env.Null();
828
+ }
829
+
830
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
831
+
832
+ const char* reason = nullptr;
833
+ if (info.Length() > 1 && info[1].IsString()) {
834
+ std::string reason_str = info[1].As<Napi::String>().Utf8Value();
835
+ reason = reason_str.c_str();
836
+ }
837
+
838
+ rats_error_t result = rats_reject_file_transfer(client_, transfer_id.c_str(), reason);
839
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
840
+ }
841
+
842
+ Napi::Value CancelFileTransfer(const Napi::CallbackInfo& info) {
843
+ Napi::Env env = info.Env();
844
+
845
+ if (info.Length() < 1 || !info[0].IsString()) {
846
+ Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
847
+ return env.Null();
848
+ }
849
+
850
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
851
+
852
+ rats_error_t result = rats_cancel_file_transfer(client_, transfer_id.c_str());
853
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
854
+ }
855
+
856
+ Napi::Value PauseFileTransfer(const Napi::CallbackInfo& info) {
857
+ Napi::Env env = info.Env();
858
+
859
+ if (info.Length() < 1 || !info[0].IsString()) {
860
+ Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
861
+ return env.Null();
862
+ }
863
+
864
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
865
+
866
+ rats_error_t result = rats_pause_file_transfer(client_, transfer_id.c_str());
867
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
868
+ }
869
+
870
+ Napi::Value ResumeFileTransfer(const Napi::CallbackInfo& info) {
871
+ Napi::Env env = info.Env();
872
+
873
+ if (info.Length() < 1 || !info[0].IsString()) {
874
+ Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
875
+ return env.Null();
876
+ }
877
+
878
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
879
+
880
+ rats_error_t result = rats_resume_file_transfer(client_, transfer_id.c_str());
881
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
882
+ }
883
+
884
+ Napi::Value GetFileTransferProgress(const Napi::CallbackInfo& info) {
885
+ Napi::Env env = info.Env();
886
+
887
+ if (info.Length() < 1 || !info[0].IsString()) {
888
+ Napi::TypeError::New(env, "Expected transfer_id (string)").ThrowAsJavaScriptException();
889
+ return env.Null();
890
+ }
891
+
892
+ std::string transfer_id = info[0].As<Napi::String>().Utf8Value();
893
+
894
+ char* progress_json = rats_get_file_transfer_progress_json(client_, transfer_id.c_str());
895
+ if (!progress_json) return env.Null();
896
+
897
+ Napi::String result = Napi::String::New(env, progress_json);
898
+ rats_string_free(progress_json);
899
+ return result;
900
+ }
901
+
902
+ Napi::Value GetFileTransferStatistics(const Napi::CallbackInfo& info) {
903
+ Napi::Env env = info.Env();
904
+ char* stats_json = rats_get_file_transfer_statistics_json(client_);
905
+ if (!stats_json) return env.Null();
906
+
907
+ Napi::String result = Napi::String::New(env, stats_json);
908
+ rats_string_free(stats_json);
909
+ return result;
910
+ }
911
+
912
+ // NAT Traversal methods
913
+ Napi::Value DiscoverPublicIp(const Napi::CallbackInfo& info) {
914
+ Napi::Env env = info.Env();
915
+
916
+ const char* stun_server = "stun.l.google.com";
917
+ int stun_port = 19302;
918
+
919
+ if (info.Length() > 0 && info[0].IsString()) {
920
+ std::string server = info[0].As<Napi::String>().Utf8Value();
921
+ stun_server = server.c_str();
922
+ }
923
+
924
+ if (info.Length() > 1 && info[1].IsNumber()) {
925
+ stun_port = info[1].As<Napi::Number>().Int32Value();
926
+ }
927
+
928
+ rats_error_t result = rats_discover_and_ignore_public_ip(client_, stun_server, stun_port);
929
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
930
+ }
931
+
932
+ Napi::Value GetPublicIp(const Napi::CallbackInfo& info) {
933
+ Napi::Env env = info.Env();
934
+ char* public_ip = rats_get_public_ip(client_);
935
+ if (!public_ip) return env.Null();
936
+
937
+ Napi::String result = Napi::String::New(env, public_ip);
938
+ rats_string_free(public_ip);
939
+ return result;
940
+ }
941
+
942
+ Napi::Value DetectNatType(const Napi::CallbackInfo& info) {
943
+ Napi::Env env = info.Env();
944
+ int nat_type = rats_detect_nat_type(client_);
945
+ return Napi::Number::New(env, nat_type);
946
+ }
947
+
948
+ Napi::Value GetNatCharacteristics(const Napi::CallbackInfo& info) {
949
+ Napi::Env env = info.Env();
950
+ char* characteristics_json = rats_get_nat_characteristics_json(client_);
951
+ if (!characteristics_json) return env.Null();
952
+
953
+ Napi::String result = Napi::String::New(env, characteristics_json);
954
+ rats_string_free(characteristics_json);
955
+ return result;
956
+ }
957
+
958
+ void AddIgnoredAddress(const Napi::CallbackInfo& info) {
959
+ if (info.Length() < 1 || !info[0].IsString()) {
960
+ Napi::TypeError::New(info.Env(), "Expected ip_address (string)").ThrowAsJavaScriptException();
961
+ return;
962
+ }
963
+
964
+ std::string ip_address = info[0].As<Napi::String>().Utf8Value();
965
+ rats_add_ignored_address(client_, ip_address.c_str());
966
+ }
967
+
968
+ // Callback methods
969
+ void OnConnection(const Napi::CallbackInfo& info) {
970
+ Napi::Env env = info.Env();
971
+
972
+ if (info.Length() < 1 || !info[0].IsFunction()) {
973
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
974
+ return;
975
+ }
976
+
977
+ auto callback_data = std::make_shared<CallbackData>(env);
978
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
979
+
980
+ connection_callbacks[client_] = callback_data;
981
+ rats_set_connection_callback(client_, connection_callback_wrapper, client_);
982
+ }
983
+
984
+ void OnString(const Napi::CallbackInfo& info) {
985
+ Napi::Env env = info.Env();
986
+
987
+ if (info.Length() < 1 || !info[0].IsFunction()) {
988
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
989
+ return;
990
+ }
991
+
992
+ auto callback_data = std::make_shared<CallbackData>(env);
993
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
994
+
995
+ string_callbacks[client_] = callback_data;
996
+ rats_set_string_callback(client_, string_callback_wrapper, client_);
997
+ }
998
+
999
+ void OnBinary(const Napi::CallbackInfo& info) {
1000
+ Napi::Env env = info.Env();
1001
+
1002
+ if (info.Length() < 1 || !info[0].IsFunction()) {
1003
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1004
+ return;
1005
+ }
1006
+
1007
+ auto callback_data = std::make_shared<CallbackData>(env);
1008
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1009
+
1010
+ binary_callbacks[client_] = callback_data;
1011
+ rats_set_binary_callback(client_, binary_callback_wrapper, client_);
1012
+ }
1013
+
1014
+ void OnJson(const Napi::CallbackInfo& info) {
1015
+ Napi::Env env = info.Env();
1016
+
1017
+ if (info.Length() < 1 || !info[0].IsFunction()) {
1018
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1019
+ return;
1020
+ }
1021
+
1022
+ auto callback_data = std::make_shared<CallbackData>(env);
1023
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1024
+
1025
+ json_callbacks[client_] = callback_data;
1026
+ rats_set_json_callback(client_, json_callback_wrapper, client_);
1027
+ }
1028
+
1029
+ void OnDisconnect(const Napi::CallbackInfo& info) {
1030
+ Napi::Env env = info.Env();
1031
+
1032
+ if (info.Length() < 1 || !info[0].IsFunction()) {
1033
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1034
+ return;
1035
+ }
1036
+
1037
+ auto callback_data = std::make_shared<CallbackData>(env);
1038
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1039
+
1040
+ disconnect_callbacks[client_] = callback_data;
1041
+ rats_set_disconnect_callback(client_, disconnect_callback_wrapper, client_);
1042
+ }
1043
+
1044
+ void OnFileProgress(const Napi::CallbackInfo& info) {
1045
+ Napi::Env env = info.Env();
1046
+
1047
+ if (info.Length() < 1 || !info[0].IsFunction()) {
1048
+ Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
1049
+ return;
1050
+ }
1051
+
1052
+ auto callback_data = std::make_shared<CallbackData>(env);
1053
+ callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
1054
+
1055
+ file_progress_callbacks[client_] = callback_data;
1056
+ rats_set_file_progress_callback(client_, file_progress_callback_wrapper, client_);
1057
+ }
1058
+
1059
+ // Configuration persistence
1060
+ Napi::Value LoadConfiguration(const Napi::CallbackInfo& info) {
1061
+ Napi::Env env = info.Env();
1062
+ rats_error_t result = rats_load_configuration(client_);
1063
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
1064
+ }
1065
+
1066
+ Napi::Value SaveConfiguration(const Napi::CallbackInfo& info) {
1067
+ Napi::Env env = info.Env();
1068
+ rats_error_t result = rats_save_configuration(client_);
1069
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
1070
+ }
1071
+
1072
+ Napi::Value SetDataDirectory(const Napi::CallbackInfo& info) {
1073
+ Napi::Env env = info.Env();
1074
+
1075
+ if (info.Length() < 1 || !info[0].IsString()) {
1076
+ Napi::TypeError::New(env, "Expected directory_path (string)").ThrowAsJavaScriptException();
1077
+ return env.Null();
1078
+ }
1079
+
1080
+ std::string directory_path = info[0].As<Napi::String>().Utf8Value();
1081
+ rats_error_t result = rats_set_data_directory(client_, directory_path.c_str());
1082
+ return Napi::Boolean::New(env, result == RATS_SUCCESS);
1083
+ }
1084
+
1085
+ Napi::Value GetDataDirectory(const Napi::CallbackInfo& info) {
1086
+ Napi::Env env = info.Env();
1087
+ char* directory = rats_get_data_directory(client_);
1088
+ if (!directory) return env.Null();
1089
+
1090
+ Napi::String result = Napi::String::New(env, directory);
1091
+ rats_string_free(directory);
1092
+ return result;
1093
+ }
1094
+ };
1095
+
1096
+ // Library utility functions
1097
+ Napi::Value GetVersionString(const Napi::CallbackInfo& info) {
1098
+ Napi::Env env = info.Env();
1099
+ const char* version = rats_get_version_string();
1100
+ return Napi::String::New(env, version);
1101
+ }
1102
+
1103
+ Napi::Value GetVersion(const Napi::CallbackInfo& info) {
1104
+ Napi::Env env = info.Env();
1105
+ int major, minor, patch, build;
1106
+ rats_get_version(&major, &minor, &patch, &build);
1107
+
1108
+ Napi::Object version = Napi::Object::New(env);
1109
+ version.Set("major", Napi::Number::New(env, major));
1110
+ version.Set("minor", Napi::Number::New(env, minor));
1111
+ version.Set("patch", Napi::Number::New(env, patch));
1112
+ version.Set("build", Napi::Number::New(env, build));
1113
+
1114
+ return version;
1115
+ }
1116
+
1117
+ Napi::Value GetGitDescribe(const Napi::CallbackInfo& info) {
1118
+ Napi::Env env = info.Env();
1119
+ const char* git_describe = rats_get_git_describe();
1120
+ return Napi::String::New(env, git_describe);
1121
+ }
1122
+
1123
+ Napi::Value GetAbi(const Napi::CallbackInfo& info) {
1124
+ Napi::Env env = info.Env();
1125
+ uint32_t abi = rats_get_abi();
1126
+ return Napi::Number::New(env, abi);
1127
+ }
1128
+
1129
+ // Constants
1130
+ Napi::Object InitConstants(Napi::Env env) {
1131
+ Napi::Object constants = Napi::Object::New(env);
1132
+
1133
+ // Error codes
1134
+ Napi::Object errors = Napi::Object::New(env);
1135
+ errors.Set("SUCCESS", Napi::Number::New(env, RATS_SUCCESS));
1136
+ errors.Set("INVALID_HANDLE", Napi::Number::New(env, RATS_ERROR_INVALID_HANDLE));
1137
+ errors.Set("INVALID_PARAMETER", Napi::Number::New(env, RATS_ERROR_INVALID_PARAMETER));
1138
+ errors.Set("NOT_RUNNING", Napi::Number::New(env, RATS_ERROR_NOT_RUNNING));
1139
+ errors.Set("OPERATION_FAILED", Napi::Number::New(env, RATS_ERROR_OPERATION_FAILED));
1140
+ errors.Set("PEER_NOT_FOUND", Napi::Number::New(env, RATS_ERROR_PEER_NOT_FOUND));
1141
+ errors.Set("MEMORY_ALLOCATION", Napi::Number::New(env, RATS_ERROR_MEMORY_ALLOCATION));
1142
+ errors.Set("JSON_PARSE", Napi::Number::New(env, RATS_ERROR_JSON_PARSE));
1143
+ constants.Set("ERRORS", errors);
1144
+
1145
+ // Connection strategies
1146
+ Napi::Object strategies = Napi::Object::New(env);
1147
+ strategies.Set("DIRECT_ONLY", Napi::Number::New(env, RATS_STRATEGY_DIRECT_ONLY));
1148
+ strategies.Set("STUN_ASSISTED", Napi::Number::New(env, RATS_STRATEGY_STUN_ASSISTED));
1149
+ strategies.Set("ICE_FULL", Napi::Number::New(env, RATS_STRATEGY_ICE_FULL));
1150
+ strategies.Set("TURN_RELAY", Napi::Number::New(env, RATS_STRATEGY_TURN_RELAY));
1151
+ strategies.Set("AUTO_ADAPTIVE", Napi::Number::New(env, RATS_STRATEGY_AUTO_ADAPTIVE));
1152
+ constants.Set("CONNECTION_STRATEGIES", strategies);
1153
+
1154
+ return constants;
1155
+ }
1156
+
1157
+ // Module initialization
1158
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
1159
+ // Export the RatsClient class
1160
+ RatsClient::Init(env, exports);
1161
+
1162
+ // Export utility functions
1163
+ exports.Set("getVersionString", Napi::Function::New(env, GetVersionString));
1164
+ exports.Set("getVersion", Napi::Function::New(env, GetVersion));
1165
+ exports.Set("getGitDescribe", Napi::Function::New(env, GetGitDescribe));
1166
+ exports.Set("getAbi", Napi::Function::New(env, GetAbi));
1167
+
1168
+ // Export constants
1169
+ exports.Set("constants", InitConstants(env));
1170
+
1171
+ return exports;
1172
+ }
1173
+
1174
+ NODE_API_MODULE(librats, Init)