simplex-chat 0.2.1 → 6.5.0-beta.10

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 (116) hide show
  1. package/README.md +108 -21
  2. package/binding.gyp +50 -0
  3. package/cpp/simplex.cc +426 -0
  4. package/cpp/simplex.h +46 -0
  5. package/dist/api.d.ts +423 -0
  6. package/dist/api.js +857 -0
  7. package/dist/api.js.map +1 -0
  8. package/dist/bot.d.ts +29 -0
  9. package/dist/bot.js +189 -0
  10. package/dist/bot.js.map +1 -0
  11. package/dist/core.d.ts +128 -0
  12. package/dist/core.js +121 -0
  13. package/dist/core.js.map +1 -0
  14. package/dist/index.d.ts +19 -2
  15. package/dist/index.js +20 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/simplex.d.ts +10 -0
  18. package/dist/simplex.js +1 -0
  19. package/dist/util.d.ts +15 -0
  20. package/dist/util.js +91 -0
  21. package/dist/util.js.map +1 -0
  22. package/docs/Namespace.api.md +33 -0
  23. package/docs/Namespace.bot.md +23 -0
  24. package/docs/Namespace.core.md +48 -0
  25. package/docs/Namespace.util.md +25 -0
  26. package/docs/README.md +12 -0
  27. package/docs/api.Class.ChatApi.md +1752 -0
  28. package/docs/api.Class.ChatCommandError.md +205 -0
  29. package/docs/api.Enumeration.ConnReqType.md +27 -0
  30. package/docs/api.Interface.BotAddressSettings.md +60 -0
  31. package/docs/api.TypeAlias.DbConfig.md +64 -0
  32. package/docs/api.TypeAlias.EventSubscriberFunc.md +27 -0
  33. package/docs/api.TypeAlias.EventSubscribers.md +11 -0
  34. package/docs/api.Variable.defaultBotAddressSettings.md +11 -0
  35. package/docs/bot.Function.run.md +21 -0
  36. package/docs/bot.Interface.BotConfig.md +75 -0
  37. package/docs/bot.Interface.BotOptions.md +81 -0
  38. package/docs/bot.TypeAlias.BotDbOpts.md +17 -0
  39. package/docs/core.Class.ChatAPIError.md +205 -0
  40. package/docs/core.Class.ChatInitError.md +205 -0
  41. package/docs/core.DBMigrationError.Interface.ErrorMigration.md +41 -0
  42. package/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md +33 -0
  43. package/docs/core.DBMigrationError.Interface.ErrorSQL.md +41 -0
  44. package/docs/core.DBMigrationError.Interface.InvalidConfirmation.md +25 -0
  45. package/docs/core.DBMigrationError.TypeAlias.Tag.md +11 -0
  46. package/docs/core.Enumeration.MigrationConfirmation.md +43 -0
  47. package/docs/core.Function.chatCloseStore.md +23 -0
  48. package/docs/core.Function.chatDecryptFile.md +31 -0
  49. package/docs/core.Function.chatEncryptFile.md +31 -0
  50. package/docs/core.Function.chatMigrateInit.md +31 -0
  51. package/docs/core.Function.chatReadFile.md +27 -0
  52. package/docs/core.Function.chatRecvMsgWait.md +27 -0
  53. package/docs/core.Function.chatSendCmd.md +27 -0
  54. package/docs/core.Function.chatWriteFile.md +31 -0
  55. package/docs/core.Interface.APIResult.md +31 -0
  56. package/docs/core.Interface.CryptoArgs.md +27 -0
  57. package/docs/core.Interface.UpMigration.md +25 -0
  58. package/docs/core.MTRError.Interface.MTREDifferent.md +33 -0
  59. package/docs/core.MTRError.Interface.MTRENoDown.md +33 -0
  60. package/docs/core.MTRError.TypeAlias.Tag.md +11 -0
  61. package/docs/core.MigrationError.Interface.MEDowngrade.md +33 -0
  62. package/docs/core.MigrationError.Interface.MEUpgrade.md +33 -0
  63. package/docs/core.MigrationError.Interface.MigrationError.md +33 -0
  64. package/docs/core.MigrationError.TypeAlias.Tag.md +11 -0
  65. package/docs/core.Namespace.DBMigrationError.md +18 -0
  66. package/docs/core.Namespace.MTRError.md +16 -0
  67. package/docs/core.Namespace.MigrationError.md +17 -0
  68. package/docs/core.TypeAlias.DBMigrationError.md +11 -0
  69. package/docs/core.TypeAlias.MTRError.md +11 -0
  70. package/docs/core.TypeAlias.MigrationError.md +11 -0
  71. package/docs/util.Function.botAddressSettings.md +21 -0
  72. package/docs/util.Function.chatInfoName.md +21 -0
  73. package/docs/util.Function.chatInfoRef.md +21 -0
  74. package/docs/util.Function.ciBotCommand.md +21 -0
  75. package/docs/util.Function.ciContentText.md +21 -0
  76. package/docs/util.Function.contactAddressStr.md +21 -0
  77. package/docs/util.Function.fromLocalProfile.md +21 -0
  78. package/docs/util.Function.reactionText.md +21 -0
  79. package/docs/util.Function.senderName.md +25 -0
  80. package/docs/util.Interface.BotCommand.md +25 -0
  81. package/examples/squaring-bot-pg.js +21 -0
  82. package/examples/squaring-bot-readme.js +17 -0
  83. package/examples/squaring-bot.ts +42 -0
  84. package/package.json +34 -37
  85. package/src/api.ts +939 -0
  86. package/src/bot.ts +214 -0
  87. package/src/core.ts +210 -0
  88. package/src/download-libs.js +239 -0
  89. package/src/index.ts +22 -0
  90. package/src/simplex.d.ts +10 -0
  91. package/src/simplex.js +1 -0
  92. package/src/util.ts +92 -0
  93. package/tsconfig.json +23 -0
  94. package/dist/client.d.ts +0 -76
  95. package/dist/client.js +0 -329
  96. package/dist/client.js.map +0 -1
  97. package/dist/command.d.ts +0 -412
  98. package/dist/command.js +0 -186
  99. package/dist/command.js.map +0 -1
  100. package/dist/index-web.d.ts +0 -1
  101. package/dist/index-web.js +0 -6
  102. package/dist/index-web.js.map +0 -1
  103. package/dist/index.bundle.js +0 -11
  104. package/dist/queue.d.ts +0 -24
  105. package/dist/queue.js +0 -77
  106. package/dist/queue.js.map +0 -1
  107. package/dist/response.d.ts +0 -755
  108. package/dist/response.js +0 -21
  109. package/dist/response.js.map +0 -1
  110. package/dist/test.html +0 -7
  111. package/dist/transport.d.ts +0 -51
  112. package/dist/transport.js +0 -131
  113. package/dist/transport.js.map +0 -1
  114. package/dist/websocket.d.ts +0 -8
  115. package/dist/websocket.js +0 -4
  116. package/dist/websocket.js.map +0 -1
package/README.md CHANGED
@@ -1,42 +1,129 @@
1
- # SimpleX Chat JavaScript client
1
+ # SimpleX Chat Node.js library
2
2
 
3
- This is a TypeScript library that defines WebSocket API client for [SimpleX Chat terminal CLI](https://github.com/simplex-chat/simplex-chat/blob/stable/docs/CLI.md) that should be run as a WebSockets server on any port:
3
+ This library replaced now deprecated [SimpleX Chat WebRTC TypeScript client](https://www.npmjs.com/package/@simplex-chat/webrtc-client).
4
4
 
5
- ```bash
6
- simplex-chat -p 5225
5
+ ## Use cases
6
+
7
+ - chat bots: you can implement any logic of connecting with and communicating with SimpleX Chat users. Using chat groups a chat bot can connect SimpleX Chat users with each other.
8
+ - control of the equipment: e.g. servers or home automation. SimpleX Chat provides secure and authorised connections, so this is more secure than using rest APIs.
9
+ - any scenarios of scripted message sending.
10
+ - chat and chat-based interfaces.
11
+
12
+ Please share your use cases and implementations.
13
+
14
+ ## Quick start: a simple bot
15
+
16
+ ```
17
+ npm i simplex-chat@6.5.0-beta.10
7
18
  ```
8
19
 
9
- Client API provides types and functions to:
20
+ Simple bot that replies with squares of numbers you send to it:
10
21
 
11
- - create and change user profile (although, in most cases you can do it manually, via SimpleX Chat terminal app).
12
- - create and accept invitations or connect with the contacts.
13
- - create and manage long-term user address, accepting connection requests automatically.
14
- - create, join and manage group.
15
- - send and receive files.
22
+ ```javascript
23
+ (async () => {
24
+ const {bot} = await import("simplex-chat")
25
+ // if you are running from this GitHub repo:
26
+ // const {bot} = await import("../dist/index.js")
27
+ const [chat, _user, _address] = await bot.run({
28
+ profile: {displayName: "Squaring bot example", fullName: ""},
29
+ dbOpts: {type: "sqlite", filePrefix: "./squaring_bot"},
30
+ options: {
31
+ addressSettings: {welcomeMessage: "Send a number, I will square it.",
32
+ },
33
+ onMessage: async (ci, content) => {
34
+ const n = +content.text
35
+ const reply = typeof n === "number" && !isNaN(n)
36
+ ? `${n} * ${n} = ${n * n}`
37
+ : `this is not a number`
38
+ await chat.apiSendTextReply(ci, reply)
39
+ }
40
+ })
41
+ })()
42
+ ```
16
43
 
17
- ## Use cases
44
+ If you installed this package as dependency, you can run this example with:
18
45
 
19
- - chat bots: you can implement any logic of connecting with and communicating with SimpleX Chat users. Using chat groups a chat bot can connect SimleX Chat users with each other.
20
- - control of the equipment: e.g. servers or home automation. SimpleX Chat provides secure and authorised connections, so this is more secure than using rest APIs.
46
+ ```sh
47
+ node ./node_modules/simplex-chat/examples/squaring-bot-readme.js
48
+ ```
21
49
 
22
- Please share your use cases and implementations.
50
+ If you run it on Mac, the first time it will take 20-30 seconds for MacOS to verify the library.
23
51
 
24
- ## Quick start
52
+ If you cloned this repository, you can:
25
53
 
26
54
  ```
27
- npm i simplex-chat
55
+ cd ./packages/simplex-chat-nodejs
56
+ npm install
28
57
  npm run build
58
+ node ./examples/squaring-bot-readme.js
29
59
  ```
30
60
 
31
- See the example of a simple chat bot in [squaring-bot.js](./examples/squaring-bot.js):
61
+ There is an example with more options in [./examples/squaring-bot.ts](./examples/squaring-bot.ts).
62
+
63
+ You can run it with: `npx ts-node ./examples/squaring-bot.ts`
64
+
65
+ ## PostgreSQL backend
32
66
 
33
- - start `simplex-chat` as a server on port 5225: `simplex-chat -p 5225 -d test_db`
34
- - run chatbot: `node examples/squaring-bot`
35
- - connect to chatbot via SimpleX Chat client using the address of the chat bot
67
+ By default, the package uses SQLite. To use PostgreSQL instead:
68
+
69
+ ```bash
70
+ npm install simplex-chat --simplex_backend=postgres
71
+ ```
72
+
73
+ Or persist the setting in `.npmrc`:
74
+
75
+ ```ini
76
+ simplex_backend=postgres
77
+ ```
78
+
79
+ ### Prerequisites (PostgreSQL)
80
+
81
+ - `libpq5` must be installed on the host system (`apt install libpq5` on Debian/Ubuntu)
82
+ - PostgreSQL backend is only available for Linux x86_64
83
+ - A PostgreSQL server accessible via connection string
84
+
85
+ ### Passing PostgreSQL connection
86
+
87
+ The `DbConfig` type is a discriminated union — pick the variant that matches
88
+ the backend you installed:
89
+
90
+ ```ts
91
+ // SQLite (default)
92
+ dbOpts: {type: "sqlite", filePrefix: "./data/bot"}
93
+ // optional: encryptionKey: "<sqlcipher-key>"
94
+
95
+ // PostgreSQL
96
+ dbOpts: {
97
+ type: "postgres",
98
+ connectionString: "postgres://user:pass@host/db",
99
+ // schemaPrefix: "bot", // optional — defaults to "simplex_v1"
100
+ }
101
+ ```
36
102
 
37
103
  ## Documentation
38
104
 
39
- Please refer to the available client API in [client.ts](./src/client.ts).
105
+ The library docs are [here](./docs/README.md).
106
+
107
+ Library provides these modules:
108
+ - [bot](./docs/Namespace.bot.md): a simple declarative API to run a chat-bot with a single function call. It automates creating and updating of the bot profile, address and bot commands shown in the app UI.
109
+ - [api](./docs/Namespace.api.md): an API to send chat commands and receive chat events to/from chat core. You need to use it in bot event handlers, and for any other use cases.
110
+ - [core](./docs/Namespace.core.md): a low level API to the core library - the same that is used in desktop clients. You are unlikely to ever need to use this module directly.
111
+ - [util](./docs/Namespace.util.md): useful functions for chat events and types.
112
+
113
+
114
+ This library uses [@simplex-chat/types](https://www.npmjs.com/package/@simplex-chat/types) package with auto-generated [bot API types](../../bots/api/README.md).
115
+
116
+ ## Supported chat functions
117
+
118
+ Library provides types and functions to:
119
+
120
+ - create and change user profile (although, in most cases you can do it manually, via SimpleX Chat terminal app).
121
+ - create and accept invitations or connect with the contacts.
122
+ - create and manage long-term user address, accepting connection requests automatically.
123
+ - send, receive, delete and update messages, and add message reactions.
124
+ - create, join and manage group.
125
+ - send and receive files.
126
+ - etc.
40
127
 
41
128
  ## License
42
129
 
package/binding.gyp ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "simplex",
5
+ "sources": [ "cpp/simplex.cc" ],
6
+ "include_dirs": [
7
+ "<!@(node -p \"require('node-addon-api').include\")"
8
+ ],
9
+ "dependencies": [
10
+ "<!(node -p \"require('node-addon-api').gyp\")"
11
+ ],
12
+ "cflags!": [ "-fno-exceptions" ],
13
+ "cflags_cc!": [ "-fno-exceptions" ],
14
+ "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
15
+ "conditions": [
16
+ ["OS=='mac'", {
17
+ "libraries": [
18
+ "-L<(module_root_dir)/libs",
19
+ "-lsimplex"
20
+ ],
21
+ "xcode_settings": {
22
+ "OTHER_LDFLAGS": [
23
+ "-Wl,-rpath,@loader_path/../../libs"
24
+ ]
25
+ }
26
+ }],
27
+ ["OS=='linux'", {
28
+ "libraries": [
29
+ "-L<(module_root_dir)/libs",
30
+ "-lsimplex"
31
+ ],
32
+ "ldflags": [
33
+ "-Wl,-rpath,'$$ORIGIN'/../../libs"
34
+ ]
35
+ }],
36
+ ["OS=='win'", {
37
+ "libraries": [
38
+ "<(module_root_dir)/libs/libsimplex.lib"
39
+ ],
40
+ "copies": [{
41
+ "destination": "<(PRODUCT_DIR)",
42
+ "files": [
43
+ "<(module_root_dir)/libs/*"
44
+ ]
45
+ }]
46
+ }]
47
+ ]
48
+ }
49
+ ]
50
+ }
package/cpp/simplex.cc ADDED
@@ -0,0 +1,426 @@
1
+ #include <napi.h>
2
+ #include <sstream>
3
+ #include <string>
4
+ #include <functional>
5
+ #include <cstdlib>
6
+ #include "simplex.h"
7
+
8
+ namespace simplex {
9
+
10
+ using namespace Napi;
11
+
12
+ void haskell_init() {
13
+ #ifdef _WIN32
14
+ // non-moving GC is broken on windows with GHC 9.4-9.6.3
15
+ int argc = 5;
16
+ const char *argv[] = {
17
+ "simplex",
18
+ "+RTS", // requires `hs_init_with_rtsopts`
19
+ "-A64m", // chunk size for new allocations
20
+ "-H64m", // initial heap size
21
+ "--install-signal-handlers=no",
22
+ nullptr};
23
+ #else
24
+ int argc = 6;
25
+ const char *argv[] = {
26
+ "simplex",
27
+ "+RTS", // requires `hs_init_with_rtsopts`
28
+ "-A64m", // chunk size for new allocations
29
+ "-H64m", // initial heap size
30
+ "-xn", // non-moving GC
31
+ "--install-signal-handlers=no",
32
+ nullptr};
33
+ #endif
34
+ char **pargv = const_cast<char **>(argv);
35
+ hs_init_with_rtsopts(&argc, &pargv);
36
+ }
37
+
38
+ class ResultAsyncWorker : public AsyncWorker {
39
+ public:
40
+ using ExecuteFn = std::function<void(ResultAsyncWorker*)>;
41
+ using ResultProcessor = std::function<void(ResultAsyncWorker*, Napi::Env)>;
42
+
43
+ ResultAsyncWorker(Function& callback, ExecuteFn execute_fn, ResultProcessor result_processor = nullptr)
44
+ : AsyncWorker(callback), execute_fn_(std::move(execute_fn)), result_processor_(std::move(result_processor)) {}
45
+
46
+ void Execute() override {
47
+ execute_fn_(this);
48
+ }
49
+
50
+ void OnOK() override {
51
+ HandleScope scope(Env());
52
+ if (result_processor_) {
53
+ result_processor_(this, Env());
54
+ } else {
55
+ Callback().Call({Env().Null(), String::New(Env(), result_)});
56
+ }
57
+ }
58
+
59
+ void OnError(const Error& e) override {
60
+ HandleScope scope(Env());
61
+ Callback().Call({e.Value(), Env().Undefined()});
62
+ }
63
+
64
+ void SetResult(std::string result) {
65
+ result_ = std::move(result);
66
+ }
67
+
68
+ void SetWorkerError(const std::string& msg) {
69
+ SetError(msg);
70
+ }
71
+
72
+ const std::string& GetStringResult() const {
73
+ return result_;
74
+ }
75
+
76
+ void SetCtrl(uintptr_t ctrl) {
77
+ ctrl_ = ctrl;
78
+ }
79
+
80
+ uintptr_t GetCtrl() const {
81
+ return ctrl_;
82
+ }
83
+
84
+ protected:
85
+ std::string result_;
86
+ uintptr_t ctrl_ = 0;
87
+
88
+ private:
89
+ ExecuteFn execute_fn_;
90
+ ResultProcessor result_processor_;
91
+ };
92
+
93
+ class BinaryAsyncWorker : public AsyncWorker {
94
+ public:
95
+ using ExecuteFn = std::function<void(BinaryAsyncWorker*)>;
96
+
97
+ BinaryAsyncWorker(Function& callback, ExecuteFn execute_fn)
98
+ : AsyncWorker(callback), execute_fn_(std::move(execute_fn)) {}
99
+
100
+ void Execute() override {
101
+ execute_fn_(this);
102
+ }
103
+
104
+ void OnOK() override {
105
+ HandleScope scope(Env());
106
+ if (original_buf == nullptr || binary_len == 0) {
107
+ Callback().Call({Env().Null(), Env().Undefined()});
108
+ return;
109
+ }
110
+ char* data_ptr = original_buf + 5;
111
+ auto finalizer = [](Napi::Env env, char* finalize_data, char* orig) {
112
+ free(orig);
113
+ };
114
+ Napi::Buffer<char> buffer = Napi::Buffer<char>::New(Env(), data_ptr, binary_len, finalizer, original_buf);
115
+ Callback().Call({Env().Null(), buffer});
116
+ }
117
+
118
+ void OnError(const Error& e) override {
119
+ HandleScope scope(Env());
120
+ Callback().Call({e.Value(), Env().Undefined()});
121
+ }
122
+
123
+ void SetWorkerError(const std::string& msg) {
124
+ SetError(msg);
125
+ }
126
+
127
+ char* original_buf = nullptr;
128
+ size_t binary_len = 0;
129
+
130
+ private:
131
+ ExecuteFn execute_fn_;
132
+ };
133
+
134
+ // Helper for converting chat_ctrl pointer to BigInt
135
+ Napi::BigInt ToChatCtrlBigInt(Napi::Env env, uintptr_t ctrl) {
136
+ return Napi::BigInt::New(env, static_cast<uint64_t>(ctrl));
137
+ }
138
+
139
+ // Helper for converting BigInt to chat_ctrl pointer
140
+ chat_ctrl FromChatCtrlBigInt(const Napi::Value& value) {
141
+ Napi::Env env = value.Env();
142
+ if (!value.IsBigInt()) {
143
+ Napi::TypeError::New(env, "Expected BigInt for ctrl").ThrowAsJavaScriptException();
144
+ return nullptr;
145
+ }
146
+ Napi::BigInt big = value.As<Napi::BigInt>();
147
+ bool lossless;
148
+ uint64_t val = big.Uint64Value(&lossless);
149
+ if (!lossless) {
150
+ Napi::TypeError::New(env, "BigInt too large for ctrl").ThrowAsJavaScriptException();
151
+ return nullptr;
152
+ }
153
+ return reinterpret_cast<chat_ctrl>(val);
154
+ }
155
+
156
+ // Helper for handling common C result patterns (no empty check)
157
+ void HandleCResult(ResultAsyncWorker* worker, char* c_res, const std::string& func_name) {
158
+ if (c_res == nullptr) {
159
+ worker->SetWorkerError(func_name + " failed");
160
+ return;
161
+ }
162
+ std::string res = c_res;
163
+ free(c_res);
164
+ worker->SetResult(res);
165
+ }
166
+
167
+ Napi::Promise CreatePromiseAndCallback(Env env, Function& cb_out) {
168
+ Promise::Deferred deferred = Promise::Deferred::New(env);
169
+ cb_out = Function::New(env, [deferred](const CallbackInfo& args) {
170
+ if (!args[0].IsNull() && !args[0].IsUndefined()) {
171
+ deferred.Reject(args[0]);
172
+ } else {
173
+ deferred.Resolve(args[1]);
174
+ }
175
+ });
176
+ return deferred.Promise();
177
+ }
178
+
179
+ // Common result processors
180
+ ResultAsyncWorker::ResultProcessor MigrateResultProcessor() {
181
+ return [](ResultAsyncWorker* worker, Napi::Env env) {
182
+ Napi::Array arr = Napi::Array::New(env, 2);
183
+ arr.Set(0u, ToChatCtrlBigInt(env, worker->GetCtrl()));
184
+ arr.Set(1u, Napi::String::New(env, worker->GetStringResult()));
185
+ worker->Callback().Call({env.Null(), arr});
186
+ };
187
+ }
188
+
189
+ // Refactored functions using common patterns
190
+
191
+ Value ChatMigrateInit(const CallbackInfo& args) {
192
+ Env env = args.Env();
193
+ if (args.Length() < 3 || !args[0].IsString() || !args[1].IsString() || !args[2].IsString()) {
194
+ TypeError::New(env, "Expected three string arguments").ThrowAsJavaScriptException();
195
+ return env.Undefined();
196
+ }
197
+
198
+ std::string path = args[0].As<String>().Utf8Value();
199
+ std::string key = args[1].As<String>().Utf8Value();
200
+ std::string confirm = args[2].As<String>().Utf8Value();
201
+
202
+ Function cb;
203
+ Promise promise = CreatePromiseAndCallback(env, cb);
204
+
205
+ auto execute_fn = [path, key, confirm](ResultAsyncWorker* worker) {
206
+ chat_ctrl ctrl = nullptr;
207
+ char* c_res = chat_migrate_init(path.c_str(), key.c_str(), confirm.c_str(), &ctrl);
208
+ worker->SetCtrl(reinterpret_cast<uintptr_t>(ctrl));
209
+ HandleCResult(worker, c_res, "chat_migrate_init");
210
+ };
211
+
212
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn), MigrateResultProcessor());
213
+ worker->Queue();
214
+
215
+ return promise;
216
+ }
217
+
218
+ Value ChatCloseStore(const CallbackInfo& args) {
219
+ Env env = args.Env();
220
+ if (args.Length() < 1 || !args[0].IsBigInt()) {
221
+ TypeError::New(env, "Expected bigint (ctrl)").ThrowAsJavaScriptException();
222
+ return env.Undefined();
223
+ }
224
+
225
+ chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
226
+
227
+ Function cb;
228
+ Promise promise = CreatePromiseAndCallback(env, cb);
229
+
230
+ auto execute_fn = [ctrl](ResultAsyncWorker* worker) {
231
+ char* c_res = chat_close_store(ctrl);
232
+ HandleCResult(worker, c_res, "chat_close_store");
233
+ };
234
+
235
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
236
+ worker->Queue();
237
+
238
+ return promise;
239
+ }
240
+
241
+ Value ChatSendCmd(const CallbackInfo& args) {
242
+ Env env = args.Env();
243
+ if (args.Length() < 2 || !args[0].IsBigInt() || !args[1].IsString()) {
244
+ TypeError::New(env, "Expected bigint (ctrl) and string (cmd)").ThrowAsJavaScriptException();
245
+ return env.Undefined();
246
+ }
247
+
248
+ chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
249
+ std::string cmd = args[1].As<String>().Utf8Value();
250
+
251
+ Function cb;
252
+ Promise promise = CreatePromiseAndCallback(env, cb);
253
+
254
+ auto execute_fn = [ctrl, cmd](ResultAsyncWorker* worker) {
255
+ char* c_res = chat_send_cmd(ctrl, cmd.c_str());
256
+ HandleCResult(worker, c_res, "chat_send_cmd");
257
+ };
258
+
259
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
260
+ worker->Queue();
261
+
262
+ return promise;
263
+ }
264
+
265
+ Value ChatRecvMsgWait(const CallbackInfo& args) {
266
+ Env env = args.Env();
267
+ if (args.Length() < 2 || !args[0].IsBigInt() || !args[1].IsNumber()) {
268
+ TypeError::New(env, "Expected bigint (ctrl), number (wait)").ThrowAsJavaScriptException();
269
+ return env.Undefined();
270
+ }
271
+
272
+ chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
273
+ int wait = static_cast<int>(args[1].As<Number>().Int32Value());
274
+
275
+ Function cb;
276
+ Promise promise = CreatePromiseAndCallback(env, cb);
277
+
278
+ auto execute_fn = [ctrl, wait](ResultAsyncWorker* worker) {
279
+ char* c_res = chat_recv_msg_wait(ctrl, wait);
280
+ HandleCResult(worker, c_res, "chat_recv_msg_wait");
281
+ };
282
+
283
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
284
+ worker->Queue();
285
+
286
+ return promise;
287
+ }
288
+
289
+ Value ChatWriteFile(const CallbackInfo& args) {
290
+ Env env = args.Env();
291
+ if (args.Length() < 3 || !args[0].IsBigInt() || !args[1].IsString() || !args[2].IsArrayBuffer()) {
292
+ TypeError::New(env, "Expected bigint (ctrl), string (path), ArrayBuffer").ThrowAsJavaScriptException();
293
+ return env.Undefined();
294
+ }
295
+
296
+ chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
297
+ std::string path = args[1].As<String>().Utf8Value();
298
+ ArrayBuffer ab = args[2].As<ArrayBuffer>();
299
+ char* data = static_cast<char*>(ab.Data());
300
+ size_t len = ab.ByteLength();
301
+
302
+ Function cb;
303
+ Promise promise = CreatePromiseAndCallback(env, cb);
304
+
305
+ auto execute_fn = [ctrl, path, ab, data, len](ResultAsyncWorker* worker) {
306
+ (void)ab; // to keep ArrayBuffer alive
307
+ char* c_res = chat_write_file(ctrl, path.c_str(), data, static_cast<int>(len));
308
+ HandleCResult(worker, c_res, "chat_write_file");
309
+ };
310
+
311
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
312
+ worker->Queue();
313
+
314
+ return promise;
315
+ }
316
+
317
+ Value ChatReadFile(const CallbackInfo& args) {
318
+ Env env = args.Env();
319
+ if (args.Length() < 3 || !args[0].IsString() || !args[1].IsString() || !args[2].IsString()) {
320
+ TypeError::New(env, "Expected three strings (path, key, nonce)").ThrowAsJavaScriptException();
321
+ return env.Undefined();
322
+ }
323
+
324
+ std::string path = args[0].As<String>().Utf8Value();
325
+ std::string key = args[1].As<String>().Utf8Value();
326
+ std::string nonce = args[2].As<String>().Utf8Value();
327
+
328
+ Function cb;
329
+ Promise promise = CreatePromiseAndCallback(env, cb);
330
+
331
+ auto execute_fn = [path, key, nonce](BinaryAsyncWorker* worker) {
332
+ char* buf = chat_read_file(path.c_str(), key.c_str(), nonce.c_str());
333
+ if (buf == nullptr) {
334
+ worker->SetWorkerError("chat_read_file failed");
335
+ return;
336
+ }
337
+ char status = buf[0];
338
+ if (status == 1) {
339
+ std::string err = buf + 1;
340
+ free(buf);
341
+ worker->SetWorkerError(err);
342
+ return;
343
+ } else if (status == 0) {
344
+ uint32_t len = *(uint32_t*)(buf + 1);
345
+ worker->original_buf = buf;
346
+ worker->binary_len = len;
347
+ } else {
348
+ free(buf);
349
+ worker->SetWorkerError("Unexpected status from chat_read_file");
350
+ return;
351
+ }
352
+ };
353
+
354
+ BinaryAsyncWorker* worker = new BinaryAsyncWorker(cb, std::move(execute_fn));
355
+ worker->Queue();
356
+
357
+ return promise;
358
+ }
359
+
360
+ Value ChatEncryptFile(const CallbackInfo& args) {
361
+ Env env = args.Env();
362
+ if (args.Length() < 3 || !args[0].IsBigInt() || !args[1].IsString() || !args[2].IsString()) {
363
+ TypeError::New(env, "Expected bigint (ctrl), two strings (fromPath, toPath)").ThrowAsJavaScriptException();
364
+ return env.Undefined();
365
+ }
366
+
367
+ chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
368
+ std::string fromPath = args[1].As<String>().Utf8Value();
369
+ std::string toPath = args[2].As<String>().Utf8Value();
370
+
371
+ Function cb;
372
+ Promise promise = CreatePromiseAndCallback(env, cb);
373
+
374
+ auto execute_fn = [ctrl, fromPath, toPath](ResultAsyncWorker* worker) {
375
+ char* c_res = chat_encrypt_file(ctrl, fromPath.c_str(), toPath.c_str());
376
+ HandleCResult(worker, c_res, "chat_encrypt_file");
377
+ };
378
+
379
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
380
+ worker->Queue();
381
+
382
+ return promise;
383
+ }
384
+
385
+ Value ChatDecryptFile(const CallbackInfo& args) {
386
+ Env env = args.Env();
387
+ if (args.Length() < 4 || !args[0].IsString() || !args[1].IsString() || !args[2].IsString() || !args[3].IsString()) {
388
+ TypeError::New(env, "Expected four strings (fromPath, key, nonce, toPath)").ThrowAsJavaScriptException();
389
+ return env.Undefined();
390
+ }
391
+
392
+ std::string fromPath = args[0].As<String>().Utf8Value();
393
+ std::string key = args[1].As<String>().Utf8Value();
394
+ std::string nonce = args[2].As<String>().Utf8Value();
395
+ std::string toPath = args[3].As<String>().Utf8Value();
396
+
397
+ Function cb;
398
+ Promise promise = CreatePromiseAndCallback(env, cb);
399
+
400
+ auto execute_fn = [fromPath, key, nonce, toPath](ResultAsyncWorker* worker) {
401
+ char* c_res = chat_decrypt_file(fromPath.c_str(), key.c_str(), nonce.c_str(), toPath.c_str());
402
+ HandleCResult(worker, c_res, "chat_decrypt_file");
403
+ };
404
+
405
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
406
+ worker->Queue();
407
+
408
+ return promise;
409
+ }
410
+
411
+ Object Init(Env env, Object exports) {
412
+ haskell_init();
413
+ exports.Set("chat_migrate_init", Function::New(env, ChatMigrateInit));
414
+ exports.Set("chat_close_store", Function::New(env, ChatCloseStore));
415
+ exports.Set("chat_send_cmd", Function::New(env, ChatSendCmd));
416
+ exports.Set("chat_recv_msg_wait", Function::New(env, ChatRecvMsgWait));
417
+ exports.Set("chat_write_file", Function::New(env, ChatWriteFile));
418
+ exports.Set("chat_read_file", Function::New(env, ChatReadFile));
419
+ exports.Set("chat_encrypt_file", Function::New(env, ChatEncryptFile));
420
+ exports.Set("chat_decrypt_file", Function::New(env, ChatDecryptFile));
421
+ return exports;
422
+ }
423
+
424
+ NODE_API_MODULE(simplex, Init)
425
+
426
+ }
package/cpp/simplex.h ADDED
@@ -0,0 +1,46 @@
1
+ //
2
+ // simplex.h
3
+ // SimpleX
4
+ //
5
+ // Created by Evgeny on 30/05/2022.
6
+ // Copyright © 2022 SimpleX Chat. All rights reserved.
7
+ //
8
+
9
+ #ifndef SimpleX_h
10
+ #define SimpleX_h
11
+
12
+ extern "C" void hs_init(int argc, char **argv[]);
13
+ extern "C" void hs_init_with_rtsopts(int * argc, char **argv[]);
14
+
15
+ typedef long* chat_ctrl;
16
+
17
+ // the last parameter is used to return the pointer to chat controller
18
+ extern "C" char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
19
+ extern "C" char *chat_close_store(chat_ctrl ctrl);
20
+ extern "C" char *chat_reopen_store(chat_ctrl ctrl);
21
+ extern "C" char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
22
+ extern "C" char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
23
+ extern "C" char *chat_parse_markdown(const char *str);
24
+ extern "C" char *chat_parse_server(const char *str);
25
+ extern "C" char *chat_password_hash(const char *pwd, const char *salt);
26
+ extern "C" char *chat_valid_name(const char *name);
27
+ extern "C" int chat_json_length(const char *str);
28
+ extern "C" char *chat_encrypt_media(chat_ctrl ctrl, const char *key, const char *frame, const int len);
29
+ extern "C" char *chat_decrypt_media(const char *key, const char *frame, const int len);
30
+
31
+ // chat_write_file returns null-terminated string with JSON of WriteFileResult
32
+ extern "C" char *chat_write_file(chat_ctrl ctrl, const char *path, const char *data, const int len);
33
+
34
+ // chat_read_file returns a buffer with:
35
+ // result status (1 byte), then if
36
+ // status == 0 (success): buffer length (uint32, 4 bytes), buffer of specified length.
37
+ // status == 1 (error): null-terminated error message string.
38
+ extern "C" char *chat_read_file(const char *path, const char *key, const char *nonce);
39
+
40
+ // chat_encrypt_file returns null-terminated string with JSON of WriteFileResult
41
+ extern "C" char *chat_encrypt_file(chat_ctrl ctrl, const char *fromPath, const char *toPath);
42
+
43
+ // chat_decrypt_file returns null-terminated string with the error message
44
+ extern "C" char *chat_decrypt_file(const char *fromPath, const char *key, const char *nonce, const char *toPath);
45
+
46
+ #endif /* simplex_h */