@taladb/sync-mongodb 0.9.2 → 0.9.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.
- package/dist/index.d.mts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +29 -16
- package/dist/index.mjs +29 -16
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -4,8 +4,10 @@ import { Collection } from 'mongodb';
|
|
|
4
4
|
interface MongoSyncAdapterOptions {
|
|
5
5
|
/**
|
|
6
6
|
* The MongoDB collection to use as the change store. Create it from your own
|
|
7
|
-
* `MongoClient` so the app owns the connection lifecycle. Index
|
|
8
|
-
*
|
|
7
|
+
* `MongoClient` so the app owns the connection lifecycle. Index both fields
|
|
8
|
+
* the adapter queries on:
|
|
9
|
+
* `await collection.createIndex({ changed_at: 1 })` (pull) and
|
|
10
|
+
* `await collection.createIndex({ doc_key: 1 })` (push).
|
|
9
11
|
*/
|
|
10
12
|
collection: Collection;
|
|
11
13
|
}
|
|
@@ -31,7 +33,8 @@ declare class MongoSyncAdapter implements SyncAdapter {
|
|
|
31
33
|
constructor(options: MongoSyncAdapterOptions);
|
|
32
34
|
/**
|
|
33
35
|
* Convenience: open a MongoDB connection from a URI and return a ready
|
|
34
|
-
* adapter plus a `close()` to release it. Creates the
|
|
36
|
+
* adapter plus a `close()` to release it. Creates the indexes the adapter
|
|
37
|
+
* queries on.
|
|
35
38
|
*
|
|
36
39
|
* ```ts
|
|
37
40
|
* const { adapter, close } = await MongoSyncAdapter.connect({
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ import { Collection } from 'mongodb';
|
|
|
4
4
|
interface MongoSyncAdapterOptions {
|
|
5
5
|
/**
|
|
6
6
|
* The MongoDB collection to use as the change store. Create it from your own
|
|
7
|
-
* `MongoClient` so the app owns the connection lifecycle. Index
|
|
8
|
-
*
|
|
7
|
+
* `MongoClient` so the app owns the connection lifecycle. Index both fields
|
|
8
|
+
* the adapter queries on:
|
|
9
|
+
* `await collection.createIndex({ changed_at: 1 })` (pull) and
|
|
10
|
+
* `await collection.createIndex({ doc_key: 1 })` (push).
|
|
9
11
|
*/
|
|
10
12
|
collection: Collection;
|
|
11
13
|
}
|
|
@@ -31,7 +33,8 @@ declare class MongoSyncAdapter implements SyncAdapter {
|
|
|
31
33
|
constructor(options: MongoSyncAdapterOptions);
|
|
32
34
|
/**
|
|
33
35
|
* Convenience: open a MongoDB connection from a URI and return a ready
|
|
34
|
-
* adapter plus a `close()` to release it. Creates the
|
|
36
|
+
* adapter plus a `close()` to release it. Creates the indexes the adapter
|
|
37
|
+
* queries on.
|
|
35
38
|
*
|
|
36
39
|
* ```ts
|
|
37
40
|
* const { adapter, close } = await MongoSyncAdapter.connect({
|
package/dist/index.js
CHANGED
|
@@ -33,13 +33,15 @@ __export(index_exports, {
|
|
|
33
33
|
MongoSyncAdapter: () => MongoSyncAdapter
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_node_crypto = require("crypto");
|
|
36
37
|
var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
37
38
|
constructor(options) {
|
|
38
39
|
this.store = options.collection;
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
42
|
* Convenience: open a MongoDB connection from a URI and return a ready
|
|
42
|
-
* adapter plus a `close()` to release it. Creates the
|
|
43
|
+
* adapter plus a `close()` to release it. Creates the indexes the adapter
|
|
44
|
+
* queries on.
|
|
43
45
|
*
|
|
44
46
|
* ```ts
|
|
45
47
|
* const { adapter, close } = await MongoSyncAdapter.connect({
|
|
@@ -55,6 +57,7 @@ var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
|
55
57
|
await client.connect();
|
|
56
58
|
const store = client.db(opts.db).collection(opts.collection ?? "taladb_changes");
|
|
57
59
|
await store.createIndex({ changed_at: 1 });
|
|
60
|
+
await store.createIndex({ doc_key: 1 });
|
|
58
61
|
return {
|
|
59
62
|
adapter: new _MongoSyncAdapter({ collection: store }),
|
|
60
63
|
close: () => client.close()
|
|
@@ -63,27 +66,37 @@ var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
|
63
66
|
async push(changeset) {
|
|
64
67
|
const changes = JSON.parse(changeset);
|
|
65
68
|
if (changes.length === 0) return;
|
|
66
|
-
const
|
|
67
|
-
|
|
69
|
+
const docKeyOf = (chg) => `${chg.collection}::${chg.id}`;
|
|
70
|
+
const stored = await this.store.find(
|
|
71
|
+
{ doc_key: { $in: [...new Set(changes.map(docKeyOf))] } },
|
|
72
|
+
{ projection: { doc_key: 1, changed_at: 1 } }
|
|
73
|
+
).toArray();
|
|
74
|
+
const storedMax = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const row of stored) {
|
|
76
|
+
storedMax.set(row.doc_key, Math.max(storedMax.get(row.doc_key) ?? -1, row.changed_at));
|
|
77
|
+
}
|
|
78
|
+
const winning = new Map(storedMax);
|
|
79
|
+
for (const chg of changes) {
|
|
80
|
+
const key = docKeyOf(chg);
|
|
81
|
+
winning.set(key, Math.max(winning.get(key) ?? -1, chg.changed_at));
|
|
82
|
+
}
|
|
83
|
+
const inserts = changes.filter((chg) => chg.changed_at === winning.get(docKeyOf(chg))).map((chg) => {
|
|
84
|
+
const change = JSON.stringify(chg);
|
|
85
|
+
const docKey = docKeyOf(chg);
|
|
86
|
+
const digest = (0, import_node_crypto.createHash)("sha256").update(change).digest("hex").slice(0, 16);
|
|
68
87
|
return {
|
|
69
88
|
updateOne: {
|
|
70
|
-
filter: { _id:
|
|
71
|
-
update:
|
|
72
|
-
{
|
|
73
|
-
$replaceWith: {
|
|
74
|
-
$cond: [
|
|
75
|
-
{ $gt: [chg.changed_at, { $ifNull: ["$changed_at", -1] }] },
|
|
76
|
-
{ _id: key, changed_at: chg.changed_at, change: JSON.stringify(chg) },
|
|
77
|
-
"$$ROOT"
|
|
78
|
-
]
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
],
|
|
89
|
+
filter: { _id: `${docKey}::${chg.changed_at}::${digest}` },
|
|
90
|
+
update: { $setOnInsert: { doc_key: docKey, changed_at: chg.changed_at, change } },
|
|
82
91
|
upsert: true
|
|
83
92
|
}
|
|
84
93
|
};
|
|
85
94
|
});
|
|
86
|
-
|
|
95
|
+
const prunes = [...winning].filter(([key, ts]) => stored.some((r) => r.doc_key === key && r.changed_at < ts)).map(([key, ts]) => ({
|
|
96
|
+
deleteMany: { filter: { doc_key: key, changed_at: { $lt: ts } } }
|
|
97
|
+
}));
|
|
98
|
+
if (inserts.length > 0) await this.store.bulkWrite(inserts, { ordered: false });
|
|
99
|
+
if (prunes.length > 0) await this.store.bulkWrite(prunes, { ordered: false });
|
|
87
100
|
}
|
|
88
101
|
async pull(sinceMs) {
|
|
89
102
|
const rows = await this.store.find({ changed_at: { $gt: sinceMs } }).sort({ changed_at: 1 }).toArray();
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
2
3
|
var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
3
4
|
constructor(options) {
|
|
4
5
|
this.store = options.collection;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* Convenience: open a MongoDB connection from a URI and return a ready
|
|
8
|
-
* adapter plus a `close()` to release it. Creates the
|
|
9
|
+
* adapter plus a `close()` to release it. Creates the indexes the adapter
|
|
10
|
+
* queries on.
|
|
9
11
|
*
|
|
10
12
|
* ```ts
|
|
11
13
|
* const { adapter, close } = await MongoSyncAdapter.connect({
|
|
@@ -21,6 +23,7 @@ var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
|
21
23
|
await client.connect();
|
|
22
24
|
const store = client.db(opts.db).collection(opts.collection ?? "taladb_changes");
|
|
23
25
|
await store.createIndex({ changed_at: 1 });
|
|
26
|
+
await store.createIndex({ doc_key: 1 });
|
|
24
27
|
return {
|
|
25
28
|
adapter: new _MongoSyncAdapter({ collection: store }),
|
|
26
29
|
close: () => client.close()
|
|
@@ -29,27 +32,37 @@ var MongoSyncAdapter = class _MongoSyncAdapter {
|
|
|
29
32
|
async push(changeset) {
|
|
30
33
|
const changes = JSON.parse(changeset);
|
|
31
34
|
if (changes.length === 0) return;
|
|
32
|
-
const
|
|
33
|
-
|
|
35
|
+
const docKeyOf = (chg) => `${chg.collection}::${chg.id}`;
|
|
36
|
+
const stored = await this.store.find(
|
|
37
|
+
{ doc_key: { $in: [...new Set(changes.map(docKeyOf))] } },
|
|
38
|
+
{ projection: { doc_key: 1, changed_at: 1 } }
|
|
39
|
+
).toArray();
|
|
40
|
+
const storedMax = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const row of stored) {
|
|
42
|
+
storedMax.set(row.doc_key, Math.max(storedMax.get(row.doc_key) ?? -1, row.changed_at));
|
|
43
|
+
}
|
|
44
|
+
const winning = new Map(storedMax);
|
|
45
|
+
for (const chg of changes) {
|
|
46
|
+
const key = docKeyOf(chg);
|
|
47
|
+
winning.set(key, Math.max(winning.get(key) ?? -1, chg.changed_at));
|
|
48
|
+
}
|
|
49
|
+
const inserts = changes.filter((chg) => chg.changed_at === winning.get(docKeyOf(chg))).map((chg) => {
|
|
50
|
+
const change = JSON.stringify(chg);
|
|
51
|
+
const docKey = docKeyOf(chg);
|
|
52
|
+
const digest = createHash("sha256").update(change).digest("hex").slice(0, 16);
|
|
34
53
|
return {
|
|
35
54
|
updateOne: {
|
|
36
|
-
filter: { _id:
|
|
37
|
-
update:
|
|
38
|
-
{
|
|
39
|
-
$replaceWith: {
|
|
40
|
-
$cond: [
|
|
41
|
-
{ $gt: [chg.changed_at, { $ifNull: ["$changed_at", -1] }] },
|
|
42
|
-
{ _id: key, changed_at: chg.changed_at, change: JSON.stringify(chg) },
|
|
43
|
-
"$$ROOT"
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
],
|
|
55
|
+
filter: { _id: `${docKey}::${chg.changed_at}::${digest}` },
|
|
56
|
+
update: { $setOnInsert: { doc_key: docKey, changed_at: chg.changed_at, change } },
|
|
48
57
|
upsert: true
|
|
49
58
|
}
|
|
50
59
|
};
|
|
51
60
|
});
|
|
52
|
-
|
|
61
|
+
const prunes = [...winning].filter(([key, ts]) => stored.some((r) => r.doc_key === key && r.changed_at < ts)).map(([key, ts]) => ({
|
|
62
|
+
deleteMany: { filter: { doc_key: key, changed_at: { $lt: ts } } }
|
|
63
|
+
}));
|
|
64
|
+
if (inserts.length > 0) await this.store.bulkWrite(inserts, { ordered: false });
|
|
65
|
+
if (prunes.length > 0) await this.store.bulkWrite(prunes, { ordered: false });
|
|
53
66
|
}
|
|
54
67
|
async pull(sinceMs) {
|
|
55
68
|
const rows = await this.store.find({ changed_at: { $gt: sinceMs } }).sort({ changed_at: 1 }).toArray();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taladb/sync-mongodb",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "MongoDB bidirectional sync adapter for TalaDB — sync a local TalaDB with a MongoDB collection, no intermediate API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"mongodb": ">=5.0.0",
|
|
42
|
-
"taladb": "^0.9.
|
|
42
|
+
"taladb": "^0.9.4"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^22.0.0",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"tsup": "^8.0.0",
|
|
48
48
|
"typescript": "^5.9.3",
|
|
49
49
|
"vitest": "^3.2.0",
|
|
50
|
-
"taladb": "0.9.
|
|
50
|
+
"taladb": "0.9.4"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "tsup",
|