@powersync/service-module-mongodb-storage 0.0.0-dev-20250820110726 → 0.0.0-dev-20250825132649

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 (55) hide show
  1. package/CHANGELOG.md +9 -7
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/migrations/db/migrations/1752661449910-connection-reporting.js +0 -12
  6. package/dist/migrations/db/migrations/1752661449910-connection-reporting.js.map +1 -1
  7. package/dist/storage/MongoBucketStorage.js +1 -1
  8. package/dist/storage/MongoBucketStorage.js.map +1 -1
  9. package/dist/storage/MongoReportStorage.d.ts +2 -3
  10. package/dist/storage/MongoReportStorage.js +2 -18
  11. package/dist/storage/MongoReportStorage.js.map +1 -1
  12. package/dist/storage/implementation/MongoBucketBatch.js +1 -1
  13. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  14. package/dist/storage/implementation/MongoSyncBucketStorage.js +1 -1
  15. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  16. package/dist/storage/implementation/PersistedBatch.js +1 -1
  17. package/dist/storage/implementation/PersistedBatch.js.map +1 -1
  18. package/dist/storage/storage-index.d.ts +2 -2
  19. package/dist/storage/storage-index.js +2 -2
  20. package/dist/storage/storage-index.js.map +1 -1
  21. package/dist/utils/test-utils.d.ts +11 -0
  22. package/dist/utils/test-utils.js +40 -0
  23. package/dist/utils/test-utils.js.map +1 -0
  24. package/dist/{storage/implementation → utils}/util.d.ts +1 -6
  25. package/dist/{storage/implementation → utils}/util.js +0 -15
  26. package/dist/utils/util.js.map +1 -0
  27. package/dist/utils/utils-index.d.ts +2 -0
  28. package/dist/utils/utils-index.js +3 -0
  29. package/dist/utils/utils-index.js.map +1 -0
  30. package/package.json +8 -8
  31. package/src/index.ts +1 -0
  32. package/src/migrations/db/migrations/1752661449910-connection-reporting.ts +0 -12
  33. package/src/storage/MongoBucketStorage.ts +1 -1
  34. package/src/storage/MongoReportStorage.ts +5 -22
  35. package/src/storage/implementation/MongoBucketBatch.ts +1 -1
  36. package/src/storage/implementation/MongoSyncBucketStorage.ts +1 -1
  37. package/src/storage/implementation/PersistedBatch.ts +1 -1
  38. package/src/storage/storage-index.ts +2 -2
  39. package/src/utils/test-utils.ts +55 -0
  40. package/src/{storage/implementation → utils}/util.ts +1 -17
  41. package/src/utils/utils-index.ts +2 -0
  42. package/test/src/__snapshots__/connection-report-storage.test.ts.snap +31 -1
  43. package/test/src/__snapshots__/storage_sync.test.ts.snap +12 -11
  44. package/test/src/connection-report-storage.test.ts +16 -28
  45. package/test/src/util.ts +3 -5
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.d.ts +0 -7
  48. package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.js +0 -13
  49. package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.js.map +0 -1
  50. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.d.ts +0 -7
  51. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js +0 -18
  52. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js.map +0 -1
  53. package/dist/storage/implementation/util.js.map +0 -1
  54. package/src/storage/implementation/MongoTestReportStorageFactoryGenerator.ts +0 -22
  55. package/src/storage/implementation/MongoTestStorageFactoryGenerator.ts +0 -28
@@ -0,0 +1,40 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { PowerSyncMongo } from '../storage/implementation/db.js';
3
+ import { MongoReportStorage } from '../storage/MongoReportStorage.js';
4
+ import { MongoBucketStorage } from '../storage/MongoBucketStorage.js';
5
+ export function mongoTestStorageFactoryGenerator(factoryOptions) {
6
+ return async (options) => {
7
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
8
+ // None of the tests insert data into this collection, so it was never created
9
+ if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) {
10
+ await db.db.createCollection('bucket_parameters');
11
+ }
12
+ // Full migrations are not currently run for tests, so we manually create this
13
+ await db.createCheckpointEventsCollection();
14
+ if (!options?.doNotClear) {
15
+ await db.clear();
16
+ }
17
+ return new MongoBucketStorage(db, { slot_name_prefix: 'test_' });
18
+ };
19
+ }
20
+ export function mongoTestReportStorageFactoryGenerator(factoryOptions) {
21
+ return async (options) => {
22
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
23
+ await db.createConnectionReportingCollection();
24
+ if (!options?.doNotClear) {
25
+ await db.clear();
26
+ }
27
+ return new MongoReportStorage(db);
28
+ };
29
+ }
30
+ export const connectMongoForTests = (url, isCI) => {
31
+ // Short timeout for tests, to fail fast when the server is not available.
32
+ // Slightly longer timeouts for CI, to avoid arbitrary test failures
33
+ const client = new mongo.MongoClient(url, {
34
+ connectTimeoutMS: isCI ? 15_000 : 5_000,
35
+ socketTimeoutMS: isCI ? 15_000 : 5_000,
36
+ serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
37
+ });
38
+ return new PowerSyncMongo(client);
39
+ };
40
+ //# sourceMappingURL=test-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/utils/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAOtE,MAAM,UAAU,gCAAgC,CAAC,cAAuC;IACtF,OAAO,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC5C,MAAM,EAAE,GAAG,oBAAoB,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;QAEzE,8EAA8E;QAC9E,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC5F,MAAM,EAAE,CAAC,EAAE,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;QACpD,CAAC;QAED,8EAA8E;QAC9E,MAAM,EAAE,CAAC,gCAAgC,EAAE,CAAC;QAE5C,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sCAAsC,CAAC,cAAuC;IAC5F,OAAO,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC5C,MAAM,EAAE,GAAG,oBAAoB,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;QAEzE,MAAM,EAAE,CAAC,mCAAmC,EAAE,CAAC;QAE/C,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAAE,IAAa,EAAE,EAAE;IACjE,0EAA0E;IAC1E,oEAAoE;IACpE,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACxC,gBAAgB,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;QACvC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;QACtC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;KAChD,CAAC,CAAC;IACH,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC,CAAC"}
@@ -1,8 +1,7 @@
1
1
  import * as bson from 'bson';
2
2
  import { mongo } from '@powersync/lib-service-mongodb';
3
3
  import { storage, utils } from '@powersync/service-core';
4
- import { PowerSyncMongo } from './db.js';
5
- import { BucketDataDocument } from './models.js';
4
+ import { BucketDataDocument } from '../storage/implementation/models.js';
6
5
  export declare function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T>;
7
6
  export declare function generateSlotName(prefix: string, sync_rules_id: number): string;
8
7
  /**
@@ -22,8 +21,4 @@ export declare function readSingleBatch<T>(cursor: mongo.FindCursor<T>): Promise
22
21
  }>;
23
22
  export declare function mapOpEntry(row: BucketDataDocument): utils.OplogEntry;
24
23
  export declare function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId): string;
25
- /**
26
- * Helper for unit tests
27
- */
28
- export declare const connectMongoForTests: (url: string, isCI: boolean) => PowerSyncMongo;
29
24
  export declare function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.Timestamp): void;
@@ -1,9 +1,7 @@
1
1
  import * as bson from 'bson';
2
2
  import * as crypto from 'crypto';
3
3
  import * as uuid from 'uuid';
4
- import { mongo } from '@powersync/lib-service-mongodb';
5
4
  import { storage, utils } from '@powersync/service-core';
6
- import { PowerSyncMongo } from './db.js';
7
5
  import { ServiceAssertionError } from '@powersync/lib-services-framework';
8
6
  export function idPrefixFilter(prefix, rest) {
9
7
  let filter = {
@@ -96,19 +94,6 @@ export function replicaIdToSubkey(table, id) {
96
94
  return uuid.v5(repr, utils.ID_NAMESPACE);
97
95
  }
98
96
  }
99
- /**
100
- * Helper for unit tests
101
- */
102
- export const connectMongoForTests = (url, isCI) => {
103
- // Short timeout for tests, to fail fast when the server is not available.
104
- // Slightly longer timeouts for CI, to avoid arbitrary test failures
105
- const client = new mongo.MongoClient(url, {
106
- connectTimeoutMS: isCI ? 15_000 : 5_000,
107
- socketTimeoutMS: isCI ? 15_000 : 5_000,
108
- serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
109
- });
110
- return new PowerSyncMongo(client);
111
- };
112
97
  export function setSessionSnapshotTime(session, time) {
113
98
  // This is a workaround for the lack of direct support for snapshot reads in the MongoDB driver.
114
99
  if (!session.snapshotEnabled) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * as test_utils from './test-utils.js';
2
+ export * from './util.js';
@@ -0,0 +1,3 @@
1
+ export * as test_utils from './test-utils.js';
2
+ export * from './util.js';
3
+ //# sourceMappingURL=utils-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-index.js","sourceRoot":"","sources":["../../src/utils/utils-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAC9C,cAAc,WAAW,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@powersync/service-module-mongodb-storage",
3
3
  "repository": "https://github.com/powersync-ja/powersync-service",
4
4
  "types": "dist/index.d.ts",
5
- "version": "0.0.0-dev-20250820110726",
5
+ "version": "0.0.0-dev-20250825132649",
6
6
  "main": "dist/index.js",
7
7
  "license": "FSL-1.1-ALv2",
8
8
  "type": "module",
@@ -27,15 +27,15 @@
27
27
  "lru-cache": "^10.2.2",
28
28
  "ts-codec": "^1.3.0",
29
29
  "uuid": "^11.1.0",
30
- "@powersync/lib-service-mongodb": "0.0.0-dev-20250820110726",
31
- "@powersync/lib-services-framework": "0.0.0-dev-20250820110726",
32
- "@powersync/service-core": "0.0.0-dev-20250820110726",
33
- "@powersync/service-types": "0.0.0-dev-20250820110726",
34
- "@powersync/service-jsonbig": "0.0.0-dev-20250820110726",
35
- "@powersync/service-sync-rules": "0.0.0-dev-20250820110726"
30
+ "@powersync/lib-service-mongodb": "0.0.0-dev-20250825132649",
31
+ "@powersync/lib-services-framework": "0.0.0-dev-20250825132649",
32
+ "@powersync/service-core": "0.0.0-dev-20250825132649",
33
+ "@powersync/service-types": "0.0.0-dev-20250825132649",
34
+ "@powersync/service-jsonbig": "0.0.0-dev-20250825132649",
35
+ "@powersync/service-sync-rules": "0.0.0-dev-20250825132649"
36
36
  },
37
37
  "devDependencies": {
38
- "@powersync/service-core-tests": "0.0.0-dev-20250820110726"
38
+ "@powersync/service-core-tests": "0.0.0-dev-20250825132649"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b",
package/src/index.ts CHANGED
@@ -5,3 +5,4 @@ export * as storage from './storage/storage-index.js';
5
5
 
6
6
  export * from './types/types.js';
7
7
  export * as types from './types/types.js';
8
+ export * as utils from './utils/utils-index.js';
@@ -51,18 +51,6 @@ export const down: migrations.PowerSyncMigrationFunction = async (context) => {
51
51
  const db = storage.createPowerSyncMongo(configuration.storage as MongoStorageConfig);
52
52
 
53
53
  try {
54
- if (await db.connection_report_events.indexExists('connection_list_index')) {
55
- await db.connection_report_events.dropIndex('connection_list_index');
56
- }
57
- if (await db.connection_report_events.indexExists('connection_user_id_index')) {
58
- await db.connection_report_events.dropIndex('connection_user_id_index');
59
- }
60
- if (await db.connection_report_events.indexExists('connection_client_id_index')) {
61
- await db.connection_report_events.dropIndex('connection_client_id_index');
62
- }
63
- if (await db.connection_report_events.indexExists('connection_index')) {
64
- await db.connection_report_events.dropIndex('connection_index');
65
- }
66
54
  await db.db.dropCollection('connection_report_events');
67
55
  } finally {
68
56
  await db.client.close();
@@ -12,7 +12,7 @@ import { PowerSyncMongo } from './implementation/db.js';
12
12
  import { SyncRuleDocument } from './implementation/models.js';
13
13
  import { MongoPersistedSyncRulesContent } from './implementation/MongoPersistedSyncRulesContent.js';
14
14
  import { MongoSyncBucketStorage } from './implementation/MongoSyncBucketStorage.js';
15
- import { generateSlotName } from './implementation/util.js';
15
+ import { generateSlotName } from '../utils/util.js';
16
16
 
17
17
  export class MongoBucketStorage
18
18
  extends BaseObserver<storage.BucketStorageFactoryListener>
@@ -30,10 +30,10 @@ export class MongoReportStorage implements storage.ReportStorage {
30
30
 
31
31
  async getClientConnectionReports(
32
32
  data: event_types.ClientConnectionReportRequest
33
- ): Promise<event_types.ClientConnectionReport> {
33
+ ): Promise<event_types.ClientConnectionReportResponse> {
34
34
  const { start, end } = data;
35
35
  const result = await this.db.connection_report_events
36
- .aggregate<event_types.ClientConnectionReport>([
36
+ .aggregate<event_types.ClientConnectionReportResponse>([
37
37
  {
38
38
  $match: {
39
39
  connected_at: { $lte: end, $gte: start }
@@ -79,15 +79,13 @@ export class MongoReportStorage implements storage.ReportStorage {
79
79
  }
80
80
  );
81
81
  }
82
- async getConnectedClients(data: event_types.ClientConnectionsRequest): Promise<event_types.ClientConnectionReport> {
83
- const timeframeFilter = this.listConnectionsDateRange(data);
82
+ async getConnectedClients(): Promise<event_types.ClientConnectionReportResponse> {
84
83
  const result = await this.db.connection_report_events
85
- .aggregate<event_types.ClientConnectionReport>([
84
+ .aggregate<event_types.ClientConnectionReportResponse>([
86
85
  {
87
86
  $match: {
88
87
  disconnected_at: { $exists: false },
89
- jwt_exp: { $gt: new Date() },
90
- ...timeframeFilter
88
+ jwt_exp: { $gt: new Date() }
91
89
  }
92
90
  },
93
91
  this.connectionsFacetPipeline(),
@@ -177,19 +175,4 @@ export class MongoReportStorage implements storage.ReportStorage {
177
175
  }
178
176
  };
179
177
  }
180
-
181
- private listConnectionsDateRange(data: event_types.ClientConnectionsRequest) {
182
- const { range } = data;
183
- if (!range) {
184
- return undefined;
185
- }
186
- const endDate = data.range?.end ? new Date(data.range.end) : new Date();
187
- const startDate = new Date(range.start);
188
- return {
189
- connected_at: {
190
- $lte: endDate,
191
- $gte: startDate
192
- }
193
- };
194
- }
195
178
  }
@@ -28,7 +28,7 @@ import { MongoIdSequence } from './MongoIdSequence.js';
28
28
  import { batchCreateCustomWriteCheckpoints } from './MongoWriteCheckpointAPI.js';
29
29
  import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js';
30
30
  import { PersistedBatch } from './PersistedBatch.js';
31
- import { idPrefixFilter } from './util.js';
31
+ import { idPrefixFilter } from '../../utils/util.js';
32
32
 
33
33
  /**
34
34
  * 15MB
@@ -32,7 +32,7 @@ import { BucketDataDocument, BucketDataKey, BucketStateDocument, SourceKey, Sour
32
32
  import { MongoBucketBatch } from './MongoBucketBatch.js';
33
33
  import { MongoCompactor } from './MongoCompactor.js';
34
34
  import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
35
- import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from './util.js';
35
+ import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../utils/util.js';
36
36
  import { MongoParameterCompactor } from './MongoParameterCompactor.js';
37
37
 
38
38
  export class MongoSyncBucketStorage
@@ -16,7 +16,7 @@ import {
16
16
  CurrentDataDocument,
17
17
  SourceKey
18
18
  } from './models.js';
19
- import { replicaIdToSubkey } from './util.js';
19
+ import { replicaIdToSubkey } from '../../utils/util.js';
20
20
 
21
21
  /**
22
22
  * Maximum size of operations we write in a single transaction.
@@ -7,9 +7,9 @@ export * from './implementation/MongoPersistedSyncRulesContent.js';
7
7
  export * from './implementation/MongoStorageProvider.js';
8
8
  export * from './implementation/MongoSyncBucketStorage.js';
9
9
  export * from './implementation/MongoSyncRulesLock.js';
10
- export * from './implementation/MongoTestStorageFactoryGenerator.js';
11
10
  export * from './implementation/OperationBatch.js';
12
11
  export * from './implementation/PersistedBatch.js';
13
- export * from './implementation/util.js';
12
+ export * from '../utils/util.js';
14
13
  export * from './MongoBucketStorage.js';
15
14
  export * from './MongoReportStorage.js';
15
+ export * as test_utils from '../utils/test-utils.js';
@@ -0,0 +1,55 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { PowerSyncMongo } from '../storage/implementation/db.js';
3
+ import { TestStorageOptions } from '@powersync/service-core';
4
+ import { MongoReportStorage } from '../storage/MongoReportStorage.js';
5
+ import { MongoBucketStorage } from '../storage/MongoBucketStorage.js';
6
+
7
+ export type MongoTestStorageOptions = {
8
+ url: string;
9
+ isCI: boolean;
10
+ };
11
+
12
+ export function mongoTestStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
13
+ return async (options?: TestStorageOptions) => {
14
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
15
+
16
+ // None of the tests insert data into this collection, so it was never created
17
+ if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) {
18
+ await db.db.createCollection('bucket_parameters');
19
+ }
20
+
21
+ // Full migrations are not currently run for tests, so we manually create this
22
+ await db.createCheckpointEventsCollection();
23
+
24
+ if (!options?.doNotClear) {
25
+ await db.clear();
26
+ }
27
+
28
+ return new MongoBucketStorage(db, { slot_name_prefix: 'test_' });
29
+ };
30
+ }
31
+
32
+ export function mongoTestReportStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
33
+ return async (options?: TestStorageOptions) => {
34
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
35
+
36
+ await db.createConnectionReportingCollection();
37
+
38
+ if (!options?.doNotClear) {
39
+ await db.clear();
40
+ }
41
+
42
+ return new MongoReportStorage(db);
43
+ };
44
+ }
45
+
46
+ export const connectMongoForTests = (url: string, isCI: boolean) => {
47
+ // Short timeout for tests, to fail fast when the server is not available.
48
+ // Slightly longer timeouts for CI, to avoid arbitrary test failures
49
+ const client = new mongo.MongoClient(url, {
50
+ connectTimeoutMS: isCI ? 15_000 : 5_000,
51
+ socketTimeoutMS: isCI ? 15_000 : 5_000,
52
+ serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
53
+ });
54
+ return new PowerSyncMongo(client);
55
+ };
@@ -3,9 +3,7 @@ import * as crypto from 'crypto';
3
3
  import * as uuid from 'uuid';
4
4
  import { mongo } from '@powersync/lib-service-mongodb';
5
5
  import { storage, utils } from '@powersync/service-core';
6
-
7
- import { PowerSyncMongo } from './db.js';
8
- import { BucketDataDocument } from './models.js';
6
+ import { BucketDataDocument } from '../storage/implementation/models.js';
9
7
  import { ServiceAssertionError } from '@powersync/lib-services-framework';
10
8
 
11
9
  export function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T> {
@@ -104,20 +102,6 @@ export function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId):
104
102
  }
105
103
  }
106
104
 
107
- /**
108
- * Helper for unit tests
109
- */
110
- export const connectMongoForTests = (url: string, isCI: boolean) => {
111
- // Short timeout for tests, to fail fast when the server is not available.
112
- // Slightly longer timeouts for CI, to avoid arbitrary test failures
113
- const client = new mongo.MongoClient(url, {
114
- connectTimeoutMS: isCI ? 15_000 : 5_000,
115
- socketTimeoutMS: isCI ? 15_000 : 5_000,
116
- serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
117
- });
118
- return new PowerSyncMongo(client);
119
- };
120
-
121
105
  export function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.Timestamp) {
122
106
  // This is a workaround for the lack of direct support for snapshot reads in the MongoDB driver.
123
107
  if (!session.snapshotEnabled) {
@@ -0,0 +1,2 @@
1
+ export * as test_utils from './test-utils.js';
2
+ export * from './util.js';
@@ -45,6 +45,11 @@ exports[`SDK reporting storage > Should delete rows older than specified range 1
45
45
  "sdk": "powersync-js/1.24.5",
46
46
  "users": 1,
47
47
  },
48
+ {
49
+ "clients": 1,
50
+ "sdk": "unknown",
51
+ "users": 1,
52
+ },
48
53
  ],
49
54
  "users": 5,
50
55
  }
@@ -58,8 +63,18 @@ exports[`SDK reporting storage > Should show connected users with start range 1`
58
63
  "sdk": "powersync-dart/1.6.4",
59
64
  "users": 1,
60
65
  },
66
+ {
67
+ "clients": 1,
68
+ "sdk": "powersync-js/1.21.1",
69
+ "users": 1,
70
+ },
71
+ {
72
+ "clients": 1,
73
+ "sdk": "unknown",
74
+ "users": 1,
75
+ },
61
76
  ],
62
- "users": 1,
77
+ "users": 2,
63
78
  }
64
79
  `;
65
80
 
@@ -94,6 +109,11 @@ exports[`SDK reporting storage > Should show connection report data for user ove
94
109
  "sdk": "powersync-js/1.21.4",
95
110
  "users": 1,
96
111
  },
112
+ {
113
+ "clients": 1,
114
+ "sdk": "unknown",
115
+ "users": 1,
116
+ },
97
117
  ],
98
118
  "users": 3,
99
119
  }
@@ -137,6 +157,11 @@ exports[`SDK reporting storage > Should show connection report data for user ove
137
157
  "sdk": "powersync-js/1.24.5",
138
158
  "users": 1,
139
159
  },
160
+ {
161
+ "clients": 1,
162
+ "sdk": "unknown",
163
+ "users": 1,
164
+ },
140
165
  ],
141
166
  "users": 7,
142
167
  }
@@ -170,6 +195,11 @@ exports[`SDK reporting storage > Should show connection report data for user ove
170
195
  "sdk": "powersync-js/1.24.5",
171
196
  "users": 1,
172
197
  },
198
+ {
199
+ "clients": 1,
200
+ "sdk": "unknown",
201
+ "users": 1,
202
+ },
173
203
  ],
174
204
  "users": 5,
175
205
  }
@@ -39,7 +39,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = `
39
39
  "bucket": "mybucket[]",
40
40
  "data": [
41
41
  {
42
- "checksum": -93886621n,
42
+ "checksum": -93886621,
43
43
  "op": "CLEAR",
44
44
  "op_id": "2",
45
45
  },
@@ -74,7 +74,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = `
74
74
  "bucket": "mybucket[]",
75
75
  "data": [
76
76
  {
77
- "checksum": 1859363232n,
77
+ "checksum": 1859363232,
78
78
  "data": "{"id":"t1","description":"Test 1b"}",
79
79
  "object_id": "t1",
80
80
  "object_type": "test",
@@ -83,7 +83,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = `
83
83
  "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a",
84
84
  },
85
85
  {
86
- "checksum": 3028503153n,
86
+ "checksum": 3028503153,
87
87
  "data": "{"id":"t2","description":"Test 2b"}",
88
88
  "object_id": "t2",
89
89
  "object_type": "test",
@@ -203,6 +203,7 @@ exports[`sync - mongodb > sends checkpoint complete line for empty checkpoint 1`
203
203
  "next_after": "1",
204
204
  },
205
205
  },
206
+ null,
206
207
  {
207
208
  "checkpoint_complete": {
208
209
  "last_op_id": "1",
@@ -274,7 +275,7 @@ exports[`sync - mongodb > sync buckets in order 1`] = `
274
275
  "bucket": "b1[]",
275
276
  "data": [
276
277
  {
277
- "checksum": 2912868539n,
278
+ "checksum": 2912868539,
278
279
  "data": "{"id":"earlier","description":"Test 2"}",
279
280
  "object_id": "earlier",
280
281
  "object_type": "test",
@@ -299,7 +300,7 @@ exports[`sync - mongodb > sync buckets in order 1`] = `
299
300
  "bucket": "b0[]",
300
301
  "data": [
301
302
  {
302
- "checksum": 920318466n,
303
+ "checksum": 920318466,
303
304
  "data": "{"id":"t1","description":"Test 1"}",
304
305
  "object_id": "t1",
305
306
  "object_type": "test",
@@ -354,7 +355,7 @@ exports[`sync - mongodb > sync global data 1`] = `
354
355
  "bucket": "mybucket[]",
355
356
  "data": [
356
357
  {
357
- "checksum": 920318466n,
358
+ "checksum": 920318466,
358
359
  "data": "{"id":"t1","description":"Test 1"}",
359
360
  "object_id": "t1",
360
361
  "object_type": "test",
@@ -363,7 +364,7 @@ exports[`sync - mongodb > sync global data 1`] = `
363
364
  "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a",
364
365
  },
365
366
  {
366
- "checksum": 3280762209n,
367
+ "checksum": 3280762209,
367
368
  "data": "{"id":"t2","description":"Test 2"}",
368
369
  "object_id": "t2",
369
370
  "object_type": "test",
@@ -702,7 +703,7 @@ exports[`sync - mongodb > sync updates to data query only 2`] = `
702
703
  "bucket": "by_user["user1"]",
703
704
  "data": [
704
705
  {
705
- "checksum": 1418351250n,
706
+ "checksum": 1418351250,
706
707
  "data": "{"id":"list1","user_id":"user1","name":"User 1"}",
707
708
  "object_id": "list1",
708
709
  "object_type": "lists",
@@ -787,7 +788,7 @@ exports[`sync - mongodb > sync updates to global data 2`] = `
787
788
  "bucket": "mybucket[]",
788
789
  "data": [
789
790
  {
790
- "checksum": 920318466n,
791
+ "checksum": 920318466,
791
792
  "data": "{"id":"t1","description":"Test 1"}",
792
793
  "object_id": "t1",
793
794
  "object_type": "test",
@@ -836,7 +837,7 @@ exports[`sync - mongodb > sync updates to global data 3`] = `
836
837
  "bucket": "mybucket[]",
837
838
  "data": [
838
839
  {
839
- "checksum": 3280762209n,
840
+ "checksum": 3280762209,
840
841
  "data": "{"id":"t2","description":"Test 2"}",
841
842
  "object_id": "t2",
842
843
  "object_type": "test",
@@ -909,7 +910,7 @@ exports[`sync - mongodb > sync updates to parameter query + data 2`] = `
909
910
  "bucket": "by_user["user1"]",
910
911
  "data": [
911
912
  {
912
- "checksum": 1418351250n,
913
+ "checksum": 1418351250,
913
914
  "data": "{"id":"list1","user_id":"user1","name":"User 1"}",
914
915
  "object_id": "list1",
915
916
  "object_type": "lists",
@@ -68,6 +68,15 @@ describe('SDK reporting storage', async () => {
68
68
  jwt_exp: nowLess5minutes
69
69
  };
70
70
 
71
+ const user_old = {
72
+ user_id: 'user_one',
73
+ client_id: '',
74
+ connected_at: now,
75
+ sdk: 'unknown',
76
+ user_agent: 'Dart (flutter-web) Chrome/128 android',
77
+ jwt_exp: nowAdd5minutes
78
+ };
79
+
71
80
  const user_week = {
72
81
  user_id: 'user_week',
73
82
  client_id: 'client_week',
@@ -103,7 +112,8 @@ describe('SDK reporting storage', async () => {
103
112
  user_four,
104
113
  user_week,
105
114
  user_month,
106
- user_expired
115
+ user_expired,
116
+ user_old
107
117
  ]);
108
118
  }
109
119
 
@@ -119,34 +129,10 @@ describe('SDK reporting storage', async () => {
119
129
  await deleteData();
120
130
  });
121
131
  it('Should show connected users with start range', async () => {
122
- const current = await factory.getConnectedClients({
123
- range: {
124
- start: new Date(
125
- now.getFullYear(),
126
- now.getMonth(),
127
- now.getDate(),
128
- now.getHours(),
129
- now.getMinutes() - 1
130
- ).toISOString()
131
- }
132
- });
133
- expect(current).toMatchSnapshot();
134
- });
135
- it('Should show connected users with start range and end range', async () => {
136
- const current = await factory.getConnectedClients({
137
- range: {
138
- end: nowLess5minutes.toISOString(),
139
- start: new Date(
140
- now.getFullYear(),
141
- now.getMonth(),
142
- now.getDate(),
143
- now.getHours(),
144
- now.getMinutes() - 6
145
- ).toISOString()
146
- }
147
- });
132
+ const current = await factory.getConnectedClients();
148
133
  expect(current).toMatchSnapshot();
149
134
  });
135
+
150
136
  it('Should show connection report data for user over the past month', async () => {
151
137
  const sdk = await factory.getClientConnectionReports({
152
138
  start: monthAgo,
@@ -187,7 +173,9 @@ describe('SDK reporting storage', async () => {
187
173
  user_agent: user_one.user_agent
188
174
  });
189
175
 
190
- const sdk = await factory.db.connection_report_events.find({ user_id: user_one.user_id }).toArray();
176
+ const sdk = await factory.db.connection_report_events
177
+ .find({ user_id: user_one.user_id, client_id: user_one.client_id })
178
+ .toArray();
191
179
  expect(sdk).toHaveLength(1);
192
180
  expect(new Date(sdk[0].connected_at)).toEqual(newConnectAt);
193
181
  expect(new Date(sdk[0].jwt_exp!)).toEqual(jwtExp);
package/test/src/util.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { env } from './env.js';
2
+ import { mongoTestReportStorageFactoryGenerator, mongoTestStorageFactoryGenerator } from '@module/utils/test-utils.js';
2
3
 
3
- import { MongoTestStorageFactoryGenerator } from '@module/storage/implementation/MongoTestStorageFactoryGenerator.js';
4
- import { MongoTestReportStorageFactoryGenerator } from '@module/storage/implementation/MongoTestReportStorageFactoryGenerator.js';
5
-
6
- export const INITIALIZED_MONGO_STORAGE_FACTORY = MongoTestStorageFactoryGenerator({
4
+ export const INITIALIZED_MONGO_STORAGE_FACTORY = mongoTestStorageFactoryGenerator({
7
5
  url: env.MONGO_TEST_URL,
8
6
  isCI: env.CI
9
7
  });
10
8
 
11
- export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = MongoTestReportStorageFactoryGenerator({
9
+ export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = mongoTestReportStorageFactoryGenerator({
12
10
  url: env.MONGO_TEST_URL,
13
11
  isCI: env.CI
14
12
  });