@powersync/service-module-mongodb-storage 0.12.8 → 0.12.9

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 (62) hide show
  1. package/CHANGELOG.md +18 -0
  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.d.ts +3 -0
  6. package/dist/migrations/db/migrations/1752661449910-connection-reporting.js +36 -0
  7. package/dist/migrations/db/migrations/1752661449910-connection-reporting.js.map +1 -0
  8. package/dist/storage/MongoBucketStorage.js +1 -1
  9. package/dist/storage/MongoBucketStorage.js.map +1 -1
  10. package/dist/storage/MongoReportStorage.d.ts +17 -0
  11. package/dist/storage/MongoReportStorage.js +152 -0
  12. package/dist/storage/MongoReportStorage.js.map +1 -0
  13. package/dist/storage/implementation/MongoBucketBatch.js +1 -1
  14. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  15. package/dist/storage/implementation/MongoStorageProvider.d.ts +1 -1
  16. package/dist/storage/implementation/MongoStorageProvider.js +7 -3
  17. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  18. package/dist/storage/implementation/MongoSyncBucketStorage.js +1 -1
  19. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  20. package/dist/storage/implementation/PersistedBatch.js +1 -1
  21. package/dist/storage/implementation/PersistedBatch.js.map +1 -1
  22. package/dist/storage/implementation/db.d.ts +6 -1
  23. package/dist/storage/implementation/db.js +15 -0
  24. package/dist/storage/implementation/db.js.map +1 -1
  25. package/dist/storage/implementation/models.d.ts +3 -0
  26. package/dist/storage/storage-index.d.ts +3 -2
  27. package/dist/storage/storage-index.js +3 -2
  28. package/dist/storage/storage-index.js.map +1 -1
  29. package/dist/utils/test-utils.d.ts +13 -0
  30. package/dist/utils/test-utils.js +40 -0
  31. package/dist/utils/test-utils.js.map +1 -0
  32. package/dist/{storage/implementation → utils}/util.d.ts +1 -6
  33. package/dist/{storage/implementation → utils}/util.js +0 -15
  34. package/dist/utils/util.js.map +1 -0
  35. package/dist/utils/utils-index.d.ts +2 -0
  36. package/dist/utils/utils-index.js +3 -0
  37. package/dist/utils/utils-index.js.map +1 -0
  38. package/package.json +8 -8
  39. package/src/index.ts +1 -0
  40. package/src/migrations/db/migrations/1752661449910-connection-reporting.ts +58 -0
  41. package/src/storage/MongoBucketStorage.ts +1 -1
  42. package/src/storage/MongoReportStorage.ts +174 -0
  43. package/src/storage/implementation/MongoBucketBatch.ts +1 -1
  44. package/src/storage/implementation/MongoStorageProvider.ts +9 -4
  45. package/src/storage/implementation/MongoSyncBucketStorage.ts +2 -1
  46. package/src/storage/implementation/PersistedBatch.ts +1 -1
  47. package/src/storage/implementation/db.ts +17 -0
  48. package/src/storage/implementation/models.ts +3 -0
  49. package/src/storage/storage-index.ts +3 -2
  50. package/src/utils/test-utils.ts +57 -0
  51. package/src/{storage/implementation → utils}/util.ts +2 -18
  52. package/src/utils/utils-index.ts +2 -0
  53. package/test/src/__snapshots__/connection-report-storage.test.ts.snap +215 -0
  54. package/test/src/connection-report-storage.test.ts +133 -0
  55. package/test/src/storage.test.ts +3 -51
  56. package/test/src/util.ts +6 -2
  57. package/tsconfig.tsbuildinfo +1 -1
  58. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.d.ts +0 -9
  59. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js +0 -20
  60. package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js.map +0 -1
  61. package/dist/storage/implementation/util.js.map +0 -1
  62. package/src/storage/implementation/MongoTestStorageFactoryGenerator.ts +0 -32
@@ -0,0 +1,174 @@
1
+ import { storage } from '@powersync/service-core';
2
+ import { event_types } from '@powersync/service-types';
3
+ import { PowerSyncMongo } from './implementation/db.js';
4
+ import { logger } from '@powersync/lib-services-framework';
5
+
6
+ export class MongoReportStorage implements storage.ReportStorage {
7
+ public readonly db: PowerSyncMongo;
8
+
9
+ constructor(db: PowerSyncMongo) {
10
+ this.db = db;
11
+ }
12
+ async deleteOldConnectionData(data: event_types.DeleteOldConnectionData): Promise<void> {
13
+ const { date } = data;
14
+ const result = await this.db.connection_report_events.deleteMany({
15
+ connected_at: { $lt: date },
16
+ $or: [
17
+ { disconnected_at: { $exists: true } },
18
+ { jwt_exp: { $lt: new Date() }, disconnected_at: { $exists: false } }
19
+ ]
20
+ });
21
+ if (result.deletedCount > 0) {
22
+ logger.info(
23
+ `TTL from ${date.toISOString()}: ${result.deletedCount} MongoDB documents have been removed from connection_report_events.`
24
+ );
25
+ }
26
+ }
27
+
28
+ async getClientConnectionReports(
29
+ data: event_types.ClientConnectionReportRequest
30
+ ): Promise<event_types.ClientConnectionReportResponse> {
31
+ const { start, end } = data;
32
+ const result = await this.db.connection_report_events
33
+ .aggregate<event_types.ClientConnectionReportResponse>([
34
+ {
35
+ $match: {
36
+ connected_at: { $lte: end, $gte: start }
37
+ }
38
+ },
39
+ this.connectionsFacetPipeline(),
40
+ this.connectionsProjectPipeline()
41
+ ])
42
+ .toArray();
43
+ return result[0];
44
+ }
45
+
46
+ async reportClientConnection(data: event_types.ClientConnectionBucketData): Promise<void> {
47
+ const updateFilter = this.updateDocFilter(data.user_id, data.client_id!);
48
+ await this.db.connection_report_events.findOneAndUpdate(
49
+ updateFilter,
50
+ {
51
+ $set: data,
52
+ $unset: {
53
+ disconnected_at: ''
54
+ }
55
+ },
56
+ {
57
+ upsert: true
58
+ }
59
+ );
60
+ }
61
+ async reportClientDisconnection(data: event_types.ClientDisconnectionEventData): Promise<void> {
62
+ const { connected_at, user_id, client_id } = data;
63
+ await this.db.connection_report_events.findOneAndUpdate(
64
+ {
65
+ client_id,
66
+ user_id,
67
+ connected_at
68
+ },
69
+ {
70
+ $set: {
71
+ disconnected_at: data.disconnected_at
72
+ },
73
+ $unset: {
74
+ jwt_exp: ''
75
+ }
76
+ }
77
+ );
78
+ }
79
+ async getConnectedClients(): Promise<event_types.ClientConnectionReportResponse> {
80
+ const result = await this.db.connection_report_events
81
+ .aggregate<event_types.ClientConnectionReportResponse>([
82
+ {
83
+ $match: {
84
+ disconnected_at: { $exists: false },
85
+ jwt_exp: { $gt: new Date() }
86
+ }
87
+ },
88
+ this.connectionsFacetPipeline(),
89
+ this.connectionsProjectPipeline()
90
+ ])
91
+ .toArray();
92
+ return result[0];
93
+ }
94
+
95
+ async [Symbol.asyncDispose]() {
96
+ // No-op
97
+ }
98
+
99
+ private parseJsDate(date: Date) {
100
+ const year = date.getUTCFullYear();
101
+ const month = date.getUTCMonth();
102
+ const today = date.getUTCDate();
103
+ const day = date.getUTCDay();
104
+ return {
105
+ year,
106
+ month,
107
+ today,
108
+ day,
109
+ parsedDate: date
110
+ };
111
+ }
112
+
113
+ private connectionsFacetPipeline() {
114
+ return {
115
+ $facet: {
116
+ unique_users: [
117
+ {
118
+ $group: {
119
+ _id: '$user_id'
120
+ }
121
+ },
122
+ {
123
+ $count: 'count'
124
+ }
125
+ ],
126
+ sdk_versions_array: [
127
+ {
128
+ $group: {
129
+ _id: '$sdk',
130
+ total: { $sum: 1 },
131
+ client_ids: { $addToSet: '$client_id' },
132
+ user_ids: { $addToSet: '$user_id' }
133
+ }
134
+ },
135
+ {
136
+ $project: {
137
+ _id: 0,
138
+ sdk: '$_id',
139
+ users: { $size: '$user_ids' },
140
+ clients: { $size: '$client_ids' }
141
+ }
142
+ },
143
+ {
144
+ $sort: {
145
+ sdk: 1
146
+ }
147
+ }
148
+ ]
149
+ }
150
+ };
151
+ }
152
+
153
+ private connectionsProjectPipeline() {
154
+ return {
155
+ $project: {
156
+ users: { $ifNull: [{ $arrayElemAt: ['$unique_users.count', 0] }, 0] },
157
+ sdks: '$sdk_versions_array'
158
+ }
159
+ };
160
+ }
161
+
162
+ private updateDocFilter(userId: string, clientId: string) {
163
+ const { year, month, today } = this.parseJsDate(new Date());
164
+ const nextDay = today + 1;
165
+ return {
166
+ user_id: userId,
167
+ client_id: clientId,
168
+ connected_at: {
169
+ $gte: new Date(Date.UTC(year, month, today)),
170
+ $lt: new Date(Date.UTC(year, month, nextDay))
171
+ }
172
+ };
173
+ }
174
+ }
@@ -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
@@ -4,8 +4,9 @@ import { POWERSYNC_VERSION, storage } from '@powersync/service-core';
4
4
  import { MongoStorageConfig } from '../../types/types.js';
5
5
  import { MongoBucketStorage } from '../MongoBucketStorage.js';
6
6
  import { PowerSyncMongo } from './db.js';
7
+ import { MongoReportStorage } from '../MongoReportStorage.js';
7
8
 
8
- export class MongoStorageProvider implements storage.BucketStorageProvider {
9
+ export class MongoStorageProvider implements storage.StorageProvider {
9
10
  get type() {
10
11
  return lib_mongo.MONGO_CONNECTION_TYPE;
11
12
  }
@@ -37,15 +38,19 @@ export class MongoStorageProvider implements storage.BucketStorageProvider {
37
38
  await client.connect();
38
39
 
39
40
  const database = new PowerSyncMongo(client, { database: resolvedConfig.storage.database });
40
- const factory = new MongoBucketStorage(database, {
41
+ const syncStorageFactory = new MongoBucketStorage(database, {
41
42
  // TODO currently need the entire resolved config due to this
42
43
  slot_name_prefix: resolvedConfig.slot_name_prefix
43
44
  });
45
+
46
+ // Storage factory for reports
47
+ const reportStorageFactory = new MongoReportStorage(database);
44
48
  return {
45
- storage: factory,
49
+ storage: syncStorageFactory,
50
+ reportStorage: reportStorageFactory,
46
51
  shutDown: async () => {
47
52
  shuttingDown = true;
48
- await factory[Symbol.asyncDispose]();
53
+ await syncStorageFactory[Symbol.asyncDispose]();
49
54
  await client.close();
50
55
  },
51
56
  tearDown: () => {
@@ -37,7 +37,8 @@ import { MongoChecksumOptions, MongoChecksums } from './MongoChecksums.js';
37
37
  import { MongoCompactor } from './MongoCompactor.js';
38
38
  import { MongoParameterCompactor } from './MongoParameterCompactor.js';
39
39
  import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
40
- import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from './util.js';
40
+ import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../utils/util.js';
41
+
41
42
 
42
43
  export interface MongoSyncBucketStorageOptions {
43
44
  checksumOptions?: MongoChecksumOptions;
@@ -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.
@@ -8,6 +8,7 @@ import {
8
8
  BucketParameterDocument,
9
9
  BucketStateDocument,
10
10
  CheckpointEventDocument,
11
+ ClientConnectionDocument,
11
12
  CurrentDataDocument,
12
13
  CustomWriteCheckpointDocument,
13
14
  IdSequenceDocument,
@@ -37,6 +38,7 @@ export class PowerSyncMongo {
37
38
  readonly locks: mongo.Collection<lib_mongo.locks.Lock>;
38
39
  readonly bucket_state: mongo.Collection<BucketStateDocument>;
39
40
  readonly checkpoint_events: mongo.Collection<CheckpointEventDocument>;
41
+ readonly connection_report_events: mongo.Collection<ClientConnectionDocument>;
40
42
 
41
43
  readonly client: mongo.MongoClient;
42
44
  readonly db: mongo.Db;
@@ -61,6 +63,7 @@ export class PowerSyncMongo {
61
63
  this.locks = this.db.collection('locks');
62
64
  this.bucket_state = this.db.collection('bucket_state');
63
65
  this.checkpoint_events = this.db.collection('checkpoint_events');
66
+ this.connection_report_events = this.db.collection('connection_report_events');
64
67
  }
65
68
 
66
69
  /**
@@ -128,6 +131,20 @@ export class PowerSyncMongo {
128
131
  });
129
132
  }
130
133
 
134
+ /**
135
+ * Only use in migrations and tests.
136
+ */
137
+ async createConnectionReportingCollection() {
138
+ const existingCollections = await this.db
139
+ .listCollections({ name: 'connection_report_events' }, { nameOnly: false })
140
+ .toArray();
141
+ const collection = existingCollections[0];
142
+ if (collection != null) {
143
+ return;
144
+ }
145
+ await this.db.createCollection('connection_report_events');
146
+ }
147
+
131
148
  /**
132
149
  * Only use in migrations and tests.
133
150
  */
@@ -1,6 +1,7 @@
1
1
  import { InternalOpId, storage } from '@powersync/service-core';
2
2
  import { SqliteJsonValue } from '@powersync/service-sync-rules';
3
3
  import * as bson from 'bson';
4
+ import { event_types } from '@powersync/service-types';
4
5
 
5
6
  /**
6
7
  * Replica id uniquely identifying a row on the source database.
@@ -238,3 +239,5 @@ export interface InstanceDocument {
238
239
  // The instance UUID
239
240
  _id: string;
240
241
  }
242
+
243
+ export interface ClientConnectionDocument extends event_types.ClientConnection {}
@@ -7,8 +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';
14
+ export * from './MongoReportStorage.js';
15
+ export * as test_utils from '../utils/test-utils.js';
@@ -0,0 +1,57 @@
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
+ import { MongoSyncBucketStorageOptions } from '../storage/implementation/MongoSyncBucketStorage.js';
7
+
8
+ export type MongoTestStorageOptions = {
9
+ url: string;
10
+ isCI: boolean;
11
+ internalOptions?: MongoSyncBucketStorageOptions;
12
+ };
13
+
14
+ export function mongoTestStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
15
+ return async (options?: TestStorageOptions) => {
16
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
17
+
18
+ // None of the tests insert data into this collection, so it was never created
19
+ if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) {
20
+ await db.db.createCollection('bucket_parameters');
21
+ }
22
+
23
+ // Full migrations are not currently run for tests, so we manually create this
24
+ await db.createCheckpointEventsCollection();
25
+
26
+ if (!options?.doNotClear) {
27
+ await db.clear();
28
+ }
29
+
30
+ return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }, factoryOptions.internalOptions);
31
+ };
32
+ }
33
+
34
+ export function mongoTestReportStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
35
+ return async (options?: TestStorageOptions) => {
36
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
37
+
38
+ await db.createConnectionReportingCollection();
39
+
40
+ if (!options?.doNotClear) {
41
+ await db.clear();
42
+ }
43
+
44
+ return new MongoReportStorage(db);
45
+ };
46
+ }
47
+
48
+ export const connectMongoForTests = (url: string, isCI: boolean) => {
49
+ // Short timeout for tests, to fail fast when the server is not available.
50
+ // Slightly longer timeouts for CI, to avoid arbitrary test failures
51
+ const client = new mongo.MongoClient(url, {
52
+ connectTimeoutMS: isCI ? 15_000 : 5_000,
53
+ socketTimeoutMS: isCI ? 15_000 : 5_000,
54
+ serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
55
+ });
56
+ return new PowerSyncMongo(client);
57
+ };
@@ -3,11 +3,9 @@ import * as crypto from 'crypto';
3
3
  import * as uuid from 'uuid';
4
4
 
5
5
  import { mongo } from '@powersync/lib-service-mongodb';
6
- import { BucketChecksum, PartialChecksum, PartialOrFullChecksum, storage, utils } from '@powersync/service-core';
7
-
8
- import { PowerSyncMongo } from './db.js';
9
- import { BucketDataDocument } from './models.js';
6
+ import { storage, utils } from '@powersync/service-core';
10
7
  import { ServiceAssertionError } from '@powersync/lib-services-framework';
8
+ import { BucketDataDocument } from '../storage/implementation/models.js';
11
9
 
12
10
  export function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T> {
13
11
  let filter = {
@@ -105,20 +103,6 @@ export function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId):
105
103
  }
106
104
  }
107
105
 
108
- /**
109
- * Helper for unit tests
110
- */
111
- export const connectMongoForTests = (url: string, isCI: boolean) => {
112
- // Short timeout for tests, to fail fast when the server is not available.
113
- // Slightly longer timeouts for CI, to avoid arbitrary test failures
114
- const client = new mongo.MongoClient(url, {
115
- connectTimeoutMS: isCI ? 15_000 : 5_000,
116
- socketTimeoutMS: isCI ? 15_000 : 5_000,
117
- serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
118
- });
119
- return new PowerSyncMongo(client);
120
- };
121
-
122
106
  export function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.Timestamp) {
123
107
  // This is a workaround for the lack of direct support for snapshot reads in the MongoDB driver.
124
108
  if (!session.snapshotEnabled) {
@@ -0,0 +1,2 @@
1
+ export * as test_utils from './test-utils.js';
2
+ export * from './util.js';
@@ -0,0 +1,215 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Connection reporting storage > Should create a connection report if its after a day 1`] = `
4
+ [
5
+ {
6
+ "client_id": "client_week",
7
+ "sdk": "powersync-js/1.24.5",
8
+ "user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
9
+ "user_id": "user_week",
10
+ },
11
+ {
12
+ "client_id": "client_week",
13
+ "sdk": "powersync-js/1.24.5",
14
+ "user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
15
+ "user_id": "user_week",
16
+ },
17
+ ]
18
+ `;
19
+
20
+ exports[`Connection reporting storage > Should delete rows older than specified range 1`] = `
21
+ {
22
+ "sdks": [
23
+ {
24
+ "clients": 1,
25
+ "sdk": "powersync-dart/1.6.4",
26
+ "users": 1,
27
+ },
28
+ {
29
+ "clients": 1,
30
+ "sdk": "powersync-js/1.21.1",
31
+ "users": 1,
32
+ },
33
+ {
34
+ "clients": 1,
35
+ "sdk": "powersync-js/1.21.2",
36
+ "users": 1,
37
+ },
38
+ {
39
+ "clients": 1,
40
+ "sdk": "powersync-js/1.21.4",
41
+ "users": 1,
42
+ },
43
+ {
44
+ "clients": 1,
45
+ "sdk": "powersync-js/1.24.5",
46
+ "users": 1,
47
+ },
48
+ {
49
+ "clients": 1,
50
+ "sdk": "unknown",
51
+ "users": 1,
52
+ },
53
+ ],
54
+ "users": 5,
55
+ }
56
+ `;
57
+
58
+ exports[`Connection reporting storage > Should update a connected connection report and make it disconnected 1`] = `
59
+ [
60
+ {
61
+ "client_id": "client_three",
62
+ "sdk": "powersync-js/1.21.2",
63
+ "user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
64
+ "user_id": "user_three",
65
+ },
66
+ ]
67
+ `;
68
+
69
+ exports[`Connection reporting storage > Should update a connection report if its within a day 1`] = `
70
+ [
71
+ {
72
+ "client_id": "client_one",
73
+ "sdk": "powersync-dart/1.6.4",
74
+ "user_agent": "powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android",
75
+ "user_id": "user_one",
76
+ },
77
+ ]
78
+ `;
79
+
80
+ exports[`Report storage tests > Should show connection report data for user over the past day 1`] = `
81
+ {
82
+ "sdks": [
83
+ {
84
+ "clients": 1,
85
+ "sdk": "powersync-dart/1.6.4",
86
+ "users": 1,
87
+ },
88
+ {
89
+ "clients": 1,
90
+ "sdk": "powersync-js/1.21.1",
91
+ "users": 1,
92
+ },
93
+ {
94
+ "clients": 1,
95
+ "sdk": "powersync-js/1.21.4",
96
+ "users": 1,
97
+ },
98
+ {
99
+ "clients": 1,
100
+ "sdk": "unknown",
101
+ "users": 1,
102
+ },
103
+ ],
104
+ "users": 3,
105
+ }
106
+ `;
107
+
108
+ exports[`Report storage tests > Should show connection report data for user over the past month 1`] = `
109
+ {
110
+ "sdks": [
111
+ {
112
+ "clients": 1,
113
+ "sdk": "powersync-dart/1.6.4",
114
+ "users": 1,
115
+ },
116
+ {
117
+ "clients": 1,
118
+ "sdk": "powersync-js/1.21.1",
119
+ "users": 1,
120
+ },
121
+ {
122
+ "clients": 1,
123
+ "sdk": "powersync-js/1.21.2",
124
+ "users": 1,
125
+ },
126
+ {
127
+ "clients": 1,
128
+ "sdk": "powersync-js/1.21.4",
129
+ "users": 1,
130
+ },
131
+ {
132
+ "clients": 1,
133
+ "sdk": "powersync-js/1.23.6",
134
+ "users": 1,
135
+ },
136
+ {
137
+ "clients": 1,
138
+ "sdk": "powersync-js/1.23.7",
139
+ "users": 1,
140
+ },
141
+ {
142
+ "clients": 1,
143
+ "sdk": "powersync-js/1.24.5",
144
+ "users": 1,
145
+ },
146
+ {
147
+ "clients": 1,
148
+ "sdk": "unknown",
149
+ "users": 1,
150
+ },
151
+ ],
152
+ "users": 7,
153
+ }
154
+ `;
155
+
156
+ exports[`Report storage tests > Should show connection report data for user over the past week 1`] = `
157
+ {
158
+ "sdks": [
159
+ {
160
+ "clients": 1,
161
+ "sdk": "powersync-dart/1.6.4",
162
+ "users": 1,
163
+ },
164
+ {
165
+ "clients": 1,
166
+ "sdk": "powersync-js/1.21.1",
167
+ "users": 1,
168
+ },
169
+ {
170
+ "clients": 1,
171
+ "sdk": "powersync-js/1.21.2",
172
+ "users": 1,
173
+ },
174
+ {
175
+ "clients": 1,
176
+ "sdk": "powersync-js/1.21.4",
177
+ "users": 1,
178
+ },
179
+ {
180
+ "clients": 1,
181
+ "sdk": "powersync-js/1.24.5",
182
+ "users": 1,
183
+ },
184
+ {
185
+ "clients": 1,
186
+ "sdk": "unknown",
187
+ "users": 1,
188
+ },
189
+ ],
190
+ "users": 5,
191
+ }
192
+ `;
193
+
194
+ exports[`Report storage tests > Should show currently connected users 1`] = `
195
+ {
196
+ "sdks": [
197
+ {
198
+ "clients": 1,
199
+ "sdk": "powersync-dart/1.6.4",
200
+ "users": 1,
201
+ },
202
+ {
203
+ "clients": 1,
204
+ "sdk": "powersync-js/1.21.1",
205
+ "users": 1,
206
+ },
207
+ {
208
+ "clients": 1,
209
+ "sdk": "unknown",
210
+ "users": 1,
211
+ },
212
+ ],
213
+ "users": 2,
214
+ }
215
+ `;