@powersync/service-module-mongodb-storage 0.0.0-dev-20250730153301 → 0.0.0-dev-20250804065653

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @powersync/service-module-mongodb-storage
2
2
 
3
- ## 0.0.0-dev-20250730153301
3
+ ## 0.0.0-dev-20250804065653
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -29,11 +29,11 @@
29
29
  - Updated dependencies [d56eeb9]
30
30
  - Updated dependencies [a700ec9]
31
31
  - Updated dependencies [d4db4e2]
32
- - @powersync/service-core@0.0.0-dev-20250730153301
33
- - @powersync/service-types@0.0.0-dev-20250730153301
34
- - @powersync/lib-services-framework@0.0.0-dev-20250730153301
35
- - @powersync/service-sync-rules@0.0.0-dev-20250730153301
36
- - @powersync/lib-service-mongodb@0.0.0-dev-20250730153301
32
+ - @powersync/service-core@0.0.0-dev-20250804065653
33
+ - @powersync/service-types@0.0.0-dev-20250804065653
34
+ - @powersync/lib-services-framework@0.0.0-dev-20250804065653
35
+ - @powersync/service-sync-rules@0.0.0-dev-20250804065653
36
+ - @powersync/lib-service-mongodb@0.0.0-dev-20250804065653
37
37
 
38
38
  ## 0.10.4
39
39
 
@@ -0,0 +1,7 @@
1
+ import { TestStorageOptions } from '@powersync/service-core';
2
+ import { MongoReportStorage } from '../MongoReportStorage.js';
3
+ export type MongoTestStorageOptions = {
4
+ url: string;
5
+ isCI: boolean;
6
+ };
7
+ export declare const MongoTestReportStorageFactoryGenerator: (factoryOptions: MongoTestStorageOptions) => (options?: TestStorageOptions) => Promise<MongoReportStorage>;
@@ -0,0 +1,13 @@
1
+ import { connectMongoForTests } from './util.js';
2
+ import { MongoReportStorage } from '../MongoReportStorage.js';
3
+ export const MongoTestReportStorageFactoryGenerator = (factoryOptions) => {
4
+ return async (options) => {
5
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
6
+ await db.createSdkReportingCollection();
7
+ if (!options?.doNotClear) {
8
+ await db.clear();
9
+ }
10
+ return new MongoReportStorage(db);
11
+ };
12
+ };
13
+ //# sourceMappingURL=MongoTestReportStorageFactoryGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MongoTestReportStorageFactoryGenerator.js","sourceRoot":"","sources":["../../../src/storage/implementation/MongoTestReportStorageFactoryGenerator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAO9D,MAAM,CAAC,MAAM,sCAAsC,GAAG,CAAC,cAAuC,EAAE,EAAE;IAChG,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,4BAA4B,EAAE,CAAC;QAExC,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,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-20250730153301",
5
+ "version": "0.0.0-dev-20250804065653",
6
6
  "main": "dist/index.js",
7
7
  "license": "FSL-1.1-Apache-2.0",
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-20250730153301",
31
- "@powersync/lib-services-framework": "0.0.0-dev-20250730153301",
32
- "@powersync/service-core": "0.0.0-dev-20250730153301",
33
- "@powersync/service-types": "0.0.0-dev-20250730153301",
30
+ "@powersync/lib-service-mongodb": "0.0.0-dev-20250804065653",
31
+ "@powersync/lib-services-framework": "0.0.0-dev-20250804065653",
32
+ "@powersync/service-core": "0.0.0-dev-20250804065653",
33
+ "@powersync/service-types": "0.0.0-dev-20250804065653",
34
34
  "@powersync/service-jsonbig": "0.17.10",
35
- "@powersync/service-sync-rules": "0.0.0-dev-20250730153301"
35
+ "@powersync/service-sync-rules": "0.0.0-dev-20250804065653"
36
36
  },
37
37
  "devDependencies": {
38
- "@powersync/service-core-tests": "0.0.0-dev-20250730153301"
38
+ "@powersync/service-core-tests": "0.0.0-dev-20250804065653"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b",
@@ -0,0 +1,22 @@
1
+ import { TestStorageOptions } from '@powersync/service-core';
2
+ import { connectMongoForTests } from './util.js';
3
+ import { MongoReportStorage } from '../MongoReportStorage.js';
4
+
5
+ export type MongoTestStorageOptions = {
6
+ url: string;
7
+ isCI: boolean;
8
+ };
9
+
10
+ export const MongoTestReportStorageFactoryGenerator = (factoryOptions: MongoTestStorageOptions) => {
11
+ return async (options?: TestStorageOptions) => {
12
+ const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
13
+
14
+ await db.createSdkReportingCollection();
15
+
16
+ if (!options?.doNotClear) {
17
+ await db.clear();
18
+ }
19
+
20
+ return new MongoReportStorage(db);
21
+ };
22
+ };
@@ -0,0 +1,253 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import { INITIALIZED_MONGO_REPORT_STORAGE_FACTORY } from './util.js';
3
+ import { event_types } from '@powersync/service-types';
4
+
5
+ function removeVolatileFields(sdks: event_types.SdkConnectDocument[]): Partial<event_types.SdkConnectDocument>[] {
6
+ return sdks.map((sdk) => {
7
+ const { id, disconnect_at, connect_at, jwt_exp, ...rest } = sdk;
8
+ return {
9
+ ...rest
10
+ };
11
+ });
12
+ }
13
+
14
+ describe('SDK reporting storage', async () => {
15
+ const factory = await INITIALIZED_MONGO_REPORT_STORAGE_FACTORY();
16
+ const now = new Date();
17
+ const nowAdd5minutes = new Date(
18
+ now.getFullYear(),
19
+ now.getMonth(),
20
+ now.getDate(),
21
+ now.getHours(),
22
+ now.getMinutes() + 5
23
+ );
24
+ const nowLess5minutes = new Date(
25
+ now.getFullYear(),
26
+ now.getMonth(),
27
+ now.getDate(),
28
+ now.getHours(),
29
+ now.getMinutes() - 5
30
+ );
31
+ const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
32
+ const weekAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
33
+ const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
34
+ const user_one = {
35
+ user_id: 'user_one',
36
+ client_id: 'client_one',
37
+ connect_at: now,
38
+ sdk: 'powersync-dart/1.6.4',
39
+ user_agent: 'powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android',
40
+ jwt_exp: nowAdd5minutes
41
+ };
42
+ const user_two = {
43
+ user_id: 'user_two',
44
+ client_id: 'client_two',
45
+ connect_at: nowLess5minutes,
46
+ sdk: 'powersync-js/1.21.1',
47
+ user_agent: 'powersync-js/1.21.0 powersync-web Chromium/138 linux',
48
+ jwt_exp: nowAdd5minutes
49
+ };
50
+ const user_three = {
51
+ user_id: 'user_three',
52
+ client_id: 'client_three',
53
+ connect_at: yesterday,
54
+ sdk: 'powersync-js/1.21.2',
55
+ user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
56
+ disconnect_at: yesterday
57
+ };
58
+
59
+ const user_four = {
60
+ user_id: 'user_four',
61
+ client_id: 'client_four',
62
+ connect_at: now,
63
+ sdk: 'powersync-js/1.21.4',
64
+ user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
65
+ jwt_exp: nowLess5minutes
66
+ };
67
+
68
+ const user_week = {
69
+ user_id: 'user_week',
70
+ client_id: 'client_week',
71
+ connect_at: weekAgo,
72
+ sdk: 'powersync-js/1.24.5',
73
+ user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
74
+ disconnect_at: weekAgo
75
+ };
76
+
77
+ const user_month = {
78
+ user_id: 'user_month',
79
+ client_id: 'client_month',
80
+ connect_at: monthAgo,
81
+ sdk: 'powersync-js/1.23.6',
82
+ user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
83
+ disconnect_at: monthAgo
84
+ };
85
+
86
+ const user_expired = {
87
+ user_id: 'user_expired',
88
+ client_id: 'client_expired',
89
+ connect_at: monthAgo,
90
+ sdk: 'powersync-js/1.23.7',
91
+ user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
92
+ jwt_exp: monthAgo
93
+ };
94
+
95
+ async function loadData() {
96
+ await factory.db.sdk_report_events.insertMany([
97
+ user_one,
98
+ user_two,
99
+ user_three,
100
+ user_four,
101
+ user_week,
102
+ user_month,
103
+ user_expired
104
+ ]);
105
+ }
106
+
107
+ function deleteData() {
108
+ return factory.db.sdk_report_events.deleteMany();
109
+ }
110
+
111
+ beforeAll(async () => {
112
+ await loadData();
113
+ });
114
+
115
+ afterAll(async () => {
116
+ await deleteData();
117
+ });
118
+ it('Should show connected users with start range', async () => {
119
+ const current = await factory.listCurrentConnections({
120
+ range: {
121
+ start_date: new Date(
122
+ now.getFullYear(),
123
+ now.getMonth(),
124
+ now.getDate(),
125
+ now.getHours(),
126
+ now.getMinutes() - 1
127
+ ).toISOString()
128
+ }
129
+ });
130
+ expect(current).toMatchSnapshot();
131
+ });
132
+ it('Should show connected users with start range and end range', async () => {
133
+ const current = await factory.listCurrentConnections({
134
+ range: {
135
+ end_date: nowLess5minutes.toISOString(),
136
+ start_date: new Date(
137
+ now.getFullYear(),
138
+ now.getMonth(),
139
+ now.getDate(),
140
+ now.getHours(),
141
+ now.getMinutes() - 6
142
+ ).toISOString()
143
+ }
144
+ });
145
+ expect(current).toMatchSnapshot();
146
+ });
147
+ it('Should show SDK scrape data for user over the past month', async () => {
148
+ const sdk = await factory.scrapeSdkData({
149
+ interval: 1,
150
+ timeframe: 'month'
151
+ });
152
+ expect(sdk).toMatchSnapshot();
153
+ });
154
+ it('Should show SDK scrape data for user over the past week', async () => {
155
+ const sdk = await factory.scrapeSdkData({
156
+ interval: 1,
157
+ timeframe: 'week'
158
+ });
159
+ expect(sdk).toMatchSnapshot();
160
+ });
161
+ it('Should show SDK scrape data for user over the past day', async () => {
162
+ const sdk = await factory.scrapeSdkData({
163
+ interval: 1,
164
+ timeframe: 'day'
165
+ });
166
+ expect(sdk).toMatchSnapshot();
167
+ });
168
+
169
+ it('Should update a sdk event if its within a day', async () => {
170
+ const newConnectAt = new Date(
171
+ now.getFullYear(),
172
+ now.getMonth(),
173
+ now.getDate(),
174
+ now.getHours(),
175
+ now.getMinutes() + 20
176
+ );
177
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
178
+ await factory.reportSdkConnect({
179
+ sdk: user_one.sdk,
180
+ connect_at: newConnectAt,
181
+ jwt_exp: jwtExp,
182
+ client_id: user_one.client_id,
183
+ user_id: user_one.user_id,
184
+ user_agent: user_one.user_agent
185
+ });
186
+
187
+ const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
188
+ expect(sdk).toHaveLength(1);
189
+ expect(new Date(sdk[0].connect_at)).toEqual(newConnectAt);
190
+ expect(new Date(sdk[0].jwt_exp!)).toEqual(jwtExp);
191
+ expect(sdk[0].disconnect_at).toBeNull();
192
+ const cleaned = removeVolatileFields(sdk);
193
+ expect(cleaned).toMatchSnapshot();
194
+ });
195
+
196
+ it('Should update a connected sdk event and make it disconnected', async () => {
197
+ const disconnectAt = new Date(
198
+ now.getFullYear(),
199
+ now.getMonth(),
200
+ now.getDate(),
201
+ now.getHours(),
202
+ now.getMinutes() + 20
203
+ );
204
+ const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
205
+
206
+ await factory.reportSdkDisconnect({
207
+ disconnect_at: disconnectAt,
208
+ jwt_exp: jwtExp,
209
+ client_id: user_one.client_id,
210
+ user_id: user_one.user_id,
211
+ user_agent: user_one.user_agent
212
+ });
213
+
214
+ const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
215
+ expect(sdk).toHaveLength(1);
216
+ expect(new Date(sdk[0].disconnect_at!)).toEqual(disconnectAt);
217
+ const cleaned = removeVolatileFields(sdk);
218
+ expect(cleaned).toMatchSnapshot();
219
+ });
220
+
221
+ it('Should create a sdk event if its after a day', async () => {
222
+ const newConnectAt = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, now.getHours());
223
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
224
+
225
+ await factory.reportSdkConnect({
226
+ sdk: user_week.sdk,
227
+ connect_at: newConnectAt,
228
+ jwt_exp: jwtExp,
229
+ client_id: user_week.client_id,
230
+ user_id: user_week.user_id,
231
+ user_agent: user_week.user_agent
232
+ });
233
+
234
+ const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
235
+ expect(sdk).toHaveLength(2);
236
+ const cleaned = removeVolatileFields(sdk);
237
+ expect(cleaned).toMatchSnapshot();
238
+ });
239
+
240
+ it('Should delete rows older than specified range', async () => {
241
+ await deleteData();
242
+ await loadData();
243
+ await factory.deleteOldSdkData({
244
+ interval: 1,
245
+ timeframe: 'week'
246
+ });
247
+ const sdk = await factory.scrapeSdkData({
248
+ interval: 1,
249
+ timeframe: 'month'
250
+ });
251
+ expect(sdk).toMatchSnapshot();
252
+ });
253
+ });
package/test/src/util.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import { env } from './env.js';
2
2
 
3
3
  import { MongoTestStorageFactoryGenerator } from '@module/storage/implementation/MongoTestStorageFactoryGenerator.js';
4
+ import { MongoTestReportStorageFactoryGenerator } from '@module/storage/implementation/MongoTestReportStorageFactoryGenerator.js';
4
5
 
5
6
  export const INITIALIZED_MONGO_STORAGE_FACTORY = MongoTestStorageFactoryGenerator({
6
7
  url: env.MONGO_TEST_URL,
7
8
  isCI: env.CI
8
9
  });
10
+
11
+ export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = MongoTestReportStorageFactoryGenerator({
12
+ url: env.MONGO_TEST_URL,
13
+ isCI: env.CI
14
+ });