simplex-chat 7.0.2 → 7.1.0-beta.4

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 (56) hide show
  1. package/binding.gyp +2 -0
  2. package/cpp/simplex.cc +265 -29
  3. package/cpp/simplex.h +2 -0
  4. package/dist/api.d.ts +3 -64
  5. package/dist/api.js +8 -106
  6. package/dist/api.js.map +1 -1
  7. package/dist/bot.d.ts +4 -2
  8. package/dist/bot.js +1 -1
  9. package/dist/bot.js.map +1 -1
  10. package/dist/util.js +1 -1
  11. package/dist/util.js.map +1 -1
  12. package/docs/Namespace.bot.md +1 -0
  13. package/docs/api.Class.ChatApi.md +122 -78
  14. package/docs/bot.Function.run.md +1 -1
  15. package/docs/bot.Function.subscribeChatItems.md +27 -0
  16. package/docs/bot.Interface.BotConfig.md +7 -7
  17. package/docs/bot.Interface.BotOptions.md +10 -10
  18. package/docs/bot.TypeAlias.BotDbOpts.md +4 -0
  19. package/docs/core.Class.ChatAPIError.md +4 -4
  20. package/docs/core.Class.ChatInitError.md +4 -4
  21. package/docs/core.DBMigrationError.Interface.ErrorMigration.md +4 -4
  22. package/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md +3 -3
  23. package/docs/core.DBMigrationError.Interface.ErrorSQL.md +4 -4
  24. package/docs/core.DBMigrationError.Interface.InvalidConfirmation.md +2 -2
  25. package/docs/core.DBMigrationError.Interface.InvalidQueueSize.md +25 -0
  26. package/docs/core.DBMigrationError.TypeAlias.Tag.md +2 -2
  27. package/docs/core.Enumeration.MigrationConfirmation.md +5 -5
  28. package/docs/core.Function.chatCloseStore.md +1 -1
  29. package/docs/core.Function.chatDecryptFile.md +1 -1
  30. package/docs/core.Function.chatEncryptFile.md +1 -1
  31. package/docs/core.Function.chatMigrateInit.md +8 -2
  32. package/docs/core.Function.chatReadFile.md +3 -3
  33. package/docs/core.Function.chatRecvMsgWait.md +1 -1
  34. package/docs/core.Function.chatSendCmd.md +1 -1
  35. package/docs/core.Function.chatWriteFile.md +2 -2
  36. package/docs/core.Interface.APIResult.md +3 -3
  37. package/docs/core.Interface.CryptoArgs.md +3 -3
  38. package/docs/core.Interface.UpMigration.md +3 -3
  39. package/docs/core.MTRError.Interface.MTREDifferent.md +3 -3
  40. package/docs/core.MTRError.Interface.MTRENoDown.md +10 -10
  41. package/docs/core.MTRError.TypeAlias.Tag.md +1 -1
  42. package/docs/core.MigrationError.Interface.MEDowngrade.md +3 -3
  43. package/docs/core.MigrationError.Interface.MEUpgrade.md +4 -4
  44. package/docs/core.MigrationError.Interface.MigrationError.md +3 -3
  45. package/docs/core.MigrationError.TypeAlias.Tag.md +1 -1
  46. package/docs/core.Namespace.DBMigrationError.md +1 -0
  47. package/docs/core.TypeAlias.DBMigrationError.md +2 -2
  48. package/docs/core.TypeAlias.MTRError.md +1 -1
  49. package/docs/core.TypeAlias.MigrationError.md +1 -1
  50. package/package.json +2 -2
  51. package/src/api.ts +40 -22
  52. package/src/bot.ts +17 -30
  53. package/src/core.ts +15 -7
  54. package/src/download-libs.js +28 -1
  55. package/src/simplex.d.ts +3 -2
  56. package/src/util.ts +1 -1
package/binding.gyp CHANGED
@@ -11,6 +11,8 @@
11
11
  ],
12
12
  "cflags!": [ "-fno-exceptions" ],
13
13
  "cflags_cc!": [ "-fno-exceptions" ],
14
+ "xcode_settings": { "GCC_ENABLE_CPP_EXCEPTIONS": "YES" },
15
+ "msvs_settings": { "VCCLCompilerTool": { "ExceptionHandling": 1 } },
14
16
  "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
15
17
  "conditions": [
16
18
  ["OS=='mac'", {
package/cpp/simplex.cc CHANGED
@@ -3,6 +3,14 @@
3
3
  #include <string>
4
4
  #include <functional>
5
5
  #include <cstdlib>
6
+ #include <climits>
7
+ #include <memory>
8
+ #include <thread>
9
+ #include <system_error>
10
+ #include <mutex>
11
+ #include <condition_variable>
12
+ #include <deque>
13
+ #include <unordered_map>
6
14
  #include "simplex.h"
7
15
 
8
16
  namespace simplex {
@@ -81,6 +89,11 @@ class ResultAsyncWorker : public AsyncWorker {
81
89
  return ctrl_;
82
90
  }
83
91
 
92
+ // the worker thread reads this object's memory until the worker completes
93
+ void KeepAlive(Object obj) {
94
+ keep_alive_ = Persistent(obj);
95
+ }
96
+
84
97
  protected:
85
98
  std::string result_;
86
99
  uintptr_t ctrl_ = 0;
@@ -88,6 +101,7 @@ class ResultAsyncWorker : public AsyncWorker {
88
101
  private:
89
102
  ExecuteFn execute_fn_;
90
103
  ResultProcessor result_processor_;
104
+ ObjectReference keep_alive_;
91
105
  };
92
106
 
93
107
  class BinaryAsyncWorker : public AsyncWorker {
@@ -97,21 +111,25 @@ class BinaryAsyncWorker : public AsyncWorker {
97
111
  BinaryAsyncWorker(Function& callback, ExecuteFn execute_fn)
98
112
  : AsyncWorker(callback), execute_fn_(std::move(execute_fn)) {}
99
113
 
114
+ ~BinaryAsyncWorker() {
115
+ free(original_buf);
116
+ }
117
+
100
118
  void Execute() override {
101
119
  execute_fn_(this);
102
120
  }
103
121
 
104
122
  void OnOK() override {
105
123
  HandleScope scope(Env());
106
- if (original_buf == nullptr || binary_len == 0) {
107
- Callback().Call({Env().Null(), Env().Undefined()});
124
+ char* buf = original_buf;
125
+ original_buf = nullptr;
126
+ if (binary_len == 0) {
127
+ free(buf);
128
+ Callback().Call({Env().Null(), Buffer<char>::New(Env(), 0)});
108
129
  return;
109
130
  }
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);
131
+ // Copies when the runtime forbids external buffers (Electron); the finalizer then runs immediately.
132
+ Buffer<char> buffer = Buffer<char>::NewOrCopy(Env(), buf + 5, binary_len, [](Napi::Env, char*, char* orig) { free(orig); }, buf);
115
133
  Callback().Call({Env().Null(), buffer});
116
134
  }
117
135
 
@@ -176,6 +194,159 @@ Napi::Promise CreatePromiseAndCallback(Env env, Function& cb_out) {
176
194
  return deferred.Promise();
177
195
  }
178
196
 
197
+ const char* const RECEIVER_STOPPED = "chat receiver stopped";
198
+
199
+ struct RecvRequest {
200
+ int wait = 0;
201
+ std::shared_ptr<Promise::Deferred> deferred;
202
+ };
203
+
204
+ // Holds the event loop open only while receives are pending; used only on the JS main thread.
205
+ class PendingReceives {
206
+ public:
207
+ explicit PendingReceives(ThreadSafeFunction tsfn) : tsfn_(tsfn) {}
208
+
209
+ const ThreadSafeFunction& Tsfn() const {
210
+ return tsfn_;
211
+ }
212
+
213
+ void Add(Napi::Env env) {
214
+ if (count_++ == 0) tsfn_.Ref(env);
215
+ }
216
+
217
+ void Remove(Napi::Env env) {
218
+ if (--count_ == 0) tsfn_.Unref(env);
219
+ }
220
+
221
+ private:
222
+ ThreadSafeFunction tsfn_;
223
+ size_t count_ = 0;
224
+ };
225
+
226
+ // A blocking receive would hold a libuv pool thread for up to `wait`, stalling fs, dns and crypto.
227
+ class Receiver {
228
+ public:
229
+ // Returns nullptr with a pending JS exception if the TSFN cannot be created, throws std::system_error if the thread cannot start.
230
+ static std::shared_ptr<Receiver> Start(Napi::Env env, chat_ctrl ctrl) {
231
+ ThreadSafeFunction tsfn = ThreadSafeFunction::New(env, Function::New(env, [](const CallbackInfo&) {}), "chat_recv_msg_wait", 0, 1);
232
+ if (env.IsExceptionPending()) {
233
+ return nullptr;
234
+ }
235
+ auto receiver = std::make_shared<Receiver>(ctrl, tsfn);
236
+ try {
237
+ receiver->thread_ = std::thread(&Receiver::Run, receiver.get());
238
+ } catch (const std::system_error&) {
239
+ tsfn.Release();
240
+ throw;
241
+ }
242
+ return receiver;
243
+ }
244
+
245
+ Receiver(chat_ctrl ctrl, ThreadSafeFunction tsfn) : ctrl_(ctrl), pending_(std::make_shared<PendingReceives>(tsfn)) {}
246
+
247
+ Receiver(const Receiver&) = delete;
248
+ Receiver& operator=(const Receiver&) = delete;
249
+
250
+ ~Receiver() {
251
+ Stop();
252
+ }
253
+
254
+ void Enqueue(Napi::Env env, RecvRequest request) {
255
+ pending_->Add(env);
256
+ {
257
+ std::lock_guard<std::mutex> lock(mutex_);
258
+ queue_.push_back(std::move(request));
259
+ }
260
+ cv_.notify_one();
261
+ }
262
+
263
+ void RequestStop() {
264
+ {
265
+ std::lock_guard<std::mutex> lock(mutex_);
266
+ stop_ = true;
267
+ }
268
+ cv_.notify_one();
269
+ }
270
+
271
+ // Waits for the receive in progress, so it must not run on the JS main thread outside env teardown.
272
+ void Stop() {
273
+ RequestStop();
274
+ if (thread_.joinable()) {
275
+ thread_.join();
276
+ }
277
+ }
278
+
279
+ private:
280
+ void Run() {
281
+ for (;;) {
282
+ RecvRequest request;
283
+ {
284
+ std::unique_lock<std::mutex> lock(mutex_);
285
+ cv_.wait(lock, [this] { return stop_ || !queue_.empty(); });
286
+ if (stop_) break;
287
+ request = std::move(queue_.front());
288
+ queue_.pop_front();
289
+ }
290
+ char* c_res = chat_recv_msg_wait(ctrl_, request.wait);
291
+ napi_status status = Settle(request.deferred, [c_res](Napi::Env env, Promise::Deferred& deferred) {
292
+ if (c_res == nullptr) {
293
+ deferred.Reject(Error::New(env, "chat_recv_msg_wait failed").Value());
294
+ } else {
295
+ deferred.Resolve(String::New(env, c_res));
296
+ free(c_res);
297
+ }
298
+ });
299
+ if (status != napi_ok) {
300
+ free(c_res);
301
+ }
302
+ }
303
+ std::deque<RecvRequest> unserved;
304
+ {
305
+ std::lock_guard<std::mutex> lock(mutex_);
306
+ unserved.swap(queue_);
307
+ }
308
+ for (RecvRequest& request : unserved) {
309
+ Settle(request.deferred, [](Napi::Env env, Promise::Deferred& deferred) {
310
+ deferred.Reject(Error::New(env, RECEIVER_STOPPED).Value());
311
+ });
312
+ }
313
+ pending_->Tsfn().Release();
314
+ // Each OS thread that enters Haskell keeps an RTS task record until it calls hs_thread_done.
315
+ hs_thread_done();
316
+ }
317
+
318
+ template <typename SettleFn>
319
+ napi_status Settle(std::shared_ptr<Promise::Deferred> deferred, SettleFn settle) {
320
+ // The callback may run after this Receiver is destroyed, so it owns the pending count.
321
+ std::shared_ptr<PendingReceives> pending = pending_;
322
+ return pending->Tsfn().BlockingCall([pending, deferred, settle](Napi::Env env, Function) {
323
+ settle(env, *deferred);
324
+ pending->Remove(env);
325
+ });
326
+ }
327
+
328
+ const chat_ctrl ctrl_;
329
+ const std::shared_ptr<PendingReceives> pending_;
330
+ std::mutex mutex_;
331
+ std::condition_variable cv_;
332
+ std::deque<RecvRequest> queue_;
333
+ bool stop_ = false;
334
+ std::thread thread_;
335
+ };
336
+
337
+ // Keyed by chat_ctrl, accessed only on the JS main thread.
338
+ using Receivers = std::unordered_map<uintptr_t, std::shared_ptr<Receiver>>;
339
+
340
+ std::shared_ptr<Receiver> TakeReceiver(Receivers& receivers, chat_ctrl ctrl) {
341
+ auto it = receivers.find(reinterpret_cast<uintptr_t>(ctrl));
342
+ if (it == receivers.end()) {
343
+ return nullptr;
344
+ }
345
+ std::shared_ptr<Receiver> receiver = std::move(it->second);
346
+ receivers.erase(it);
347
+ return receiver;
348
+ }
349
+
179
350
  // Common result processors
180
351
  ResultAsyncWorker::ResultProcessor MigrateResultProcessor() {
181
352
  return [](ResultAsyncWorker* worker, Napi::Env env) {
@@ -215,6 +386,39 @@ Value ChatMigrateInit(const CallbackInfo& args) {
215
386
  return promise;
216
387
  }
217
388
 
389
+ Value ChatMigrateInitQueue(const CallbackInfo& args) {
390
+ Env env = args.Env();
391
+ if (args.Length() < 4 || !args[0].IsString() || !args[1].IsString() || !args[2].IsString() || !args[3].IsNumber()) {
392
+ TypeError::New(env, "Expected three string arguments and number").ThrowAsJavaScriptException();
393
+ return env.Undefined();
394
+ }
395
+
396
+ std::string path = args[0].As<String>().Utf8Value();
397
+ std::string key = args[1].As<String>().Utf8Value();
398
+ std::string confirm = args[2].As<String>().Utf8Value();
399
+ Number queue_size_arg = args[3].As<Number>();
400
+ int queue_size = queue_size_arg.Int32Value();
401
+ if (static_cast<double>(queue_size) != queue_size_arg.DoubleValue()) {
402
+ RangeError::New(env, "Expected 32-bit integer queue size").ThrowAsJavaScriptException();
403
+ return env.Undefined();
404
+ }
405
+
406
+ Function cb;
407
+ Promise promise = CreatePromiseAndCallback(env, cb);
408
+
409
+ auto execute_fn = [path, key, confirm, queue_size](ResultAsyncWorker* worker) {
410
+ chat_ctrl ctrl = nullptr;
411
+ char* c_res = chat_migrate_init_queue(path.c_str(), key.c_str(), confirm.c_str(), queue_size, &ctrl);
412
+ worker->SetCtrl(reinterpret_cast<uintptr_t>(ctrl));
413
+ HandleCResult(worker, c_res, "chat_migrate_init_queue");
414
+ };
415
+
416
+ ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn), MigrateResultProcessor());
417
+ worker->Queue();
418
+
419
+ return promise;
420
+ }
421
+
218
422
  Value ChatCloseStore(const CallbackInfo& args) {
219
423
  Env env = args.Env();
220
424
  if (args.Length() < 1 || !args[0].IsBigInt()) {
@@ -223,11 +427,15 @@ Value ChatCloseStore(const CallbackInfo& args) {
223
427
  }
224
428
 
225
429
  chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
430
+ std::shared_ptr<Receiver> receiver = TakeReceiver(*static_cast<Receivers*>(args.Data()), ctrl);
226
431
 
227
432
  Function cb;
228
433
  Promise promise = CreatePromiseAndCallback(env, cb);
229
434
 
230
- auto execute_fn = [ctrl](ResultAsyncWorker* worker) {
435
+ auto execute_fn = [ctrl, receiver](ResultAsyncWorker* worker) {
436
+ if (receiver) {
437
+ receiver->Stop();
438
+ }
231
439
  char* c_res = chat_close_store(ctrl);
232
440
  HandleCResult(worker, c_res, "chat_close_store");
233
441
  };
@@ -271,44 +479,63 @@ Value ChatRecvMsgWait(const CallbackInfo& args) {
271
479
 
272
480
  chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
273
481
  int wait = static_cast<int>(args[1].As<Number>().Int32Value());
482
+ Receivers& receivers = *static_cast<Receivers*>(args.Data());
483
+
484
+ auto deferred = std::make_shared<Promise::Deferred>(Promise::Deferred::New(env));
485
+ auto it = receivers.find(reinterpret_cast<uintptr_t>(ctrl));
486
+ if (it == receivers.end()) {
487
+ std::shared_ptr<Receiver> receiver;
488
+ try {
489
+ receiver = Receiver::Start(env, ctrl);
490
+ } catch (const std::system_error& e) {
491
+ deferred->Reject(Error::New(env, e.what()).Value());
492
+ return deferred->Promise();
493
+ }
494
+ if (!receiver) {
495
+ return env.Undefined();
496
+ }
497
+ it = receivers.emplace(reinterpret_cast<uintptr_t>(ctrl), std::move(receiver)).first;
498
+ }
499
+ it->second->Enqueue(env, {wait, deferred});
274
500
 
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;
501
+ return deferred->Promise();
287
502
  }
288
503
 
289
504
  Value ChatWriteFile(const CallbackInfo& args) {
290
505
  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();
506
+ if (args.Length() < 3 || !args[0].IsBigInt() || !args[1].IsString() || !(args[2].IsArrayBuffer() || args[2].IsTypedArray())) {
507
+ TypeError::New(env, "Expected bigint (ctrl), string (path), ArrayBuffer or Uint8Array").ThrowAsJavaScriptException();
293
508
  return env.Undefined();
294
509
  }
295
510
 
296
511
  chat_ctrl ctrl = FromChatCtrlBigInt(args[0]);
297
512
  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();
513
+ char* data;
514
+ size_t len;
515
+ if (args[2].IsArrayBuffer()) {
516
+ ArrayBuffer ab = args[2].As<ArrayBuffer>();
517
+ data = static_cast<char*>(ab.Data());
518
+ len = ab.ByteLength();
519
+ } else {
520
+ TypedArray view = args[2].As<TypedArray>();
521
+ data = static_cast<char*>(view.ArrayBuffer().Data()) + view.ByteOffset();
522
+ len = view.ByteLength();
523
+ }
524
+ if (len > static_cast<size_t>(INT_MAX)) {
525
+ RangeError::New(env, "Buffer is too large").ThrowAsJavaScriptException();
526
+ return env.Undefined();
527
+ }
301
528
 
302
529
  Function cb;
303
530
  Promise promise = CreatePromiseAndCallback(env, cb);
304
531
 
305
- auto execute_fn = [ctrl, path, ab, data, len](ResultAsyncWorker* worker) {
306
- (void)ab; // to keep ArrayBuffer alive
532
+ auto execute_fn = [ctrl, path, data, len](ResultAsyncWorker* worker) {
307
533
  char* c_res = chat_write_file(ctrl, path.c_str(), data, static_cast<int>(len));
308
534
  HandleCResult(worker, c_res, "chat_write_file");
309
535
  };
310
536
 
311
537
  ResultAsyncWorker* worker = new ResultAsyncWorker(cb, std::move(execute_fn));
538
+ worker->KeepAlive(args[2].As<Object>());
312
539
  worker->Queue();
313
540
 
314
541
  return promise;
@@ -410,10 +637,19 @@ Value ChatDecryptFile(const CallbackInfo& args) {
410
637
 
411
638
  Object Init(Env env, Object exports) {
412
639
  haskell_init();
640
+ auto* receivers = new Receivers();
641
+ // Stopping all receivers before joining any bounds teardown by the longest in-flight receive.
642
+ env.AddCleanupHook([receivers]() {
643
+ for (auto& entry : *receivers) {
644
+ entry.second->RequestStop();
645
+ }
646
+ delete receivers;
647
+ });
413
648
  exports.Set("chat_migrate_init", Function::New(env, ChatMigrateInit));
414
- exports.Set("chat_close_store", Function::New(env, ChatCloseStore));
649
+ exports.Set("chat_migrate_init_queue", Function::New(env, ChatMigrateInitQueue));
650
+ exports.Set("chat_close_store", Function::New(env, ChatCloseStore, "chat_close_store", receivers));
415
651
  exports.Set("chat_send_cmd", Function::New(env, ChatSendCmd));
416
- exports.Set("chat_recv_msg_wait", Function::New(env, ChatRecvMsgWait));
652
+ exports.Set("chat_recv_msg_wait", Function::New(env, ChatRecvMsgWait, "chat_recv_msg_wait", receivers));
417
653
  exports.Set("chat_write_file", Function::New(env, ChatWriteFile));
418
654
  exports.Set("chat_read_file", Function::New(env, ChatReadFile));
419
655
  exports.Set("chat_encrypt_file", Function::New(env, ChatEncryptFile));
package/cpp/simplex.h CHANGED
@@ -11,11 +11,13 @@
11
11
 
12
12
  extern "C" void hs_init(int argc, char **argv[]);
13
13
  extern "C" void hs_init_with_rtsopts(int * argc, char **argv[]);
14
+ extern "C" void hs_thread_done(void);
14
15
 
15
16
  typedef long* chat_ctrl;
16
17
 
17
18
  // the last parameter is used to return the pointer to chat controller
18
19
  extern "C" char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
20
+ extern "C" char *chat_migrate_init_queue(const char *path, const char *key, const char *confirm, const int queueSize, chat_ctrl *ctrl);
19
21
  extern "C" char *chat_close_store(chat_ctrl ctrl);
20
22
  extern "C" char *chat_reopen_store(chat_ctrl ctrl);
21
23
  extern "C" char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
package/dist/api.d.ts CHANGED
@@ -41,27 +41,6 @@ export type EventSubscriberFunc<K extends CEvt.Tag> = (event: ChatEvent & {
41
41
  export type EventSubscribers = {
42
42
  [K in CEvt.Tag]?: EventSubscriberFunc<K>;
43
43
  };
44
- /**
45
- * Database configuration. The native library is built against exactly one
46
- * backend (see `simplex_backend` / `SIMPLEX_BACKEND` at install time); this
47
- * type makes the caller state which one they are targeting so field names
48
- * can't lie about their meaning.
49
- */
50
- export type DbConfig = {
51
- /** SQLite backend (default). */
52
- type: "sqlite";
53
- /** File prefix — two schema files are named `<prefix>_chat.db` and `<prefix>_agent.db`. */
54
- filePrefix: string;
55
- /** Optional SQLCipher encryption key. Empty/omitted = unencrypted. */
56
- encryptionKey?: string;
57
- } | {
58
- /** PostgreSQL backend (Linux x86_64 only, libpq5 required). */
59
- type: "postgres";
60
- /** Schema prefix used to namespace tables. Defaults to `"simplex_v1"` when omitted. */
61
- schemaPrefix?: string;
62
- /** PostgreSQL connection string (e.g. `postgres://user:pass@host/db`). */
63
- connectionString: string;
64
- };
65
44
  /**
66
45
  * Main API class for interacting with the chat core library.
67
46
  */
@@ -74,10 +53,11 @@ export declare class ChatApi {
74
53
  private constructor();
75
54
  /**
76
55
  * Initializes the ChatApi.
77
- * @param {DbConfig} db - Database configuration (sqlite or postgres).
56
+ * @param {string} dbFilePrefix - File prefix for the database files.
57
+ * @param {string} [dbKey=""] - Database encryption key.
78
58
  * @param {core.MigrationConfirmation} [confirm=core.MigrationConfirmation.YesUp] - Migration confirmation mode.
79
59
  */
80
- static init(db: DbConfig, confirm?: core.MigrationConfirmation): Promise<ChatApi>;
60
+ static init(dbFilePrefix: string, dbKey?: string, confirm?: core.MigrationConfirmation): Promise<ChatApi>;
81
61
  /**
82
62
  * Start chat controller. Must be called with the existing user profile.
83
63
  */
@@ -348,40 +328,11 @@ export declare class ChatApi {
348
328
  * Network usage: no.
349
329
  */
350
330
  apiListGroups(userId: number, contactId?: number, search?: string): Promise<T.GroupInfo[]>;
351
- /**
352
- * Get chat previews (paginated).
353
- * Network usage: no.
354
- *
355
- * Prefer this over apiListContacts / apiListGroups for any scan: those
356
- * methods load every record into memory in a single response and will fail
357
- * on large databases.
358
- */
359
- apiGetChats(userId: number, pagination: T.PaginationByTime, query?: T.ChatListQuery, pendingConnections?: boolean): Promise<T.AChat[]>;
360
331
  /**
361
332
  * Delete chat.
362
333
  * Network usage: background.
363
334
  */
364
335
  apiDeleteChat(chatType: T.ChatType, chatId: number, deleteMode?: T.ChatDeleteMode): Promise<void>;
365
- /**
366
- * Set group custom data.
367
- * Network usage: no.
368
- */
369
- apiSetGroupCustomData(groupId: number, customData?: object): Promise<void>;
370
- /**
371
- * Set contact custom data.
372
- * Network usage: no.
373
- */
374
- apiSetContactCustomData(contactId: number, customData?: object): Promise<void>;
375
- /**
376
- * Set auto-accept member contacts.
377
- * Network usage: no.
378
- */
379
- apiSetAutoAcceptMemberContacts(userId: number, onOff: boolean): Promise<void>;
380
- /**
381
- * Get chat items.
382
- * Network usage: no.
383
- */
384
- apiGetChat(chatType: T.ChatType, chatId: number, count: number): Promise<any>;
385
336
  /**
386
337
  * Get active user profile
387
338
  * Network usage: no.
@@ -417,16 +368,4 @@ export declare class ChatApi {
417
368
  * Network usage: background.
418
369
  */
419
370
  apiSetContactPrefs(contactId: number, preferences: T.Preferences): Promise<void>;
420
- /**
421
- * Create a direct message contact with a group member.
422
- * Returns the created contact.
423
- * Network usage: interactive.
424
- */
425
- apiCreateMemberContact(groupId: number, groupMemberId: number): Promise<T.Contact>;
426
- /**
427
- * Send a direct message invitation to a group member contact.
428
- * The contact must have been created with {@link apiCreateMemberContact}.
429
- * Network usage: interactive.
430
- */
431
- apiSendMemberContactInvitation(contactId: number, message?: T.MsgContent | string): Promise<T.Contact>;
432
371
  }
package/dist/api.js CHANGED
@@ -26,17 +26,6 @@ exports.defaultBotAddressSettings = {
26
26
  welcomeMessage: undefined,
27
27
  businessAddress: false
28
28
  };
29
- function dbConfigToMigrateArgs(db) {
30
- var _a, _b;
31
- switch (db.type) {
32
- case "sqlite":
33
- return [db.filePrefix, (_a = db.encryptionKey) !== null && _a !== void 0 ? _a : ""];
34
- case "postgres":
35
- return [(_b = db.schemaPrefix) !== null && _b !== void 0 ? _b : "", db.connectionString];
36
- default:
37
- throw new Error(`Invalid DbConfig: ${JSON.stringify(db)}`);
38
- }
39
- }
40
29
  /**
41
30
  * Main API class for interacting with the chat core library.
42
31
  */
@@ -50,12 +39,12 @@ class ChatApi {
50
39
  }
51
40
  /**
52
41
  * Initializes the ChatApi.
53
- * @param {DbConfig} db - Database configuration (sqlite or postgres).
42
+ * @param {string} dbFilePrefix - File prefix for the database files.
43
+ * @param {string} [dbKey=""] - Database encryption key.
54
44
  * @param {core.MigrationConfirmation} [confirm=core.MigrationConfirmation.YesUp] - Migration confirmation mode.
55
45
  */
56
- static async init(db, confirm = core.MigrationConfirmation.YesUp) {
57
- const [path, key] = dbConfigToMigrateArgs(db);
58
- const ctrl = await core.chatMigrateInit(path, key, confirm);
46
+ static async init(dbFilePrefix, dbKey = "", confirm = core.MigrationConfirmation.YesUp) {
47
+ const ctrl = await core.chatMigrateInit(dbFilePrefix, dbKey, confirm);
59
48
  return new ChatApi(ctrl);
60
49
  }
61
50
  /**
@@ -357,8 +346,7 @@ class ChatApi {
357
346
  const r = await this.sendChatCmd(types_1.CC.APISendMessages.cmdString({
358
347
  sendRef,
359
348
  composedMessages: messages,
360
- liveMessage,
361
- signMessages: false
349
+ liveMessage
362
350
  }));
363
351
  if (r.type === "newChatItems")
364
352
  return r.chatItems;
@@ -604,7 +592,7 @@ class ChatApi {
604
592
  * Network usage: interactive.
605
593
  */
606
594
  async apiConnectPlan(userId, connectionLink) {
607
- const r = await this.sendChatCmd(types_1.CC.APIConnectPlan.cmdString({ userId, connectTarget: connectionLink, resolveMode: types_1.T.PlanResolveMode.Unknown }));
595
+ const r = await this.sendChatCmd(types_1.CC.APIConnectPlan.cmdString({ userId, connectionLink }));
608
596
  if (r.type === "connectionPlan")
609
597
  return [r.connectionPlan, r.connLink];
610
598
  throw new ChatCommandError("error getting connect plan", r);
@@ -622,7 +610,7 @@ class ChatApi {
622
610
  * Network usage: interactive.
623
611
  */
624
612
  async apiConnectActiveUser(connLink) {
625
- const r = await this.sendChatCmd(types_1.CC.Connect.cmdString({ incognito: false, connTarget_: connLink }));
613
+ const r = await this.sendChatCmd(types_1.CC.Connect.cmdString({ incognito: false, connLink_: connLink }));
626
614
  return this.handleConnectResult(r);
627
615
  }
628
616
  handleConnectResult(r) {
@@ -677,20 +665,6 @@ class ChatApi {
677
665
  return r.groups;
678
666
  throw new ChatCommandError("error listing groups", r);
679
667
  }
680
- /**
681
- * Get chat previews (paginated).
682
- * Network usage: no.
683
- *
684
- * Prefer this over apiListContacts / apiListGroups for any scan: those
685
- * methods load every record into memory in a single response and will fail
686
- * on large databases.
687
- */
688
- async apiGetChats(userId, pagination, query = { type: "filters", favorite: false, unread: false }, pendingConnections = false) {
689
- const r = await this.sendChatCmd(types_1.CC.APIGetChats.cmdString({ userId, pendingConnections, pagination, query }));
690
- if (r.type === "apiChats")
691
- return r.chats;
692
- throw new ChatCommandError("error getting chats", r);
693
- }
694
668
  /**
695
669
  * Delete chat.
696
670
  * Network usage: background.
@@ -709,47 +683,6 @@ class ChatApi {
709
683
  }
710
684
  throw new ChatCommandError("error deleting chat", r);
711
685
  }
712
- /**
713
- * Set group custom data.
714
- * Network usage: no.
715
- */
716
- async apiSetGroupCustomData(groupId, customData) {
717
- const r = await this.sendChatCmd(types_1.CC.APISetGroupCustomData.cmdString({ groupId, customData }));
718
- if (r.type === "cmdOk")
719
- return;
720
- throw new ChatCommandError("error setting group custom data", r);
721
- }
722
- /**
723
- * Set contact custom data.
724
- * Network usage: no.
725
- */
726
- async apiSetContactCustomData(contactId, customData) {
727
- const r = await this.sendChatCmd(types_1.CC.APISetContactCustomData.cmdString({ contactId, customData }));
728
- if (r.type === "cmdOk")
729
- return;
730
- throw new ChatCommandError("error setting contact custom data", r);
731
- }
732
- /**
733
- * Set auto-accept member contacts.
734
- * Network usage: no.
735
- */
736
- async apiSetAutoAcceptMemberContacts(userId, onOff) {
737
- const r = await this.sendChatCmd(types_1.CC.APISetUserAutoAcceptMemberContacts.cmdString({ userId, onOff }));
738
- if (r.type === "cmdOk")
739
- return;
740
- throw new ChatCommandError("error setting auto-accept member contacts", r);
741
- }
742
- /**
743
- * Get chat items.
744
- * Network usage: no.
745
- */
746
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
747
- async apiGetChat(chatType, chatId, count) {
748
- const r = await this.sendChatCmd(`/_get chat ${types_1.T.ChatType.cmdString(chatType)}${chatId} count=${count}`);
749
- if (r.type === "apiChat")
750
- return r.chat;
751
- throw new ChatCommandError("error getting chat", r);
752
- }
753
686
  /**
754
687
  * Get active user profile
755
688
  * Network usage: no.
@@ -777,7 +710,7 @@ class ChatApi {
777
710
  * Network usage: no.
778
711
  */
779
712
  async apiCreateActiveUser(profile) {
780
- const r = await this.sendChatCmd(types_1.CC.CreateActiveUser.cmdString({ newUser: { profile, pastTimestamp: false, userChatRelay: false, clientService: false } }));
713
+ const r = await this.sendChatCmd(types_1.CC.CreateActiveUser.cmdString({ newUser: { profile, pastTimestamp: false } }));
781
714
  if (r.type === "activeUser")
782
715
  return r.user;
783
716
  throw new ChatCommandError("unexpected response", r);
@@ -836,37 +769,6 @@ class ChatApi {
836
769
  if (r.type !== "contactPrefsUpdated")
837
770
  throw new ChatCommandError("error setting contact prefs", r);
838
771
  }
839
- /**
840
- * Create a direct message contact with a group member.
841
- * Returns the created contact.
842
- * Network usage: interactive.
843
- */
844
- async apiCreateMemberContact(groupId, groupMemberId) {
845
- const r = await this.sendChatCmd(`/_create member contact #${groupId} ${groupMemberId}`);
846
- if (r.type === "newMemberContact")
847
- return r.contact;
848
- throw new ChatCommandError("error creating member contact", r);
849
- }
850
- /**
851
- * Send a direct message invitation to a group member contact.
852
- * The contact must have been created with {@link apiCreateMemberContact}.
853
- * Network usage: interactive.
854
- */
855
- async apiSendMemberContactInvitation(contactId, message) {
856
- let cmd = `/_invite member contact @${contactId}`;
857
- if (message !== undefined) {
858
- if (typeof message === "string") {
859
- cmd += ` text ${message}`;
860
- }
861
- else {
862
- cmd += ` json ${JSON.stringify(message)}`;
863
- }
864
- }
865
- const r = await this.sendChatCmd(cmd);
866
- if (r.type === "newMemberContactSentInv")
867
- return r.contact;
868
- throw new ChatCommandError("error sending member contact invitation", r);
869
- }
870
772
  }
871
773
  exports.ChatApi = ChatApi;
872
774
  //# sourceMappingURL=api.js.map