@powersync/service-module-mongodb 0.0.0-dev-20241001150444
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/CHANGELOG.md +15 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dist/api/MongoRouteAPIAdapter.d.ts +22 -0
- package/dist/api/MongoRouteAPIAdapter.js +64 -0
- package/dist/api/MongoRouteAPIAdapter.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/module/MongoModule.d.ts +13 -0
- package/dist/module/MongoModule.js +46 -0
- package/dist/module/MongoModule.js.map +1 -0
- package/dist/replication/ChangeStream.d.ts +53 -0
- package/dist/replication/ChangeStream.js +389 -0
- package/dist/replication/ChangeStream.js.map +1 -0
- package/dist/replication/ChangeStreamReplicationJob.d.ts +16 -0
- package/dist/replication/ChangeStreamReplicationJob.js +90 -0
- package/dist/replication/ChangeStreamReplicationJob.js.map +1 -0
- package/dist/replication/ChangeStreamReplicator.d.ts +13 -0
- package/dist/replication/ChangeStreamReplicator.js +26 -0
- package/dist/replication/ChangeStreamReplicator.js.map +1 -0
- package/dist/replication/ConnectionManagerFactory.d.ts +9 -0
- package/dist/replication/ConnectionManagerFactory.js +21 -0
- package/dist/replication/ConnectionManagerFactory.js.map +1 -0
- package/dist/replication/MongoErrorRateLimiter.d.ts +11 -0
- package/dist/replication/MongoErrorRateLimiter.js +44 -0
- package/dist/replication/MongoErrorRateLimiter.js.map +1 -0
- package/dist/replication/MongoManager.d.ts +14 -0
- package/dist/replication/MongoManager.js +36 -0
- package/dist/replication/MongoManager.js.map +1 -0
- package/dist/replication/MongoRelation.d.ts +9 -0
- package/dist/replication/MongoRelation.js +174 -0
- package/dist/replication/MongoRelation.js.map +1 -0
- package/dist/replication/replication-index.d.ts +4 -0
- package/dist/replication/replication-index.js +5 -0
- package/dist/replication/replication-index.js.map +1 -0
- package/dist/types/types.d.ts +51 -0
- package/dist/types/types.js +37 -0
- package/dist/types/types.js.map +1 -0
- package/package.json +47 -0
- package/src/api/MongoRouteAPIAdapter.ts +86 -0
- package/src/index.ts +5 -0
- package/src/module/MongoModule.ts +52 -0
- package/src/replication/ChangeStream.ts +503 -0
- package/src/replication/ChangeStreamReplicationJob.ts +104 -0
- package/src/replication/ChangeStreamReplicator.ts +36 -0
- package/src/replication/ConnectionManagerFactory.ts +27 -0
- package/src/replication/MongoErrorRateLimiter.ts +45 -0
- package/src/replication/MongoManager.ts +47 -0
- package/src/replication/MongoRelation.ts +156 -0
- package/src/replication/replication-index.ts +4 -0
- package/src/types/types.ts +65 -0
- package/test/src/change_stream.test.ts +306 -0
- package/test/src/change_stream_utils.ts +148 -0
- package/test/src/env.ts +7 -0
- package/test/src/mongo_test.test.ts +219 -0
- package/test/src/setup.ts +7 -0
- package/test/src/util.ts +52 -0
- package/test/tsconfig.json +28 -0
- package/tsconfig.json +28 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { setTimeout } from 'timers/promises';
|
|
2
|
+
export class MongoErrorRateLimiter {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.nextAllowed = Date.now();
|
|
5
|
+
}
|
|
6
|
+
async waitUntilAllowed(options) {
|
|
7
|
+
const delay = Math.max(0, this.nextAllowed - Date.now());
|
|
8
|
+
// Minimum delay between connections, even without errors
|
|
9
|
+
this.setDelay(500);
|
|
10
|
+
await setTimeout(delay, undefined, { signal: options?.signal });
|
|
11
|
+
}
|
|
12
|
+
mayPing() {
|
|
13
|
+
return Date.now() >= this.nextAllowed;
|
|
14
|
+
}
|
|
15
|
+
reportError(e) {
|
|
16
|
+
// FIXME: Check mongodb-specific requirements
|
|
17
|
+
const message = e.message ?? '';
|
|
18
|
+
if (message.includes('password authentication failed')) {
|
|
19
|
+
// Wait 15 minutes, to avoid triggering Supabase's fail2ban
|
|
20
|
+
this.setDelay(900000);
|
|
21
|
+
}
|
|
22
|
+
else if (message.includes('ENOTFOUND')) {
|
|
23
|
+
// DNS lookup issue - incorrect URI or deleted instance
|
|
24
|
+
this.setDelay(120000);
|
|
25
|
+
}
|
|
26
|
+
else if (message.includes('ECONNREFUSED')) {
|
|
27
|
+
// Could be fail2ban or similar
|
|
28
|
+
this.setDelay(120000);
|
|
29
|
+
}
|
|
30
|
+
else if (message.includes('Unable to do postgres query on ended pool') ||
|
|
31
|
+
message.includes('Postgres unexpectedly closed connection')) {
|
|
32
|
+
// Connection timed out - ignore / immediately retry
|
|
33
|
+
// We don't explicitly set the delay to 0, since there could have been another error that
|
|
34
|
+
// we need to respect.
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.setDelay(30000);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
setDelay(delay) {
|
|
41
|
+
this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=MongoErrorRateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoErrorRateLimiter.js","sourceRoot":"","sources":["../../src/replication/MongoErrorRateLimiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,OAAO,qBAAqB;IAAlC;QACE,gBAAW,GAAW,IAAI,CAAC,GAAG,EAAE,CAAC;IAwCnC,CAAC;IAtCC,KAAK,CAAC,gBAAgB,CAAC,OAA0D;QAC/E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACzD,yDAAyD;QACzD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACnB,MAAM,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,WAAW,CAAC;IACxC,CAAC;IAED,WAAW,CAAC,CAAM;QAChB,6CAA6C;QAC7C,MAAM,OAAO,GAAI,CAAC,CAAC,OAAkB,IAAI,EAAE,CAAC;QAC5C,IAAI,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE;YACtD,2DAA2D;YAC3D,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;SACxB;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE;YACxC,uDAAuD;YACvD,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;SACxB;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;YAC3C,+BAA+B;YAC/B,IAAI,CAAC,QAAQ,CAAC,MAAO,CAAC,CAAC;SACxB;aAAM,IACL,OAAO,CAAC,QAAQ,CAAC,2CAA2C,CAAC;YAC7D,OAAO,CAAC,QAAQ,CAAC,yCAAyC,CAAC,EAC3D;YACA,oDAAoD;YACpD,yFAAyF;YACzF,sBAAsB;SACvB;aAAM;YACL,IAAI,CAAC,QAAQ,CAAC,KAAM,CAAC,CAAC;SACvB;IACH,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as mongo from 'mongodb';
|
|
2
|
+
import { NormalizedMongoConnectionConfig } from '../types/types.js';
|
|
3
|
+
export declare class MongoManager {
|
|
4
|
+
options: NormalizedMongoConnectionConfig;
|
|
5
|
+
/**
|
|
6
|
+
* Do not use this for any transactions.
|
|
7
|
+
*/
|
|
8
|
+
readonly client: mongo.MongoClient;
|
|
9
|
+
readonly db: mongo.Db;
|
|
10
|
+
constructor(options: NormalizedMongoConnectionConfig);
|
|
11
|
+
get connectionTag(): string;
|
|
12
|
+
end(): Promise<void>;
|
|
13
|
+
destroy(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as mongo from 'mongodb';
|
|
2
|
+
export class MongoManager {
|
|
3
|
+
constructor(options) {
|
|
4
|
+
this.options = options;
|
|
5
|
+
// The pool is lazy - no connections are opened until a query is performed.
|
|
6
|
+
this.client = new mongo.MongoClient(options.uri, {
|
|
7
|
+
auth: {
|
|
8
|
+
username: options.username,
|
|
9
|
+
password: options.password
|
|
10
|
+
},
|
|
11
|
+
// Time for connection to timeout
|
|
12
|
+
connectTimeoutMS: 5000,
|
|
13
|
+
// Time for individual requests to timeout
|
|
14
|
+
socketTimeoutMS: 60000,
|
|
15
|
+
// How long to wait for new primary selection
|
|
16
|
+
serverSelectionTimeoutMS: 30000,
|
|
17
|
+
// Avoid too many connections:
|
|
18
|
+
// 1. It can overwhelm the source database.
|
|
19
|
+
// 2. Processing too many queries in parallel can cause the process to run out of memory.
|
|
20
|
+
maxPoolSize: 8,
|
|
21
|
+
maxConnecting: 3,
|
|
22
|
+
maxIdleTimeMS: 60000
|
|
23
|
+
});
|
|
24
|
+
this.db = this.client.db(options.database, {});
|
|
25
|
+
}
|
|
26
|
+
get connectionTag() {
|
|
27
|
+
return this.options.tag;
|
|
28
|
+
}
|
|
29
|
+
async end() {
|
|
30
|
+
await this.client.close();
|
|
31
|
+
}
|
|
32
|
+
async destroy() {
|
|
33
|
+
// TODO: Implement?
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=MongoManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoManager.js","sourceRoot":"","sources":["../../src/replication/MongoManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAGjC,MAAM,OAAO,YAAY;IAOvB,YAAmB,OAAwC;QAAxC,YAAO,GAAP,OAAO,CAAiC;QACzD,2EAA2E;QAC3E,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE;YAC/C,IAAI,EAAE;gBACJ,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B;YACD,iCAAiC;YACjC,gBAAgB,EAAE,IAAK;YACvB,0CAA0C;YAC1C,eAAe,EAAE,KAAM;YACvB,6CAA6C;YAC7C,wBAAwB,EAAE,KAAM;YAEhC,8BAA8B;YAC9B,2CAA2C;YAC3C,yFAAyF;YACzF,WAAW,EAAE,CAAC;YAEd,aAAa,EAAE,CAAC;YAChB,aAAa,EAAE,KAAM;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,mBAAmB;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { storage } from '@powersync/service-core';
|
|
2
|
+
import { SqliteRow, SqliteValue } from '@powersync/service-sync-rules';
|
|
3
|
+
import * as mongo from 'mongodb';
|
|
4
|
+
export declare function getMongoRelation(source: mongo.ChangeStreamNameSpace): storage.SourceEntityDescriptor;
|
|
5
|
+
export declare function getMongoLsn(timestamp: mongo.Timestamp): string;
|
|
6
|
+
export declare function mongoLsnToTimestamp(lsn: string | null): mongo.BSON.Timestamp | null;
|
|
7
|
+
export declare function constructAfterRecord(document: mongo.Document): SqliteRow;
|
|
8
|
+
export declare function toMongoSyncRulesValue(data: any): SqliteValue;
|
|
9
|
+
export declare function createCheckpoint(client: mongo.MongoClient, db: mongo.Db): Promise<string>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import * as mongo from 'mongodb';
|
|
2
|
+
import { JSONBig, JsonContainer } from '@powersync/service-jsonbig';
|
|
3
|
+
export function getMongoRelation(source) {
|
|
4
|
+
return {
|
|
5
|
+
name: source.coll,
|
|
6
|
+
schema: source.db,
|
|
7
|
+
objectId: source.coll,
|
|
8
|
+
replicationColumns: [{ name: '_id' }]
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function getMongoLsn(timestamp) {
|
|
12
|
+
const a = timestamp.high.toString(16).padStart(8, '0');
|
|
13
|
+
const b = timestamp.low.toString(16).padStart(8, '0');
|
|
14
|
+
return a + b;
|
|
15
|
+
}
|
|
16
|
+
export function mongoLsnToTimestamp(lsn) {
|
|
17
|
+
if (lsn == null) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const a = parseInt(lsn.substring(0, 8), 16);
|
|
21
|
+
const b = parseInt(lsn.substring(8, 16), 16);
|
|
22
|
+
return mongo.Timestamp.fromBits(b, a);
|
|
23
|
+
}
|
|
24
|
+
export function constructAfterRecord(document) {
|
|
25
|
+
let record = {};
|
|
26
|
+
for (let key of Object.keys(document)) {
|
|
27
|
+
record[key] = toMongoSyncRulesValue(document[key]);
|
|
28
|
+
}
|
|
29
|
+
return record;
|
|
30
|
+
}
|
|
31
|
+
export function toMongoSyncRulesValue(data) {
|
|
32
|
+
const autoBigNum = true;
|
|
33
|
+
if (data == null) {
|
|
34
|
+
// null or undefined
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
else if (typeof data == 'string') {
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
else if (typeof data == 'number') {
|
|
41
|
+
if (Number.isInteger(data) && autoBigNum) {
|
|
42
|
+
return BigInt(data);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (typeof data == 'bigint') {
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
else if (typeof data == 'boolean') {
|
|
52
|
+
return data ? 1n : 0n;
|
|
53
|
+
}
|
|
54
|
+
else if (data instanceof mongo.ObjectId) {
|
|
55
|
+
return data.toHexString();
|
|
56
|
+
}
|
|
57
|
+
else if (data instanceof mongo.UUID) {
|
|
58
|
+
return data.toHexString();
|
|
59
|
+
}
|
|
60
|
+
else if (data instanceof Date) {
|
|
61
|
+
return data.toISOString().replace('T', ' ');
|
|
62
|
+
}
|
|
63
|
+
else if (data instanceof mongo.Binary) {
|
|
64
|
+
return new Uint8Array(data.buffer);
|
|
65
|
+
}
|
|
66
|
+
else if (data instanceof mongo.Long) {
|
|
67
|
+
return data.toBigInt();
|
|
68
|
+
}
|
|
69
|
+
else if (Array.isArray(data)) {
|
|
70
|
+
// We may be able to avoid some parse + stringify cycles here for JsonSqliteContainer.
|
|
71
|
+
return JSONBig.stringify(data.map((element) => filterJsonData(element)));
|
|
72
|
+
}
|
|
73
|
+
else if (data instanceof Uint8Array) {
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
else if (data instanceof JsonContainer) {
|
|
77
|
+
return data.toString();
|
|
78
|
+
}
|
|
79
|
+
else if (typeof data == 'object') {
|
|
80
|
+
let record = {};
|
|
81
|
+
for (let key of Object.keys(data)) {
|
|
82
|
+
record[key] = filterJsonData(data[key]);
|
|
83
|
+
}
|
|
84
|
+
return JSONBig.stringify(record);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const DEPTH_LIMIT = 20;
|
|
91
|
+
function filterJsonData(data, depth = 0) {
|
|
92
|
+
const autoBigNum = true;
|
|
93
|
+
if (depth > DEPTH_LIMIT) {
|
|
94
|
+
// This is primarily to prevent infinite recursion
|
|
95
|
+
throw new Error(`json nested object depth exceeds the limit of ${DEPTH_LIMIT}`);
|
|
96
|
+
}
|
|
97
|
+
if (data == null) {
|
|
98
|
+
return data; // null or undefined
|
|
99
|
+
}
|
|
100
|
+
else if (typeof data == 'string') {
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
103
|
+
else if (typeof data == 'number') {
|
|
104
|
+
if (autoBigNum && Number.isInteger(data)) {
|
|
105
|
+
return BigInt(data);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (typeof data == 'boolean') {
|
|
112
|
+
return data ? 1n : 0n;
|
|
113
|
+
}
|
|
114
|
+
else if (typeof data == 'bigint') {
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
else if (data instanceof Date) {
|
|
118
|
+
return data.toISOString().replace('T', ' ');
|
|
119
|
+
}
|
|
120
|
+
else if (data instanceof mongo.ObjectId) {
|
|
121
|
+
return data.toHexString();
|
|
122
|
+
}
|
|
123
|
+
else if (data instanceof mongo.UUID) {
|
|
124
|
+
return data.toHexString();
|
|
125
|
+
}
|
|
126
|
+
else if (data instanceof mongo.Binary) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
else if (data instanceof mongo.Long) {
|
|
130
|
+
return data.toBigInt();
|
|
131
|
+
}
|
|
132
|
+
else if (Array.isArray(data)) {
|
|
133
|
+
return data.map((element) => filterJsonData(element, depth + 1));
|
|
134
|
+
}
|
|
135
|
+
else if (ArrayBuffer.isView(data)) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
else if (data instanceof JsonContainer) {
|
|
139
|
+
// Can be stringified directly when using our JSONBig implementation
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
else if (typeof data == 'object') {
|
|
143
|
+
let record = {};
|
|
144
|
+
for (let key of Object.keys(data)) {
|
|
145
|
+
record[key] = filterJsonData(data[key], depth + 1);
|
|
146
|
+
}
|
|
147
|
+
return record;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function createCheckpoint(client, db) {
|
|
154
|
+
const session = client.startSession();
|
|
155
|
+
try {
|
|
156
|
+
const result = await db.collection('_powersync_checkpoints').findOneAndUpdate({
|
|
157
|
+
_id: 'checkpoint'
|
|
158
|
+
}, {
|
|
159
|
+
$inc: { i: 1 }
|
|
160
|
+
}, {
|
|
161
|
+
upsert: true,
|
|
162
|
+
returnDocument: 'after',
|
|
163
|
+
session
|
|
164
|
+
});
|
|
165
|
+
const time = session.operationTime;
|
|
166
|
+
// console.log('marked checkpoint at', time, getMongoLsn(time));
|
|
167
|
+
// TODO: Use the above when we support custom write checkpoints
|
|
168
|
+
return getMongoLsn(time);
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
await session.endSession();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=MongoRelation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoRelation.js","sourceRoot":"","sources":["../../src/replication/MongoRelation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replication-index.js","sourceRoot":"","sources":["../../src/replication/replication-index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as t from 'ts-codec';
|
|
2
|
+
export declare const MONGO_CONNECTION_TYPE: "mongodb";
|
|
3
|
+
export interface NormalizedMongoConnectionConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
tag: string;
|
|
6
|
+
uri: string;
|
|
7
|
+
database: string;
|
|
8
|
+
username?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const MongoConnectionConfig: t.Intersection<t.Codec<{
|
|
12
|
+
type: string;
|
|
13
|
+
id?: string | undefined;
|
|
14
|
+
tag?: string | undefined;
|
|
15
|
+
debug_api?: boolean | undefined;
|
|
16
|
+
}, {
|
|
17
|
+
type: string;
|
|
18
|
+
id?: string | undefined;
|
|
19
|
+
tag?: string | undefined;
|
|
20
|
+
debug_api?: boolean | undefined;
|
|
21
|
+
}, string, t.CodecProps>, t.ObjectCodec<{
|
|
22
|
+
type: t.LiteralCodec<"mongodb">;
|
|
23
|
+
/** Unique identifier for the connection - optional when a single connection is present. */
|
|
24
|
+
id: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
25
|
+
/** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */
|
|
26
|
+
tag: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
27
|
+
uri: t.IdentityCodec<t.CodecType.String>;
|
|
28
|
+
username: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
29
|
+
password: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
30
|
+
database: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
31
|
+
}>>;
|
|
32
|
+
/**
|
|
33
|
+
* Config input specified when starting services
|
|
34
|
+
*/
|
|
35
|
+
export type MongoConnectionConfig = t.Decoded<typeof MongoConnectionConfig>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolved version of {@link MongoConnectionConfig}
|
|
38
|
+
*/
|
|
39
|
+
export type ResolvedConnectionConfig = MongoConnectionConfig & NormalizedMongoConnectionConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Validate and normalize connection options.
|
|
42
|
+
*
|
|
43
|
+
* Returns destructured options.
|
|
44
|
+
*/
|
|
45
|
+
export declare function normalizeConnectionConfig(options: MongoConnectionConfig): NormalizedMongoConnectionConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Construct a mongodb URI, without username, password or ssl options.
|
|
48
|
+
*
|
|
49
|
+
* Only contains hostname, port, database.
|
|
50
|
+
*/
|
|
51
|
+
export declare function baseUri(options: NormalizedMongoConnectionConfig): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { normalizeMongoConfig } from '@powersync/service-core';
|
|
2
|
+
import * as service_types from '@powersync/service-types';
|
|
3
|
+
import * as t from 'ts-codec';
|
|
4
|
+
export const MONGO_CONNECTION_TYPE = 'mongodb';
|
|
5
|
+
export const MongoConnectionConfig = service_types.configFile.dataSourceConfig.and(t.object({
|
|
6
|
+
type: t.literal(MONGO_CONNECTION_TYPE),
|
|
7
|
+
/** Unique identifier for the connection - optional when a single connection is present. */
|
|
8
|
+
id: t.string.optional(),
|
|
9
|
+
/** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */
|
|
10
|
+
tag: t.string.optional(),
|
|
11
|
+
uri: t.string,
|
|
12
|
+
username: t.string.optional(),
|
|
13
|
+
password: t.string.optional(),
|
|
14
|
+
database: t.string.optional()
|
|
15
|
+
}));
|
|
16
|
+
/**
|
|
17
|
+
* Validate and normalize connection options.
|
|
18
|
+
*
|
|
19
|
+
* Returns destructured options.
|
|
20
|
+
*/
|
|
21
|
+
export function normalizeConnectionConfig(options) {
|
|
22
|
+
const base = normalizeMongoConfig(options);
|
|
23
|
+
return {
|
|
24
|
+
id: options.id ?? 'default',
|
|
25
|
+
tag: options.tag ?? 'default',
|
|
26
|
+
...base
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Construct a mongodb URI, without username, password or ssl options.
|
|
31
|
+
*
|
|
32
|
+
* Only contains hostname, port, database.
|
|
33
|
+
*/
|
|
34
|
+
export function baseUri(options) {
|
|
35
|
+
return options.uri;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,aAAa,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAkB,CAAC;AAaxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG,CAChF,CAAC,CAAC,MAAM,CAAC;IACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC;IACtC,2FAA2F;IAC3F,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IACvB,8FAA8F;IAC9F,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IACxB,GAAG,EAAE,CAAC,CAAC,MAAM;IACb,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;CAC9B,CAAC,CACH,CAAC;AAYF;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA8B;IACtE,MAAM,IAAI,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;QAC3B,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,SAAS;QAE7B,GAAG,IAAI;KACR,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,OAAwC;IAC9D,OAAO,OAAO,CAAC,GAAG,CAAC;AACrB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@powersync/service-module-mongodb",
|
|
3
|
+
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
|
+
"types": "dist/index.d.ts",
|
|
5
|
+
"version": "0.0.0-dev-20241001150444",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./types": {
|
|
19
|
+
"import": "./dist/types/types.js",
|
|
20
|
+
"require": "./dist/types/types.js",
|
|
21
|
+
"default": "./dist/types/types.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"mongodb": "^6.7.0",
|
|
26
|
+
"ts-codec": "^1.2.2",
|
|
27
|
+
"uuid": "^9.0.1",
|
|
28
|
+
"uri-js": "^4.4.1",
|
|
29
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20241001150444",
|
|
30
|
+
"@powersync/service-core": "0.0.0-dev-20241001150444",
|
|
31
|
+
"@powersync/service-jsonbig": "0.17.10",
|
|
32
|
+
"@powersync/service-sync-rules": "0.0.0-dev-20241001150444",
|
|
33
|
+
"@powersync/service-types": "0.0.0-dev-20241001150444"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/uuid": "^9.0.4",
|
|
37
|
+
"typescript": "^5.2.2",
|
|
38
|
+
"vitest": "^0.34.6",
|
|
39
|
+
"vite-tsconfig-paths": "^4.3.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -b",
|
|
43
|
+
"build:tests": "tsc -b test/tsconfig.json",
|
|
44
|
+
"clean": "rm -rf ./lib && tsc -b --clean",
|
|
45
|
+
"test": "vitest --no-threads"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { api, ParseSyncRulesOptions } from '@powersync/service-core';
|
|
2
|
+
import * as mongo from 'mongodb';
|
|
3
|
+
|
|
4
|
+
import * as sync_rules from '@powersync/service-sync-rules';
|
|
5
|
+
import * as service_types from '@powersync/service-types';
|
|
6
|
+
import * as types from '../types/types.js';
|
|
7
|
+
import { MongoManager } from '../replication/MongoManager.js';
|
|
8
|
+
import { createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js';
|
|
9
|
+
|
|
10
|
+
export class MongoRouteAPIAdapter implements api.RouteAPI {
|
|
11
|
+
protected client: mongo.MongoClient;
|
|
12
|
+
private db: mongo.Db;
|
|
13
|
+
|
|
14
|
+
connectionTag: string;
|
|
15
|
+
defaultSchema: string;
|
|
16
|
+
|
|
17
|
+
constructor(protected config: types.ResolvedConnectionConfig) {
|
|
18
|
+
const manager = new MongoManager(config);
|
|
19
|
+
this.client = manager.client;
|
|
20
|
+
this.db = manager.db;
|
|
21
|
+
this.defaultSchema = manager.db.databaseName;
|
|
22
|
+
this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getParseSyncRulesOptions(): ParseSyncRulesOptions {
|
|
26
|
+
return {
|
|
27
|
+
defaultSchema: this.defaultSchema
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async shutdown(): Promise<void> {
|
|
32
|
+
await this.client.close();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getSourceConfig(): Promise<service_types.configFile.DataSourceConfig> {
|
|
36
|
+
return this.config;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getConnectionStatus(): Promise<service_types.ConnectionStatusV2> {
|
|
40
|
+
// TODO: Implement
|
|
41
|
+
const base = {
|
|
42
|
+
id: this.config.id,
|
|
43
|
+
uri: types.baseUri(this.config)
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
...base,
|
|
47
|
+
connected: true,
|
|
48
|
+
errors: []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async executeQuery(query: string, params: any[]): Promise<service_types.internal_routes.ExecuteSqlResponse> {
|
|
53
|
+
return service_types.internal_routes.ExecuteSqlResponse.encode({
|
|
54
|
+
results: {
|
|
55
|
+
columns: [],
|
|
56
|
+
rows: []
|
|
57
|
+
},
|
|
58
|
+
success: false,
|
|
59
|
+
error: 'SQL querying is not supported for MongoDB'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async getDebugTablesInfo(
|
|
64
|
+
tablePatterns: sync_rules.TablePattern[],
|
|
65
|
+
sqlSyncRules: sync_rules.SqlSyncRules
|
|
66
|
+
): Promise<api.PatternResult[]> {
|
|
67
|
+
// TODO: Implement
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getReplicationLag(syncRulesId: string): Promise<number> {
|
|
72
|
+
// TODO: Implement
|
|
73
|
+
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async getReplicationHead(): Promise<string> {
|
|
78
|
+
return createCheckpoint(this.client, this.db);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getConnectionSchema(): Promise<service_types.DatabaseSchema[]> {
|
|
82
|
+
// TODO: Implement
|
|
83
|
+
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { api, ConfigurationFileSyncRulesProvider, replication, system, TearDownOptions } from '@powersync/service-core';
|
|
2
|
+
import { MongoRouteAPIAdapter } from '../api/MongoRouteAPIAdapter.js';
|
|
3
|
+
import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js';
|
|
4
|
+
import { MongoErrorRateLimiter } from '../replication/MongoErrorRateLimiter.js';
|
|
5
|
+
import { ChangeStreamReplicator } from '../replication/ChangeStreamReplicator.js';
|
|
6
|
+
import * as types from '../types/types.js';
|
|
7
|
+
|
|
8
|
+
export class MongoModule extends replication.ReplicationModule<types.MongoConnectionConfig> {
|
|
9
|
+
constructor() {
|
|
10
|
+
super({
|
|
11
|
+
name: 'MongoDB',
|
|
12
|
+
type: types.MONGO_CONNECTION_TYPE,
|
|
13
|
+
configSchema: types.MongoConnectionConfig
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async initialize(context: system.ServiceContextContainer): Promise<void> {
|
|
18
|
+
await super.initialize(context);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected createRouteAPIAdapter(): api.RouteAPI {
|
|
22
|
+
return new MongoRouteAPIAdapter(this.resolveConfig(this.decodedConfig!));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator {
|
|
26
|
+
const normalisedConfig = this.resolveConfig(this.decodedConfig!);
|
|
27
|
+
const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules);
|
|
28
|
+
const connectionFactory = new ConnectionManagerFactory(normalisedConfig);
|
|
29
|
+
|
|
30
|
+
return new ChangeStreamReplicator({
|
|
31
|
+
id: this.getDefaultId(normalisedConfig.database ?? ''),
|
|
32
|
+
syncRuleProvider: syncRuleProvider,
|
|
33
|
+
storageEngine: context.storageEngine,
|
|
34
|
+
connectionFactory: connectionFactory,
|
|
35
|
+
rateLimiter: new MongoErrorRateLimiter()
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Combines base config with normalized connection settings
|
|
41
|
+
*/
|
|
42
|
+
private resolveConfig(config: types.MongoConnectionConfig): types.ResolvedConnectionConfig {
|
|
43
|
+
return {
|
|
44
|
+
...config,
|
|
45
|
+
...types.normalizeConnectionConfig(config)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async teardown(options: TearDownOptions): Promise<void> {
|
|
50
|
+
// TODO: Implement?
|
|
51
|
+
}
|
|
52
|
+
}
|