@powersync/service-module-postgres-storage 0.0.0-dev-20250825132649 → 0.0.0-dev-20250827091123

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.
@@ -0,0 +1,3 @@
1
+ import { migrations } from '@powersync/service-core';
2
+ export declare const up: migrations.PowerSyncMigrationFunction;
3
+ export declare const down: migrations.PowerSyncMigrationFunction;
@@ -33,13 +33,13 @@ export declare const SyncRules: t.ObjectCodec<{
33
33
  *
34
34
  * This may be old if no data is incoming.
35
35
  */
36
- last_checkpoint_ts: t.Union<t.Codec<null, null, string, t.CodecProps>, t.Codec<Date, string, string, t.CodecProps>>;
36
+ last_checkpoint_ts: t.Union<t.Codec<null, null, string, t.CodecProps>, t.Codec<Date, string | import("@powersync/service-sync-rules").DateTimeValue, string, t.CodecProps>>;
37
37
  /**
38
38
  * Last time we persisted a checkpoint or keepalive.
39
39
  *
40
40
  * This should stay fairly current while replicating.
41
41
  */
42
- last_keepalive_ts: t.Union<t.Codec<null, null, string, t.CodecProps>, t.Codec<Date, string, string, t.CodecProps>>;
42
+ last_keepalive_ts: t.Union<t.Codec<null, null, string, t.CodecProps>, t.Codec<Date, string | import("@powersync/service-sync-rules").DateTimeValue, string, t.CodecProps>>;
43
43
  /**
44
44
  * If an error is stopping replication, it will be stored here.
45
45
  */
@@ -0,0 +1,107 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { openMigrationDB } from '../migration-utils.js';
54
+ export const up = async (context) => {
55
+ const env_1 = { stack: [], error: void 0, hasError: false };
56
+ try {
57
+ const { service_context: { configuration } } = context;
58
+ const client = __addDisposableResource(env_1, openMigrationDB(configuration.storage), true);
59
+ await client.transaction(async (db) => {
60
+ await db.sql `
61
+ CREATE TABLE IF NOT EXISTS connection_report_events (
62
+ id TEXT PRIMARY KEY,
63
+ user_agent TEXT NOT NULL,
64
+ client_id TEXT NOT NULL,
65
+ user_id TEXT NOT NULL,
66
+ sdk TEXT NOT NULL,
67
+ jwt_exp TIMESTAMP WITH TIME ZONE,
68
+ connected_at TIMESTAMP WITH TIME ZONE NOT NULL,
69
+ disconnected_at TIMESTAMP WITH TIME ZONE
70
+ )
71
+ `.execute();
72
+ await db.sql `
73
+ CREATE INDEX IF NOT EXISTS sdk_list_index ON connection_report_events (connected_at, jwt_exp, disconnected_at)
74
+ `.execute();
75
+ await db.sql `CREATE INDEX IF NOT EXISTS sdk_user_id_index ON connection_report_events (user_id)`.execute();
76
+ await db.sql `CREATE INDEX IF NOT EXISTS sdk_client_id_index ON connection_report_events (client_id)`.execute();
77
+ await db.sql `CREATE INDEX IF NOT EXISTS sdk_index ON connection_report_events (sdk)`.execute();
78
+ });
79
+ }
80
+ catch (e_1) {
81
+ env_1.error = e_1;
82
+ env_1.hasError = true;
83
+ }
84
+ finally {
85
+ const result_1 = __disposeResources(env_1);
86
+ if (result_1)
87
+ await result_1;
88
+ }
89
+ };
90
+ export const down = async (context) => {
91
+ const env_2 = { stack: [], error: void 0, hasError: false };
92
+ try {
93
+ const { service_context: { configuration } } = context;
94
+ const client = __addDisposableResource(env_2, openMigrationDB(configuration.storage), true);
95
+ await client.sql `DROP TABLE IF EXISTS connection_report_events`.execute();
96
+ }
97
+ catch (e_2) {
98
+ env_2.error = e_2;
99
+ env_2.hasError = true;
100
+ }
101
+ finally {
102
+ const result_2 = __disposeResources(env_2);
103
+ if (result_2)
104
+ await result_2;
105
+ }
106
+ };
107
+ //# sourceMappingURL=1756282360128-connection-reporting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"1756282360128-connection-reporting.js","sourceRoot":"","sources":["../../../src/migrations/scripts/1756282360128-connection-reporting.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,MAAM,EAAE,GAA0C,KAAK,EAAE,OAAO,EAAE,EAAE;;;QACzE,MAAM,EACJ,eAAe,EAAE,EAAE,aAAa,EAAE,EACnC,GAAG,OAAO,CAAC;QACZ,MAAY,MAAM,kCAAG,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,OAAA,CAAC;QAC5D,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACpC,MAAM,EAAE,CAAC,GAAG,CAAA;;;;;;;;;;;KAWX,CAAC,OAAO,EAAE,CAAC;YAEZ,MAAM,EAAE,CAAC,GAAG,CAAA;;KAEX,CAAC,OAAO,EAAE,CAAC;YAEZ,MAAM,EAAE,CAAC,GAAG,CAAA,oFAAoF,CAAC,OAAO,EAAE,CAAC;YAE3G,MAAM,EAAE,CAAC,GAAG,CAAA,wFAAwF,CAAC,OAAO,EAAE,CAAC;YAE/G,MAAM,EAAE,CAAC,GAAG,CAAA,wEAAwE,CAAC,OAAO,EAAE,CAAC;QACjG,CAAC,CAAC,CAAC;;;;;;;;;;;CACJ,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAA0C,KAAK,EAAE,OAAO,EAAE,EAAE;;;QAC3E,MAAM,EACJ,eAAe,EAAE,EAAE,aAAa,EAAE,EACnC,GAAG,OAAO,CAAC;QACZ,MAAY,MAAM,kCAAG,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,OAAA,CAAC;QAC5D,MAAM,MAAM,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;;;;;;;;;;;CAC3E,CAAC"}
@@ -54,7 +54,7 @@ export class PostgresReportStorageFactory {
54
54
  connection_report_events
55
55
  WHERE
56
56
  disconnected_at IS NULL
57
- AND jwt_exp > NOW() AT TIME ZONE 'UTC'
57
+ AND jwt_exp > NOW()
58
58
  ),
59
59
  unique_users AS (
60
60
  SELECT
package/dist/utils/db.js CHANGED
@@ -19,7 +19,6 @@ export const dropTables = async (client) => {
19
19
  await db.sql `DROP TABLE IF EXISTS custom_write_checkpoints`.execute();
20
20
  await db.sql `DROP SEQUENCE IF EXISTS op_id_sequence`.execute();
21
21
  await db.sql `DROP SEQUENCE IF EXISTS sync_rules_id_sequence`.execute();
22
- await db.sql `DROP TABLE IF EXISTS connection_report_events`.execute();
23
22
  });
24
23
  };
25
24
  //# sourceMappingURL=db.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAEhE,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAEpC,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAmC,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACvC,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,EAAE,CAAC,GAAG,CAAA,+BAA+B,CAAC,OAAO,EAAE,CAAC;QACtD,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,mCAAmC,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,EAAE,CAAC,GAAG,CAAA,oCAAoC,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;QACtE,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,gDAAgD,CAAC,OAAO,EAAE,CAAC;QACvE,MAAM,EAAE,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAEhE,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAEpC,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAmC,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACvC,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,EAAE,CAAC,GAAG,CAAA,+BAA+B,CAAC,OAAO,EAAE,CAAC;QACtD,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,mCAAmC,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,EAAE,CAAC,GAAG,CAAA,oCAAoC,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;QACtE,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,gDAAgD,CAAC,OAAO,EAAE,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@powersync/service-module-postgres-storage",
3
3
  "repository": "https://github.com/powersync-ja/powersync-service",
4
4
  "types": "dist/@types/index.d.ts",
5
- "version": "0.0.0-dev-20250825132649",
5
+ "version": "0.0.0-dev-20250827091123",
6
6
  "main": "dist/index.js",
7
7
  "type": "module",
8
8
  "publishConfig": {
@@ -28,14 +28,14 @@
28
28
  "p-defer": "^4.0.1",
29
29
  "ts-codec": "^1.3.0",
30
30
  "uuid": "^11.1.0",
31
- "@powersync/lib-service-postgres": "0.0.0-dev-20250825132649",
32
- "@powersync/lib-services-framework": "0.0.0-dev-20250825132649",
33
- "@powersync/service-core": "0.0.0-dev-20250825132649",
34
- "@powersync/service-types": "0.0.0-dev-20250825132649",
35
- "@powersync/service-core-tests": "0.0.0-dev-20250825132649",
36
- "@powersync/service-jpgwire": "0.0.0-dev-20250825132649",
37
- "@powersync/service-jsonbig": "0.0.0-dev-20250825132649",
38
- "@powersync/service-sync-rules": "0.0.0-dev-20250825132649"
31
+ "@powersync/lib-service-postgres": "0.0.0-dev-20250827091123",
32
+ "@powersync/lib-services-framework": "0.0.0-dev-20250827091123",
33
+ "@powersync/service-core": "0.0.0-dev-20250827091123",
34
+ "@powersync/service-types": "0.0.0-dev-20250827091123",
35
+ "@powersync/service-core-tests": "0.0.0-dev-20250827091123",
36
+ "@powersync/service-jpgwire": "0.0.0-dev-20250827091123",
37
+ "@powersync/service-jsonbig": "0.0.0-dev-20250827091123",
38
+ "@powersync/service-sync-rules": "0.0.0-dev-20250827091123"
39
39
  },
40
40
  "devDependencies": {
41
41
  "typescript": "^5.7.3"
@@ -0,0 +1,41 @@
1
+ import { migrations } from '@powersync/service-core';
2
+ import { openMigrationDB } from '../migration-utils.js';
3
+
4
+ export const up: migrations.PowerSyncMigrationFunction = async (context) => {
5
+ const {
6
+ service_context: { configuration }
7
+ } = context;
8
+ await using client = openMigrationDB(configuration.storage);
9
+ await client.transaction(async (db) => {
10
+ await db.sql`
11
+ CREATE TABLE IF NOT EXISTS connection_report_events (
12
+ id TEXT PRIMARY KEY,
13
+ user_agent TEXT NOT NULL,
14
+ client_id TEXT NOT NULL,
15
+ user_id TEXT NOT NULL,
16
+ sdk TEXT NOT NULL,
17
+ jwt_exp TIMESTAMP WITH TIME ZONE,
18
+ connected_at TIMESTAMP WITH TIME ZONE NOT NULL,
19
+ disconnected_at TIMESTAMP WITH TIME ZONE
20
+ )
21
+ `.execute();
22
+
23
+ await db.sql`
24
+ CREATE INDEX IF NOT EXISTS sdk_list_index ON connection_report_events (connected_at, jwt_exp, disconnected_at)
25
+ `.execute();
26
+
27
+ await db.sql`CREATE INDEX IF NOT EXISTS sdk_user_id_index ON connection_report_events (user_id)`.execute();
28
+
29
+ await db.sql`CREATE INDEX IF NOT EXISTS sdk_client_id_index ON connection_report_events (client_id)`.execute();
30
+
31
+ await db.sql`CREATE INDEX IF NOT EXISTS sdk_index ON connection_report_events (sdk)`.execute();
32
+ });
33
+ };
34
+
35
+ export const down: migrations.PowerSyncMigrationFunction = async (context) => {
36
+ const {
37
+ service_context: { configuration }
38
+ } = context;
39
+ await using client = openMigrationDB(configuration.storage);
40
+ await client.sql`DROP TABLE IF EXISTS connection_report_events`.execute();
41
+ };
@@ -66,7 +66,7 @@ export class PostgresReportStorageFactory implements storage.ReportStorage {
66
66
  connection_report_events
67
67
  WHERE
68
68
  disconnected_at IS NULL
69
- AND jwt_exp > NOW() AT TIME ZONE 'UTC'
69
+ AND jwt_exp > NOW()
70
70
  ),
71
71
  unique_users AS (
72
72
  SELECT
package/src/utils/db.ts CHANGED
@@ -23,6 +23,5 @@ export const dropTables = async (client: lib_postgres.DatabaseClient) => {
23
23
  await db.sql`DROP TABLE IF EXISTS custom_write_checkpoints`.execute();
24
24
  await db.sql`DROP SEQUENCE IF EXISTS op_id_sequence`.execute();
25
25
  await db.sql`DROP SEQUENCE IF EXISTS sync_rules_id_sequence`.execute();
26
- await db.sql`DROP TABLE IF EXISTS connection_report_events`.execute();
27
26
  });
28
27
  };
@@ -55,43 +55,29 @@ exports[`Connection report storage > Should delete rows older than specified ran
55
55
  }
56
56
  `;
57
57
 
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
- }
58
+ exports[`Connection report storage > Should update a connection event 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
+ ]
79
67
  `;
80
68
 
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
- }
69
+ exports[`Connection report storage > Should update a connection event 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
+ ]
92
78
  `;
93
79
 
94
- exports[`Connection report storage > Should show connection report data for user over the past day 1`] = `
80
+ exports[`Report storage tests > Should show connection report data for user over the past day 1`] = `
95
81
  {
96
82
  "sdks": [
97
83
  {
@@ -119,7 +105,7 @@ exports[`Connection report storage > Should show connection report data for user
119
105
  }
120
106
  `;
121
107
 
122
- exports[`Connection report storage > Should show connection report data for user over the past month 1`] = `
108
+ exports[`Report storage tests > Should show connection report data for user over the past month 1`] = `
123
109
  {
124
110
  "sdks": [
125
111
  {
@@ -167,7 +153,7 @@ exports[`Connection report storage > Should show connection report data for user
167
153
  }
168
154
  `;
169
155
 
170
- exports[`Connection report storage > Should show connection report data for user over the past week 1`] = `
156
+ exports[`Report storage tests > Should show connection report data for user over the past week 1`] = `
171
157
  {
172
158
  "sdks": [
173
159
  {
@@ -205,46 +191,25 @@ exports[`Connection report storage > Should show connection report data for user
205
191
  }
206
192
  `;
207
193
 
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
- ]
217
- `;
218
-
219
- exports[`Connection report storage > Should update a connection event and make it disconnected 1`] = `
220
- [
221
- {
222
- "client_id": "client_three",
223
- "sdk": "powersync-js/1.21.2",
224
- "user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
225
- "user_id": "user_three",
226
- },
227
- ]
228
- `;
229
-
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`] = `
242
- [
243
- {
244
- "client_id": "client_one",
245
- "sdk": "powersync-dart/1.6.4",
246
- "user_agent": "powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android",
247
- "user_id": "user_one",
248
- },
249
- ]
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
+ }
250
215
  `;