@powersync/service-module-postgres-storage 0.0.0-dev-20250819134004 → 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 (30) hide show
  1. package/CHANGELOG.md +11 -9
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/storage/PostgresReportStorageFactory.d.ts +2 -2
  4. package/dist/@types/storage/storage-index.d.ts +0 -1
  5. package/dist/@types/{storage/PostgresTestStorageFactoryGenerator.d.ts → utils/test-utils.d.ts} +4 -4
  6. package/dist/@types/utils/utils-index.d.ts +1 -0
  7. package/dist/migrations/scripts/1684951997326-init.js +7 -5
  8. package/dist/migrations/scripts/1684951997326-init.js.map +1 -1
  9. package/dist/storage/PostgresReportStorageFactory.js +11 -63
  10. package/dist/storage/PostgresReportStorageFactory.js.map +1 -1
  11. package/dist/storage/storage-index.js +0 -1
  12. package/dist/storage/storage-index.js.map +1 -1
  13. package/dist/utils/db.js +1 -1
  14. package/dist/utils/db.js.map +1 -1
  15. package/dist/{storage/PostgresTestStorageFactoryGenerator.js → utils/test-utils.js} +7 -7
  16. package/dist/utils/test-utils.js.map +1 -0
  17. package/dist/utils/utils-index.js +1 -0
  18. package/dist/utils/utils-index.js.map +1 -1
  19. package/package.json +9 -9
  20. package/src/migrations/scripts/1684951997326-init.ts +7 -5
  21. package/src/storage/PostgresReportStorageFactory.ts +16 -66
  22. package/src/storage/storage-index.ts +0 -1
  23. package/src/utils/db.ts +1 -1
  24. package/src/{storage/PostgresTestStorageFactoryGenerator.ts → utils/test-utils.ts} +6 -6
  25. package/src/utils/utils-index.ts +1 -0
  26. package/test/src/__snapshots__/{sdk-report-storage.test.ts.snap → connection-report-storage.test.ts.snap} +80 -28
  27. package/test/src/__snapshots__/storage_sync.test.ts.snap +12 -11
  28. package/test/src/{sdk-report-storage.test.ts → connection-report-storage.test.ts} +33 -38
  29. package/test/src/util.ts +2 -1
  30. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`SDK reporting storage > Should create a sdk event if its after a day 1`] = `
3
+ exports[`Connection report storage > Should create a connection event if its after a day 1`] = `
4
4
  [
5
5
  {
6
6
  "client_id": "client_week",
@@ -17,7 +17,7 @@ exports[`SDK reporting storage > Should create a sdk event if its after a day 1`
17
17
  ]
18
18
  `;
19
19
 
20
- exports[`SDK reporting storage > Should delete rows older than specified range 1`] = `
20
+ exports[`Connection report storage > Should delete rows older than specified range 1`] = `
21
21
  {
22
22
  "sdks": [
23
23
  {
@@ -45,12 +45,53 @@ 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
  }
51
56
  `;
52
57
 
53
- exports[`SDK reporting storage > Should show SDK scrape data for user over the past day 1`] = `
58
+ exports[`Connection report storage > Should show connected users with start range 1`] = `
59
+ {
60
+ "sdks": [
61
+ {
62
+ "clients": 1,
63
+ "sdk": "powersync-dart/1.6.4",
64
+ "users": 1,
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
+ },
76
+ ],
77
+ "users": 2,
78
+ }
79
+ `;
80
+
81
+ exports[`Connection report storage > Should show connected users with start range and end range 1`] = `
82
+ {
83
+ "sdks": [
84
+ {
85
+ "clients": 1,
86
+ "sdk": "powersync-js/1.21.1",
87
+ "users": 1,
88
+ },
89
+ ],
90
+ "users": 1,
91
+ }
92
+ `;
93
+
94
+ exports[`Connection report storage > Should show connection report data for user over the past day 1`] = `
54
95
  {
55
96
  "sdks": [
56
97
  {
@@ -68,12 +109,17 @@ exports[`SDK reporting storage > Should show SDK scrape data for user over the p
68
109
  "sdk": "powersync-js/1.21.4",
69
110
  "users": 1,
70
111
  },
112
+ {
113
+ "clients": 1,
114
+ "sdk": "unknown",
115
+ "users": 1,
116
+ },
71
117
  ],
72
118
  "users": 3,
73
119
  }
74
120
  `;
75
121
 
76
- exports[`SDK reporting storage > Should show SDK scrape data for user over the past month 1`] = `
122
+ exports[`Connection report storage > Should show connection report data for user over the past month 1`] = `
77
123
  {
78
124
  "sdks": [
79
125
  {
@@ -111,12 +157,17 @@ exports[`SDK reporting storage > Should show SDK scrape data for user over the p
111
157
  "sdk": "powersync-js/1.24.5",
112
158
  "users": 1,
113
159
  },
160
+ {
161
+ "clients": 1,
162
+ "sdk": "unknown",
163
+ "users": 1,
164
+ },
114
165
  ],
115
166
  "users": 7,
116
167
  }
117
168
  `;
118
169
 
119
- exports[`SDK reporting storage > Should show SDK scrape data for user over the past week 1`] = `
170
+ exports[`Connection report storage > Should show connection report data for user over the past week 1`] = `
120
171
  {
121
172
  "sdks": [
122
173
  {
@@ -144,38 +195,28 @@ exports[`SDK reporting storage > Should show SDK scrape data for user over the p
144
195
  "sdk": "powersync-js/1.24.5",
145
196
  "users": 1,
146
197
  },
147
- ],
148
- "users": 5,
149
- }
150
- `;
151
-
152
- exports[`SDK reporting storage > Should show connected users with start range 1`] = `
153
- {
154
- "sdks": [
155
198
  {
156
199
  "clients": 1,
157
- "sdk": "powersync-dart/1.6.4",
200
+ "sdk": "unknown",
158
201
  "users": 1,
159
202
  },
160
203
  ],
161
- "users": 1,
204
+ "users": 5,
162
205
  }
163
206
  `;
164
207
 
165
- exports[`SDK reporting storage > Should show connected users with start range and end range 1`] = `
166
- {
167
- "sdks": [
168
- {
169
- "clients": 1,
170
- "sdk": "powersync-js/1.21.1",
171
- "users": 1,
172
- },
173
- ],
174
- "users": 1,
175
- }
208
+ exports[`Connection report storage > Should update a connected sdk event and make it disconnected 1`] = `
209
+ [
210
+ {
211
+ "client_id": "client_three",
212
+ "sdk": "powersync-js/1.21.2",
213
+ "user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
214
+ "user_id": "user_three",
215
+ },
216
+ ]
176
217
  `;
177
218
 
178
- exports[`SDK reporting storage > Should update a connected sdk event and make it disconnected 1`] = `
219
+ exports[`Connection report storage > Should update a connection event and make it disconnected 1`] = `
179
220
  [
180
221
  {
181
222
  "client_id": "client_three",
@@ -186,7 +227,18 @@ exports[`SDK reporting storage > Should update a connected sdk event and make it
186
227
  ]
187
228
  `;
188
229
 
189
- exports[`SDK reporting storage > Should update a sdk event if its within a day 1`] = `
230
+ exports[`Connection report storage > Should update a connection event if its within a day 1`] = `
231
+ [
232
+ {
233
+ "client_id": "client_one",
234
+ "sdk": "powersync-dart/1.6.4",
235
+ "user_agent": "powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android",
236
+ "user_id": "user_one",
237
+ },
238
+ ]
239
+ `;
240
+
241
+ exports[`Connection report storage > Should update a sdk event if its within a day 1`] = `
190
242
  [
191
243
  {
192
244
  "client_id": "client_one",
@@ -39,7 +39,7 @@ exports[`sync - postgres > 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 - postgres > 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 - postgres > compacting data - invalidate checkpoint 2`] = `
83
83
  "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
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 - postgres > 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 - postgres > 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 - postgres > 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 - postgres > 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 - postgres > sync global data 1`] = `
363
364
  "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
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 - postgres > 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 - postgres > 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 - postgres > 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 - postgres > 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",
@@ -11,7 +11,7 @@ function removeVolatileFields(sdks: event_types.ClientConnection[]): Partial<eve
11
11
  });
12
12
  }
13
13
 
14
- describe('SDK reporting storage', async () => {
14
+ describe('Connection report storage', async () => {
15
15
  const factory = await POSTGRES_REPORT_STORAGE_FACTORY();
16
16
  const now = new Date();
17
17
  const nowAdd5minutes = new Date(
@@ -70,6 +70,16 @@ describe('SDK reporting storage', async () => {
70
70
  id: '4'
71
71
  };
72
72
 
73
+ const user_old = {
74
+ user_id: 'user_one',
75
+ client_id: '',
76
+ connected_at: now.toISOString(),
77
+ sdk: 'unknown',
78
+ user_agent: 'powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android',
79
+ jwt_exp: nowAdd5minutes.toISOString(),
80
+ id: '5'
81
+ };
82
+
73
83
  const user_week = {
74
84
  user_id: 'user_week',
75
85
  client_id: 'client_week',
@@ -103,7 +113,7 @@ describe('SDK reporting storage', async () => {
103
113
  async function loadData() {
104
114
  await factory.db.sql`
105
115
  INSERT INTO
106
- sdk_report_events (
116
+ connection_report_events (
107
117
  user_id,
108
118
  client_id,
109
119
  connected_at,
@@ -144,6 +154,16 @@ describe('SDK reporting storage', async () => {
144
154
  ${{ type: 'varchar', value: user_four.id }},
145
155
  NULL
146
156
  ),
157
+ (
158
+ ${{ type: 'varchar', value: user_old.user_id }},
159
+ ${{ type: 'varchar', value: user_old.client_id }},
160
+ ${{ type: 1184, value: user_old.connected_at }},
161
+ ${{ type: 'varchar', value: user_old.sdk }},
162
+ ${{ type: 'varchar', value: user_old.user_agent }},
163
+ ${{ type: 1184, value: user_old.jwt_exp }},
164
+ ${{ type: 'varchar', value: user_old.id }},
165
+ NULL
166
+ ),
147
167
  (
148
168
  ${{ type: 'varchar', value: user_three.user_id }},
149
169
  ${{ type: 'varchar', value: user_three.client_id }},
@@ -188,7 +208,7 @@ describe('SDK reporting storage', async () => {
188
208
  }
189
209
 
190
210
  function deleteData() {
191
- return factory.db.sql`TRUNCATE TABLE sdk_report_events`.execute();
211
+ return factory.db.sql`TRUNCATE TABLE connection_report_events`.execute();
192
212
  }
193
213
 
194
214
  beforeAll(async () => {
@@ -199,49 +219,24 @@ describe('SDK reporting storage', async () => {
199
219
  await deleteData();
200
220
  });
201
221
  it('Should show connected users with start range', async () => {
202
- const current = await factory.getConnectedClients({
203
- range: {
204
- start: new Date(
205
- now.getFullYear(),
206
- now.getMonth(),
207
- now.getDate(),
208
- now.getHours(),
209
- now.getMinutes() - 1
210
- ).toISOString()
211
- }
212
- });
213
- expect(current).toMatchSnapshot();
214
- });
215
- it('Should show connected users with start range and end range', async () => {
216
- const current = await factory.getConnectedClients({
217
- range: {
218
- end: nowLess5minutes.toISOString(),
219
- start: new Date(
220
- now.getFullYear(),
221
- now.getMonth(),
222
- now.getDate(),
223
- now.getHours(),
224
- now.getMinutes() - 6
225
- ).toISOString()
226
- }
227
- });
222
+ const current = await factory.getConnectedClients();
228
223
  expect(current).toMatchSnapshot();
229
224
  });
230
- it('Should show SDK scrape data for user over the past month', async () => {
225
+ it('Should show connection report data for user over the past month', async () => {
231
226
  const sdk = await factory.getClientConnectionReports({
232
227
  start: monthAgo,
233
228
  end: now
234
229
  });
235
230
  expect(sdk).toMatchSnapshot();
236
231
  });
237
- it('Should show SDK scrape data for user over the past week', async () => {
232
+ it('Should show connection report data for user over the past week', async () => {
238
233
  const sdk = await factory.getClientConnectionReports({
239
234
  start: weekAgo,
240
235
  end: now
241
236
  });
242
237
  expect(sdk).toMatchSnapshot();
243
238
  });
244
- it('Should show SDK scrape data for user over the past day', async () => {
239
+ it('Should show connection report data for user over the past day', async () => {
245
240
  const sdk = await factory.getClientConnectionReports({
246
241
  start: dayAgo,
247
242
  end: now
@@ -249,7 +244,7 @@ describe('SDK reporting storage', async () => {
249
244
  expect(sdk).toMatchSnapshot();
250
245
  });
251
246
 
252
- it('Should update a sdk event if its within a day', async () => {
247
+ it('Should update a connection event if its within a day', async () => {
253
248
  const newConnectAt = new Date(
254
249
  now.getFullYear(),
255
250
  now.getMonth(),
@@ -268,7 +263,7 @@ describe('SDK reporting storage', async () => {
268
263
  });
269
264
 
270
265
  const sdk = await factory.db
271
- .sql`SELECT * FROM sdk_report_events WHERE user_id = ${{ type: 'varchar', value: user_one.user_id }}`.rows<event_types.ClientConnection>();
266
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_one.user_id }} AND client_id = ${{ type: 'varchar', value: user_one.client_id }}`.rows<event_types.ClientConnection>();
272
267
  expect(sdk).toHaveLength(1);
273
268
  expect(new Date(sdk[0].connected_at).toISOString()).toEqual(newConnectAt.toISOString());
274
269
  expect(new Date(sdk[0].jwt_exp!).toISOString()).toEqual(jwtExp.toISOString());
@@ -277,7 +272,7 @@ describe('SDK reporting storage', async () => {
277
272
  expect(cleaned).toMatchSnapshot();
278
273
  });
279
274
 
280
- it('Should update a connected sdk event and make it disconnected', async () => {
275
+ it('Should update a connection event and make it disconnected', async () => {
281
276
  const disconnectAt = new Date(
282
277
  now.getFullYear(),
283
278
  now.getMonth(),
@@ -297,14 +292,14 @@ describe('SDK reporting storage', async () => {
297
292
  });
298
293
 
299
294
  const sdk = await factory.db
300
- .sql`SELECT * FROM sdk_report_events WHERE user_id = ${{ type: 'varchar', value: user_three.user_id }}`.rows<event_types.ClientConnection>();
295
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_three.user_id }}`.rows<event_types.ClientConnection>();
301
296
  expect(sdk).toHaveLength(1);
302
297
  expect(new Date(sdk[0].disconnected_at!).toISOString()).toEqual(disconnectAt.toISOString());
303
298
  const cleaned = removeVolatileFields(sdk);
304
299
  expect(cleaned).toMatchSnapshot();
305
300
  });
306
301
 
307
- it('Should create a sdk event if its after a day', async () => {
302
+ it('Should create a connection event if its after a day', async () => {
308
303
  const newConnectAt = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, now.getHours());
309
304
  const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
310
305
 
@@ -318,7 +313,7 @@ describe('SDK reporting storage', async () => {
318
313
  });
319
314
 
320
315
  const sdk = await factory.db
321
- .sql`SELECT * FROM sdk_report_events WHERE user_id = ${{ type: 'varchar', value: user_week.user_id }}`.rows<event_types.ClientConnection>();
316
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_week.user_id }}`.rows<event_types.ClientConnection>();
322
317
  expect(sdk).toHaveLength(2);
323
318
  const cleaned = removeVolatileFields(sdk);
324
319
  expect(cleaned).toMatchSnapshot();
package/test/src/util.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
- import { normalizePostgresStorageConfig, PostgresMigrationAgent, postgresTestSetup } from '../../src/index.js';
3
+ import { normalizePostgresStorageConfig, PostgresMigrationAgent } from '../../src/index.js';
4
4
  import { env } from './env.js';
5
+ import { postgresTestSetup } from '../../src/utils/test-utils.js';
5
6
 
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = path.dirname(__filename);
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostgresTestStorageFactoryGenerator.js","sourceRoot":"","sources":["../../src/storage/PostgresTestStorageFactoryGenerator.ts"],"names":[],"mappings":"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"}