@taladb/react-native 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/LICENSE-MIT +21 -0
  2. package/android/src/main/jniLibs/arm64-v8a/{libredb-cabe8e041f57653a.so → libredb-c8c61b1f6c018b08.so} +0 -0
  3. package/android/src/main/jniLibs/arm64-v8a/libtaladb_ffi.so +0 -0
  4. package/android/src/main/jniLibs/armeabi-v7a/libredb-9ad37c880f82971f.so +0 -0
  5. package/android/src/main/jniLibs/armeabi-v7a/libtaladb_ffi.so +0 -0
  6. package/android/src/main/jniLibs/x86_64/{libredb-c48d0cacc11f2aba.so → libredb-5f8ad3451c0f8dea.so} +0 -0
  7. package/android/src/main/jniLibs/x86_64/libtaladb_ffi.so +0 -0
  8. package/cpp/TalaDBHostObject.cpp +2 -129
  9. package/cpp/taladb.h +2 -75
  10. package/ios/TalaDBFfi.xcframework/ios-arm64/Headers/TalaDBHostObject.cpp +2 -129
  11. package/ios/TalaDBFfi.xcframework/ios-arm64/Headers/taladb.h +2 -75
  12. package/ios/TalaDBFfi.xcframework/ios-arm64/libtaladb_ffi.a +0 -0
  13. package/ios/TalaDBFfi.xcframework/ios-arm64_x86_64-simulator/Headers/TalaDBHostObject.cpp +2 -129
  14. package/ios/TalaDBFfi.xcframework/ios-arm64_x86_64-simulator/Headers/taladb.h +2 -75
  15. package/ios/TalaDBFfi.xcframework/ios-arm64_x86_64-simulator/libtaladb_ffi_sim.a +0 -0
  16. package/package.json +7 -2
  17. package/src/NativeTalaDB.ts +3 -27
  18. package/src/index.tsx +122 -92
  19. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  20. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  21. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  22. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  23. package/android/.gradle/8.9/gc.properties +0 -0
  24. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  25. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  26. package/android/.gradle/vcs-1/gc.properties +0 -0
  27. package/android/src/main/jniLibs/armeabi-v7a/libredb-700774fab536b136.so +0 -0
  28. /package/{LICENSE → LICENSE-APACHE} +0 -0
@@ -38,7 +38,6 @@ TalaDBHostObject::TalaDBHostObject(TalaDbHandle *db) : db_(db) {}
38
38
 
39
39
  TalaDBHostObject::~TalaDBHostObject() {
40
40
  if (db_) {
41
- taladb_sync_flush(db_, 5000);
42
41
  taladb_close(db_);
43
42
  db_ = nullptr;
44
43
  }
@@ -241,14 +240,12 @@ Value TalaDBHostObject::awaitJobAsPromise(Runtime &rt, TalaDbJob *job, bool pars
241
240
  std::vector<PropNameID> TalaDBHostObject::getPropertyNames(Runtime &rt) {
242
241
  std::vector<std::string> names = {
243
242
  "insert", "insertMany",
244
- "replaceManyWithIds", "deleteManyWithIds",
245
243
  "find", "findOne",
246
244
  "updateOne", "updateMany",
247
245
  "deleteOne", "deleteMany",
248
246
  "count",
249
247
  "aggregate",
250
- "exportChanges", "importChanges", "listCollectionNames",
251
- "importChangesValidated", "quarantined",
248
+ "listCollectionNames",
252
249
  "userVersion", "setUserVersion",
253
250
  "flush",
254
251
  "createIndex", "dropIndex",
@@ -259,7 +256,6 @@ std::vector<PropNameID> TalaDBHostObject::getPropertyNames(Runtime &rt) {
259
256
  "searchText", "hybridSearch",
260
257
  "findAsync",
261
258
  "compact",
262
- "syncStatus", "flushSync",
263
259
  "close",
264
260
  };
265
261
  std::vector<PropNameID> result;
@@ -315,46 +311,6 @@ Value TalaDBHostObject::get(Runtime &rt, const PropNameID &propName) {
315
311
  });
316
312
  }
317
313
 
318
- // ------------------------------------------------------------------
319
- // replaceManyWithIds(collection: string, docs: object[], origin: string): string[]
320
- // ------------------------------------------------------------------
321
- if (name == "replaceManyWithIds") {
322
- return Function::createFromHostFunction(
323
- rt, PropNameID::forAscii(rt, "replaceManyWithIds"), 3,
324
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
325
- if (count < 3) throw JSError(rt, "replaceManyWithIds requires 3 arguments");
326
- auto col = args[0].getString(rt).utf8(rt);
327
- auto docsJson = stringify(rt, args[1]);
328
- auto origin = args[2].getString(rt).utf8(rt);
329
- char *result = taladb_replace_many_with_ids(
330
- db_, col.c_str(), docsJson.c_str(), origin.c_str());
331
- if (!result) throw ffiError(rt, "taladb_replace_many_with_ids failed");
332
- std::string json(result);
333
- taladb_free_string(result);
334
- return parse(rt, json);
335
- });
336
- }
337
-
338
- // ------------------------------------------------------------------
339
- // deleteManyWithIds(collection: string, ids: string[], origin: string): number
340
- // ------------------------------------------------------------------
341
- if (name == "deleteManyWithIds") {
342
- return Function::createFromHostFunction(
343
- rt, PropNameID::forAscii(rt, "deleteManyWithIds"), 3,
344
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
345
- if (count < 3) throw JSError(rt, "deleteManyWithIds requires 3 arguments");
346
- auto col = args[0].getString(rt).utf8(rt);
347
- auto idsJson = stringify(rt, args[1]);
348
- auto origin = args[2].getString(rt).utf8(rt);
349
- char *result = taladb_delete_many_with_ids(
350
- db_, col.c_str(), idsJson.c_str(), origin.c_str());
351
- if (!result) throw ffiError(rt, "taladb_delete_many_with_ids failed");
352
- std::string json(result);
353
- taladb_free_string(result);
354
- return parse(rt, json);
355
- });
356
- }
357
-
358
314
  // ------------------------------------------------------------------
359
315
  // find(collection: string, filter: object | null): object[]
360
316
  // ------------------------------------------------------------------
@@ -494,38 +450,8 @@ Value TalaDBHostObject::get(Runtime &rt, const PropNameID &propName) {
494
450
  }
495
451
 
496
452
  // ------------------------------------------------------------------
497
- // Bidirectional sync — exportChanges / importChanges / listCollectionNames
498
- // (back JS db.sync(); the runtime-agnostic loop lives in taladb/src/sync.ts)
453
+ // Collection metadata
499
454
  // ------------------------------------------------------------------
500
- if (name == "exportChanges") {
501
- return Function::createFromHostFunction(
502
- rt, PropNameID::forAscii(rt, "exportChanges"), 2,
503
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
504
- if (count < 2) throw JSError(rt, "exportChanges requires 2 arguments");
505
- auto collectionsJson = stringify(rt, args[0]); // string[] → JSON array
506
- double sinceMs = args[1].getNumber();
507
- char *result = taladb_export_changes(db_, collectionsJson.c_str(), sinceMs);
508
- if (!result) throw JSError(rt, "taladb_export_changes failed");
509
- std::string json(result);
510
- taladb_free_string(result);
511
- // Return the changeset as an opaque string (not parsed) — the
512
- // JS sync adapter passes it straight to the transport.
513
- return String::createFromUtf8(rt, json);
514
- });
515
- }
516
-
517
- if (name == "importChanges") {
518
- return Function::createFromHostFunction(
519
- rt, PropNameID::forAscii(rt, "importChanges"), 1,
520
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
521
- if (count < 1) throw JSError(rt, "importChanges requires 1 argument");
522
- auto changeset = args[0].getString(rt).utf8(rt);
523
- int32_t n = taladb_import_changes(db_, changeset.c_str());
524
- if (n < 0) throw JSError(rt, "taladb_import_changes failed");
525
- return Value(static_cast<double>(n));
526
- });
527
- }
528
-
529
455
  if (name == "listCollectionNames") {
530
456
  return Function::createFromHostFunction(
531
457
  rt, PropNameID::forAscii(rt, "listCollectionNames"), 0,
@@ -538,38 +464,6 @@ Value TalaDBHostObject::get(Runtime &rt, const PropNameID &propName) {
538
464
  });
539
465
  }
540
466
 
541
- // ------------------------------------------------------------------
542
- // Validate-on-import — importChangesValidated / quarantined
543
- // ------------------------------------------------------------------
544
- if (name == "importChangesValidated") {
545
- return Function::createFromHostFunction(
546
- rt, PropNameID::forAscii(rt, "importChangesValidated"), 2,
547
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
548
- if (count < 2) throw JSError(rt, "importChangesValidated requires 2 arguments");
549
- auto changeset = args[0].getString(rt).utf8(rt);
550
- auto schemas = args[1].getString(rt).utf8(rt);
551
- char *result = taladb_import_changes_validated(db_, changeset.c_str(), schemas.c_str());
552
- if (!result) throw JSError(rt, "taladb_import_changes_validated failed");
553
- std::string json(result);
554
- taladb_free_string(result);
555
- return parse(rt, json); // { applied, skipped, quarantined }
556
- });
557
- }
558
-
559
- if (name == "quarantined") {
560
- return Function::createFromHostFunction(
561
- rt, PropNameID::forAscii(rt, "quarantined"), 1,
562
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
563
- if (count < 1) throw JSError(rt, "quarantined requires 1 argument");
564
- auto col = args[0].getString(rt).utf8(rt);
565
- char *result = taladb_quarantined(db_, col.c_str());
566
- if (!result) throw JSError(rt, "taladb_quarantined failed");
567
- std::string json(result);
568
- taladb_free_string(result);
569
- return parse(rt, json); // [{ document, reason, changedAt }]
570
- });
571
- }
572
-
573
467
  // ------------------------------------------------------------------
574
468
  // Application migrations — userVersion / setUserVersion
575
469
  // ------------------------------------------------------------------
@@ -866,26 +760,6 @@ Value TalaDBHostObject::get(Runtime &rt, const PropNameID &propName) {
866
760
  });
867
761
  }
868
762
 
869
- if (name == "syncStatus") {
870
- return Function::createFromHostFunction(
871
- rt, PropNameID::forAscii(rt, "syncStatus"), 0,
872
- [this](Runtime &rt, const Value &, const Value *, size_t) -> Value {
873
- char *raw = taladb_sync_status(db_);
874
- if (!raw) throw ffiError(rt, "taladb_sync_status failed");
875
- std::string json(raw); taladb_free_string(raw);
876
- return parse(rt, json);
877
- });
878
- }
879
-
880
- if (name == "flushSync") {
881
- return Function::createFromHostFunction(
882
- rt, PropNameID::forAscii(rt, "flushSync"), 1,
883
- [this](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
884
- uint64_t timeout = count && args[0].isNumber() ? (uint64_t)args[0].getNumber() : 5000;
885
- return Value(taladb_sync_flush(db_, timeout) == 1);
886
- });
887
- }
888
-
889
763
  // ------------------------------------------------------------------
890
764
  // close(): void (synchronous — the destructor does the real work)
891
765
  // ------------------------------------------------------------------
@@ -894,7 +768,6 @@ Value TalaDBHostObject::get(Runtime &rt, const PropNameID &propName) {
894
768
  rt, PropNameID::forAscii(rt, "close"), 0,
895
769
  [this](Runtime &rt, const Value &, const Value *, size_t) -> Value {
896
770
  if (db_) {
897
- taladb_sync_flush(db_, 5000);
898
771
  taladb_close(db_);
899
772
  db_ = nullptr;
900
773
  }
@@ -38,9 +38,9 @@ typedef struct TalaDbHandle TalaDbHandle;
38
38
  TalaDbHandle *taladb_open(const char *path);
39
39
 
40
40
  /**
41
- * Open (or create) a database with HTTP push sync configuration.
41
+ * Open (or create) a database with durability/encryption configuration.
42
42
  *
43
- * config_json — JSON-serialised TalaDbConfig, or NULL to open without sync.
43
+ * config_json — JSON-serialised TalaDbConfig, or NULL for defaults.
44
44
  * Returns an opaque handle (same semantics as taladb_open), or NULL on failure.
45
45
  */
46
46
  TalaDbHandle *taladb_open_with_config(const char *path, const char *config_json);
@@ -79,39 +79,6 @@ char *taladb_insert_many(TalaDbHandle *handle,
79
79
  const char *collection,
80
80
  const char *docs_json);
81
81
 
82
- /* -------------------------------------------------------------------------
83
- * Replication writes — id-addressed upsert / delete
84
- * ---------------------------------------------------------------------- */
85
-
86
- /**
87
- * Upsert many documents by caller-supplied `_id`, in one commit.
88
- *
89
- * Unlike taladb_insert_many(), which discards `_id` and mints a fresh ULID, this
90
- * honours the id on each document — which is what makes a replication upsert
91
- * idempotent across repeated fetches of the same remote row.
92
- *
93
- * `origin` is "remote" (authoritative rows replicated in from an origin) or
94
- * "local" (ordinary user writes). Remote rows are marked so they never replicate
95
- * back out.
96
- *
97
- * Returns a JSON array of ULID strings, or NULL on error.
98
- * Caller must free with taladb_free_string().
99
- */
100
- char *taladb_replace_many_with_ids(TalaDbHandle *handle,
101
- const char *collection,
102
- const char *docs_json,
103
- const char *origin);
104
-
105
- /**
106
- * Delete many documents by id, in one commit.
107
- * Returns a JSON number (the count removed), or NULL on error.
108
- * Caller must free with taladb_free_string().
109
- */
110
- char *taladb_delete_many_with_ids(TalaDbHandle *handle,
111
- const char *collection,
112
- const char *ids_json,
113
- const char *origin);
114
-
115
82
  /* -------------------------------------------------------------------------
116
83
  * Find
117
84
  * ---------------------------------------------------------------------- */
@@ -187,49 +154,12 @@ char *taladb_aggregate(TalaDbHandle *handle,
187
154
  const char *collection,
188
155
  const char *pipeline_json);
189
156
 
190
- /* -------------------------------------------------------------------------
191
- * Bidirectional sync — changeset export / import (backs JS db.sync())
192
- * ---------------------------------------------------------------------- */
193
-
194
- /**
195
- * Export a changeset for the collections in collections_json (a JSON array of
196
- * names) with changed_at > since_ms. Returns a JSON string, or NULL on error.
197
- * Caller must free with taladb_free_string().
198
- */
199
- char *taladb_export_changes(TalaDbHandle *handle,
200
- const char *collections_json,
201
- double since_ms);
202
-
203
- /**
204
- * Merge a JSON changeset (from a remote peer) via Last-Write-Wins.
205
- * Returns the number of documents changed, or -1 on error.
206
- */
207
- int32_t taladb_import_changes(TalaDbHandle *handle,
208
- const char *changeset_json);
209
-
210
157
  /**
211
158
  * User collection names (reserved names excluded) as a JSON array string.
212
159
  * Returns NULL on error. Caller must free with taladb_free_string().
213
160
  */
214
161
  char *taladb_list_collection_names(TalaDbHandle *handle);
215
162
 
216
- /**
217
- * Merge a JSON changeset through a tolerant structural validator built from
218
- * schemas_json ({ "<collection>": { version, required, types, defaults,
219
- * renames } }). Returns a JSON "{ applied, skipped, quarantined }" string, or
220
- * NULL on error. Caller must free with taladb_free_string().
221
- */
222
- char *taladb_import_changes_validated(TalaDbHandle *handle,
223
- const char *changeset_json,
224
- const char *schemas_json);
225
-
226
- /**
227
- * Documents set aside in a collection's quarantine table by a validated import,
228
- * as a JSON array of { document, reason, changedAt }. NULL on error. Caller
229
- * must free with taladb_free_string().
230
- */
231
- char *taladb_quarantined(TalaDbHandle *handle, const char *collection);
232
-
233
163
  /**
234
164
  * Read / write the application migration version (backs openDB({ migrations })).
235
165
  * taladb_user_version returns the version (0 if unset) or -1 on error;
@@ -320,9 +250,6 @@ char *taladb_hybrid_search(TalaDbHandle *handle,
320
250
  const char *filter_json,
321
251
  const char *options_json);
322
252
 
323
- char *taladb_sync_status(TalaDbHandle *handle);
324
- int32_t taladb_sync_flush(TalaDbHandle *handle, uint64_t timeout_ms);
325
-
326
253
  /* -------------------------------------------------------------------------
327
254
  * Async job API — run heavy queries on a background thread.
328
255
  *
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@taladb/react-native",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "TalaDB React Native module — document and vector database via JSI HostObject",
5
5
  "main": "src/index",
6
6
  "types": "src/index.tsx",
7
7
  "files": [
8
+ "LICENSE-APACHE",
9
+ "LICENSE-MIT",
8
10
  "src/",
9
11
  "cpp/",
10
12
  "ios/",
@@ -33,7 +35,7 @@
33
35
  "engines": {
34
36
  "node": ">=18"
35
37
  },
36
- "license": "Apache-2.0",
38
+ "license": "MIT OR Apache-2.0",
37
39
  "codegenConfig": {
38
40
  "name": "TalaDB",
39
41
  "type": "modules",
@@ -46,6 +48,9 @@
46
48
  "typescript": "^5.9.3",
47
49
  "react-native": "^0.83.0"
48
50
  },
51
+ "dependencies": {
52
+ "taladb": "0.11.0"
53
+ },
49
54
  "scripts": {
50
55
  "typecheck": "tsc --noEmit",
51
56
  "build:ios": "sh scripts/build-ios.sh",
@@ -13,8 +13,7 @@ export interface Spec extends TurboModule {
13
13
  * Open (or create) a TalaDB database file at the platform default path.
14
14
  * Must be called once at app startup before using `collection()`.
15
15
  *
16
- * @param configJson Optional JSON-serialised `TalaDbConfig`. Pass to enable
17
- * HTTP push sync. When omitted, sync is disabled.
16
+ * @param configJson Optional JSON-serialised durability/encryption config.
18
17
  */
19
18
  initialize(dbName: string, configJson?: string): Promise<void>;
20
19
 
@@ -25,35 +24,12 @@ export interface Spec extends TurboModule {
25
24
  // Collection CRUD — all methods are synchronous via JSI
26
25
  // ------------------------------------------------------------------
27
26
 
28
- /** Insert a document. Returns the ULID string id. */
27
+ /** Insert a document. Honours a supplied `_id`, otherwise creates a ULID. */
29
28
  insert(collection: string, doc: Object): string;
30
29
 
31
- /** Insert multiple documents. Returns an array of ULID string ids. */
30
+ /** Insert multiple documents, honouring any supplied `_id` values. */
32
31
  insertMany(collection: string, docs: Object[]): string[];
33
32
 
34
- /**
35
- * Upsert many documents **by caller-supplied `_id`**, in one commit.
36
- *
37
- * Unlike `insertMany`, which discards `_id` and mints a fresh ULID, this honours
38
- * the id on each document — which is what makes a replication upsert idempotent
39
- * across repeated fetches of the same remote row.
40
- *
41
- * `origin` is `'remote'` for authoritative rows replicated in from an origin, or
42
- * `'local'` for ordinary user writes.
43
- */
44
- replaceManyWithIds(
45
- collection: string,
46
- docs: Object[],
47
- origin: 'local' | 'remote',
48
- ): string[];
49
-
50
- /** Delete many documents by id, in one commit. Returns the number removed. */
51
- deleteManyWithIds(
52
- collection: string,
53
- ids: string[],
54
- origin: 'local' | 'remote',
55
- ): number;
56
-
57
33
  /** Find documents matching the filter. */
58
34
  find(collection: string, filter: Object | null): Object[];
59
35
 
package/src/index.tsx CHANGED
@@ -1,71 +1,32 @@
1
- /**
2
- * TalaDB React Native — public JS API.
3
- *
4
- * Usage:
5
- * ```ts
6
- * import { TalaDBModule, openDB } from '@taladb/react-native';
7
- *
8
- * // In App.tsx / index.js (once, at startup)
9
- * await TalaDBModule.initialize('myapp.db');
10
- *
11
- * // Anywhere in the app — same API as browser
12
- * const db = openDB('myapp.db');
13
- * const users = db.collection<User>('users');
14
- * const id = users.insert({ name: 'Alice', age: 30 });
15
- * ```
16
- *
17
- * All operations are **synchronous** via JSI — no async/await needed
18
- * after initialization.
19
- */
1
+ /** TalaDB React Native public API. CRUD is synchronous through the JSI host. */
2
+ import {
3
+ createWebhookDispatcher,
4
+ type WebhookConfig,
5
+ type WebhookDispatcher,
6
+ type WebhookEvent,
7
+ type WebhookStats,
8
+ } from 'taladb';
20
9
  import NativeTalaDB from './NativeTalaDB';
21
10
 
22
- // ---------------------------------------------------------------------------
23
- // Module-level helpers
24
- // ---------------------------------------------------------------------------
25
-
26
11
  export const TalaDBModule = {
27
- /**
28
- * Open (or create) the database. Call once at app startup.
29
- *
30
- * @param configJson Optional JSON-serialised `TalaDbConfig` for HTTP push
31
- * sync. Example: `JSON.stringify({ sync: { enabled: true,
32
- * endpoint: 'https://api.example.com/events' } })`.
33
- */
12
+ /** Open the native database. The config controls durability/encryption only. */
34
13
  initialize: (dbName: string, configJson?: string) =>
35
14
  NativeTalaDB.initialize(dbName, configJson),
36
- /** Close the database gracefully. */
37
15
  close: () => NativeTalaDB.close(),
38
- /** HTTP push events dropped by backpressure or failed after retries. */
39
- syncStatus: () => native().syncStatus(),
40
- /** Wait for HTTP push work accepted before this call. */
41
- flushSync: (timeoutMs = 5000) => native().flushSync(timeoutMs),
42
16
  };
43
17
 
44
- // ---------------------------------------------------------------------------
45
- // Collection handle
46
- // ---------------------------------------------------------------------------
47
-
48
18
  export interface Document {
49
19
  _id?: string;
50
20
  [key: string]: unknown;
51
21
  }
52
22
 
23
+ export type InsertDocument<T extends Document> = Omit<T, '_id'> & { _id?: string };
53
24
  export type Filter = Record<string, unknown>;
54
25
  export type Update = Record<string, unknown>;
55
26
 
56
27
  export interface Collection<T extends Document = Document> {
57
- insert(doc: Omit<T, '_id'>): string;
58
- insertMany(docs: Omit<T, '_id'>[]): string[];
59
- /**
60
- * Upsert many documents **by `_id`**, in one commit. Requires an `_id` on every
61
- * document — for replicated rows that comes from `deriveDocId(collection, key)`.
62
- *
63
- * `origin: 'remote'` marks rows as replicated in from an authoritative origin so
64
- * they are never replicated back out; it defaults to `'local'`.
65
- */
66
- replaceManyWithIds(docs: T[], origin?: 'local' | 'remote'): string[];
67
- /** Delete many documents by id, in one commit. Returns the number removed. */
68
- deleteManyWithIds(ids: string[], origin?: 'local' | 'remote'): number;
28
+ insert(doc: InsertDocument<T>): string;
29
+ insertMany(docs: InsertDocument<T>[]): string[];
69
30
  find(filter?: Filter): T[];
70
31
  findOne(filter: Filter): T | null;
71
32
  updateOne(filter: Filter, update: Update): boolean;
@@ -79,26 +40,21 @@ export interface Collection<T extends Document = Document> {
79
40
  dropFtsIndex(field: string): void;
80
41
  }
81
42
 
43
+ export interface OpenDBOptions {
44
+ /** Runtime-agnostic outbound change webhook, delivered with global `fetch`. */
45
+ webhook?: WebhookConfig;
46
+ }
47
+
82
48
  export interface DB {
83
49
  collection<T extends Document = Document>(name: string): Collection<T>;
84
- syncStatus(): { dropped: number; failed: number };
85
- flushSync(timeoutMs?: number): boolean;
50
+ webhookStats(): WebhookStats;
51
+ flushWebhook(timeoutMs?: number): Promise<boolean>;
86
52
  close(): Promise<void>;
87
53
  }
88
54
 
89
55
  interface JsiTalaDB {
90
56
  insert(collection: string, doc: Object): string;
91
57
  insertMany(collection: string, docs: Object[]): string[];
92
- replaceManyWithIds(
93
- collection: string,
94
- docs: Object[],
95
- origin: 'local' | 'remote',
96
- ): string[];
97
- deleteManyWithIds(
98
- collection: string,
99
- ids: string[],
100
- origin: 'local' | 'remote',
101
- ): number;
102
58
  find(collection: string, filter: Object | null): Object[];
103
59
  findOne(collection: string, filter: Object | null): Object | null;
104
60
  updateOne(collection: string, filter: Object, update: Object): boolean;
@@ -110,8 +66,6 @@ interface JsiTalaDB {
110
66
  dropIndex(collection: string, field: string): void;
111
67
  createFtsIndex(collection: string, field: string): void;
112
68
  dropFtsIndex(collection: string, field: string): void;
113
- syncStatus(): { dropped: number; failed: number };
114
- flushSync(timeoutMs?: number): boolean;
115
69
  }
116
70
 
117
71
  function native(): JsiTalaDB {
@@ -120,31 +74,102 @@ function native(): JsiTalaDB {
120
74
  return host;
121
75
  }
122
76
 
123
- // ---------------------------------------------------------------------------
124
- // openDB — synchronous DB handle (after initialize() has been called)
125
- // ---------------------------------------------------------------------------
77
+ const EMPTY_STATS: WebhookStats = { pending: 0, delivered: 0, failed: 0, dropped: 0 };
78
+
79
+ function emitPostImages<T extends Document>(
80
+ webhook: WebhookDispatcher | null,
81
+ collection: string,
82
+ ids: string[],
83
+ op: 'insert' | 'update',
84
+ committedAt: number,
85
+ ): void {
86
+ if (!webhook?.reports(collection) || ids.length === 0) return;
87
+ const docs = native().find(collection, { _id: { $in: ids } }) as T[];
88
+ const byId = new Map(docs.map((doc) => [doc._id, doc]));
89
+ for (const id of ids) {
90
+ const document = byId.get(id);
91
+ if (document) webhook.emit({ op, collection, id, document, committedAt } as WebhookEvent);
92
+ }
93
+ }
94
+
95
+ function emitDeletes<T extends Document>(
96
+ webhook: WebhookDispatcher | null,
97
+ collection: string,
98
+ docs: T[],
99
+ committedAt: number,
100
+ ): void {
101
+ if (!webhook?.reports(collection)) return;
102
+ for (const document of docs) {
103
+ if (typeof document._id !== 'string') continue;
104
+ // DELETE carries the pre-image: after commit there is no post-image to read.
105
+ webhook.emit({
106
+ op: 'delete',
107
+ collection,
108
+ id: document._id,
109
+ document,
110
+ committedAt,
111
+ } as WebhookEvent);
112
+ }
113
+ }
126
114
 
127
- /**
128
- * Get a synchronous DB handle for the given database name.
129
- *
130
- * `TalaDBModule.initialize(dbName)` **must** have been awaited before calling
131
- * this function.
132
- */
133
- function collection<T extends Document>(colName: string): Collection<T> {
115
+ function collection<T extends Document>(
116
+ colName: string,
117
+ webhook: WebhookDispatcher | null,
118
+ ): Collection<T> {
134
119
  return {
135
- insert: (doc) => native().insert(colName, doc as Object),
136
- insertMany: (docs) => native().insertMany(colName, docs as Object[]),
137
- replaceManyWithIds: (docs, origin = 'local') =>
138
- native().replaceManyWithIds(colName, docs as Object[], origin),
139
- deleteManyWithIds: (ids, origin = 'local') =>
140
- native().deleteManyWithIds(colName, ids, origin),
141
- find: (filter?) => native().find(colName, filter ?? null) as T[],
120
+ insert(doc) {
121
+ const id = native().insert(colName, doc as Object);
122
+ const committedAt = Date.now();
123
+ emitPostImages<T>(webhook, colName, [id], 'insert', committedAt);
124
+ return id;
125
+ },
126
+ insertMany(docs) {
127
+ const ids = native().insertMany(colName, docs as Object[]);
128
+ const committedAt = Date.now();
129
+ emitPostImages<T>(webhook, colName, ids, 'insert', committedAt);
130
+ return ids;
131
+ },
132
+ find: (filter) => native().find(colName, filter ?? null) as T[],
142
133
  findOne: (filter) => native().findOne(colName, filter) as T | null,
143
- updateOne: (filter, update) => native().updateOne(colName, filter, update),
144
- updateMany: (filter, update) => native().updateMany(colName, filter, update),
145
- deleteOne: (filter) => native().deleteOne(colName, filter),
146
- deleteMany: (filter) => native().deleteMany(colName, filter),
147
- count: (filter?) => native().count(colName, filter ?? null),
134
+ updateOne(filter, update) {
135
+ const before = native().findOne(colName, filter) as T | null;
136
+ const changed = native().updateOne(colName, filter, update);
137
+ const committedAt = Date.now();
138
+ if (changed && typeof before?._id === 'string') {
139
+ emitPostImages<T>(webhook, colName, [before._id], 'update', committedAt);
140
+ }
141
+ return changed;
142
+ },
143
+ updateMany(filter, update) {
144
+ const before = native().find(colName, filter) as T[];
145
+ const changed = native().updateMany(colName, filter, update);
146
+ const committedAt = Date.now();
147
+ if (changed > 0) {
148
+ emitPostImages<T>(
149
+ webhook,
150
+ colName,
151
+ before.map((doc) => doc._id).filter((id): id is string => typeof id === 'string'),
152
+ 'update',
153
+ committedAt,
154
+ );
155
+ }
156
+ return changed;
157
+ },
158
+ deleteOne(filter) {
159
+ const before = native().findOne(colName, filter) as T | null;
160
+ const deleted = native().deleteOne(colName, filter);
161
+ const committedAt = Date.now();
162
+ if (deleted && before) emitDeletes(webhook, colName, [before], committedAt);
163
+ return deleted;
164
+ },
165
+ deleteMany(filter) {
166
+ const before = native().find(colName, filter) as T[];
167
+ const deleted = native().deleteMany(colName, filter);
168
+ const committedAt = Date.now();
169
+ if (deleted > 0) emitDeletes(webhook, colName, before, committedAt);
170
+ return deleted;
171
+ },
172
+ count: (filter) => native().count(colName, filter ?? null),
148
173
  createIndex: (field) => native().createIndex(colName, field),
149
174
  dropIndex: (field) => native().dropIndex(colName, field),
150
175
  createFtsIndex: (field) => native().createFtsIndex(colName, field),
@@ -152,11 +177,16 @@ function collection<T extends Document>(colName: string): Collection<T> {
152
177
  };
153
178
  }
154
179
 
155
- export function openDB(_dbName: string): DB {
180
+ /** Get a synchronous DB handle after `TalaDBModule.initialize(dbName)`. */
181
+ export function openDB(_dbName: string, options?: OpenDBOptions): DB {
182
+ const webhook = createWebhookDispatcher(options?.webhook);
156
183
  return {
157
- collection,
158
- syncStatus: () => native().syncStatus(),
159
- flushSync: (timeoutMs = 5000) => native().flushSync(timeoutMs),
160
- close: () => NativeTalaDB.close(),
184
+ collection: <T extends Document>(name: string) => collection<T>(name, webhook),
185
+ webhookStats: () => webhook?.stats() ?? { ...EMPTY_STATS },
186
+ flushWebhook: (timeoutMs) => webhook?.flush(timeoutMs) ?? Promise.resolve(true),
187
+ close: async () => {
188
+ await webhook?.flush();
189
+ await NativeTalaDB.close();
190
+ },
161
191
  };
162
192
  }
File without changes