@powersync/service-module-postgres-storage 0.10.7 → 0.10.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 (43) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/migrations/scripts/1756282360128-connection-reporting.d.ts +3 -0
  4. package/dist/@types/storage/PostgresReportStorage.d.ts +24 -0
  5. package/dist/@types/storage/PostgresStorageProvider.d.ts +1 -1
  6. package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +2 -2
  7. package/dist/@types/storage/storage-index.d.ts +0 -1
  8. package/dist/@types/types/models/SdkReporting.d.ts +21 -0
  9. package/dist/@types/types/models/models-index.d.ts +1 -0
  10. package/dist/@types/{storage/PostgresTestStorageFactoryGenerator.d.ts → utils/test-utils.d.ts} +5 -3
  11. package/dist/@types/utils/utils-index.d.ts +1 -0
  12. package/dist/migrations/scripts/1756282360128-connection-reporting.js +107 -0
  13. package/dist/migrations/scripts/1756282360128-connection-reporting.js.map +1 -0
  14. package/dist/storage/PostgresReportStorage.js +238 -0
  15. package/dist/storage/PostgresReportStorage.js.map +1 -0
  16. package/dist/storage/PostgresStorageProvider.js +10 -1
  17. package/dist/storage/PostgresStorageProvider.js.map +1 -1
  18. package/dist/storage/PostgresSyncRulesStorage.js +1 -0
  19. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
  20. package/dist/storage/storage-index.js +0 -1
  21. package/dist/storage/storage-index.js.map +1 -1
  22. package/dist/types/models/SdkReporting.js +17 -0
  23. package/dist/types/models/SdkReporting.js.map +1 -0
  24. package/dist/types/models/models-index.js +1 -0
  25. package/dist/types/models/models-index.js.map +1 -1
  26. package/dist/{storage/PostgresTestStorageFactoryGenerator.js → utils/test-utils.js} +22 -6
  27. package/dist/utils/test-utils.js.map +1 -0
  28. package/dist/utils/utils-index.js +1 -0
  29. package/dist/utils/utils-index.js.map +1 -1
  30. package/package.json +9 -9
  31. package/src/migrations/scripts/1756282360128-connection-reporting.ts +41 -0
  32. package/src/storage/PostgresReportStorage.ts +258 -0
  33. package/src/storage/PostgresStorageProvider.ts +13 -2
  34. package/src/storage/PostgresSyncRulesStorage.ts +4 -1
  35. package/src/storage/storage-index.ts +0 -1
  36. package/src/types/models/SdkReporting.ts +23 -0
  37. package/src/types/models/models-index.ts +1 -0
  38. package/src/{storage/PostgresTestStorageFactoryGenerator.ts → utils/test-utils.ts} +21 -5
  39. package/src/utils/utils-index.ts +1 -0
  40. package/test/src/__snapshots__/connection-report-storage.test.ts.snap +215 -0
  41. package/test/src/connection-report-storage.test.ts +233 -0
  42. package/test/src/util.ts +3 -6
  43. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +0 -1
@@ -0,0 +1,215 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Connection report storage > Should create a connection event 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 report 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 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
+ ]
67
+ `;
68
+
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
+ ]
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
+ `;
@@ -0,0 +1,233 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import { POSTGRES_REPORT_STORAGE_FACTORY } from './util.js';
3
+ import { event_types } from '@powersync/service-types';
4
+ import { register, ReportUserData } from '@powersync/service-core-tests';
5
+ import { PostgresReportStorage } from '../../src/storage/PostgresReportStorage.js';
6
+ import { DateTimeValue } from '@powersync/service-sync-rules';
7
+
8
+ const factory = await POSTGRES_REPORT_STORAGE_FACTORY();
9
+ const userData = register.REPORT_TEST_USERS;
10
+ const dates = register.REPORT_TEST_DATES;
11
+
12
+ function removeVolatileFields(sdks: event_types.ClientConnection[]): Partial<event_types.ClientConnection>[] {
13
+ return sdks.map((sdk) => {
14
+ const { id, disconnected_at, connected_at, jwt_exp, ...rest } = sdk;
15
+ return {
16
+ ...rest
17
+ };
18
+ });
19
+ }
20
+
21
+ async function loadData(userData: ReportUserData, factory: PostgresReportStorage) {
22
+ await factory.db.sql`
23
+ INSERT INTO
24
+ connection_report_events (
25
+ user_id,
26
+ client_id,
27
+ connected_at,
28
+ sdk,
29
+ user_agent,
30
+ jwt_exp,
31
+ id,
32
+ disconnected_at
33
+ )
34
+ VALUES
35
+ (
36
+ ${{ type: 'varchar', value: userData.user_one.user_id }},
37
+ ${{ type: 'varchar', value: userData.user_one.client_id }},
38
+ ${{ type: 1184, value: userData.user_one.connected_at.toISOString() }},
39
+ ${{ type: 'varchar', value: userData.user_one.sdk }},
40
+ ${{ type: 'varchar', value: userData.user_one.user_agent }},
41
+ ${{ type: 1184, value: userData.user_one.jwt_exp.toISOString() }},
42
+ ${{ type: 'varchar', value: '1' }},
43
+ NULL
44
+ ),
45
+ (
46
+ ${{ type: 'varchar', value: userData.user_two.user_id }},
47
+ ${{ type: 'varchar', value: userData.user_two.client_id }},
48
+ ${{ type: 1184, value: userData.user_two.connected_at.toISOString() }},
49
+ ${{ type: 'varchar', value: userData.user_two.sdk }},
50
+ ${{ type: 'varchar', value: userData.user_two.user_agent }},
51
+ ${{ type: 1184, value: userData.user_two.jwt_exp.toISOString() }},
52
+ ${{ type: 'varchar', value: '2' }},
53
+ NULL
54
+ ),
55
+ (
56
+ ${{ type: 'varchar', value: userData.user_four.user_id }},
57
+ ${{ type: 'varchar', value: userData.user_four.client_id }},
58
+ ${{ type: 1184, value: userData.user_four.connected_at.toISOString() }},
59
+ ${{ type: 'varchar', value: userData.user_four.sdk }},
60
+ ${{ type: 'varchar', value: userData.user_four.user_agent }},
61
+ ${{ type: 1184, value: userData.user_four.jwt_exp.toISOString() }},
62
+ ${{ type: 'varchar', value: '4' }},
63
+ NULL
64
+ ),
65
+ (
66
+ ${{ type: 'varchar', value: userData.user_old.user_id }},
67
+ ${{ type: 'varchar', value: userData.user_old.client_id }},
68
+ ${{ type: 1184, value: userData.user_old.connected_at.toISOString() }},
69
+ ${{ type: 'varchar', value: userData.user_old.sdk }},
70
+ ${{ type: 'varchar', value: userData.user_old.user_agent }},
71
+ ${{ type: 1184, value: userData.user_old.jwt_exp.toISOString() }},
72
+ ${{ type: 'varchar', value: '5' }},
73
+ NULL
74
+ ),
75
+ (
76
+ ${{ type: 'varchar', value: userData.user_three.user_id }},
77
+ ${{ type: 'varchar', value: userData.user_three.client_id }},
78
+ ${{ type: 1184, value: userData.user_three.connected_at.toISOString() }},
79
+ ${{ type: 'varchar', value: userData.user_three.sdk }},
80
+ ${{ type: 'varchar', value: userData.user_three.user_agent }},
81
+ NULL,
82
+ ${{ type: 'varchar', value: '3' }},
83
+ ${{ type: 1184, value: userData.user_three.disconnected_at.toISOString() }}
84
+ ),
85
+ (
86
+ ${{ type: 'varchar', value: userData.user_week.user_id }},
87
+ ${{ type: 'varchar', value: userData.user_week.client_id }},
88
+ ${{ type: 1184, value: userData.user_week.connected_at.toISOString() }},
89
+ ${{ type: 'varchar', value: userData.user_week.sdk }},
90
+ ${{ type: 'varchar', value: userData.user_week.user_agent }},
91
+ NULL,
92
+ ${{ type: 'varchar', value: 'week' }},
93
+ ${{ type: 1184, value: userData.user_week.disconnected_at.toISOString() }}
94
+ ),
95
+ (
96
+ ${{ type: 'varchar', value: userData.user_month.user_id }},
97
+ ${{ type: 'varchar', value: userData.user_month.client_id }},
98
+ ${{ type: 1184, value: userData.user_month.connected_at.toISOString() }},
99
+ ${{ type: 'varchar', value: userData.user_month.sdk }},
100
+ ${{ type: 'varchar', value: userData.user_month.user_agent }},
101
+ NULL,
102
+ ${{ type: 'varchar', value: 'month' }},
103
+ ${{ type: 1184, value: userData.user_month.disconnected_at.toISOString() }}
104
+ ),
105
+ (
106
+ ${{ type: 'varchar', value: userData.user_expired.user_id }},
107
+ ${{ type: 'varchar', value: userData.user_expired.client_id }},
108
+ ${{ type: 1184, value: userData.user_expired.connected_at.toISOString() }},
109
+ ${{ type: 'varchar', value: userData.user_expired.sdk }},
110
+ ${{ type: 'varchar', value: userData.user_expired.user_agent }},
111
+ ${{ type: 1184, value: userData.user_expired.jwt_exp.toISOString() }},
112
+ ${{ type: 'varchar', value: 'expired' }},
113
+ NULL
114
+ )
115
+ `.execute();
116
+ }
117
+
118
+ async function deleteData(factory: PostgresReportStorage) {
119
+ await factory.db.sql`TRUNCATE TABLE connection_report_events`.execute();
120
+ }
121
+
122
+ beforeAll(async () => {
123
+ await loadData(userData, factory);
124
+ });
125
+ afterAll(async () => {
126
+ await deleteData(factory);
127
+ });
128
+
129
+ describe('Report storage tests', async () => {
130
+ await register.registerReportTests(factory);
131
+ });
132
+
133
+ describe('Connection report storage', async () => {
134
+ it('Should update a connection event if its within a day', async () => {
135
+ const newConnectAt = new Date(
136
+ dates.now.getFullYear(),
137
+ dates.now.getMonth(),
138
+ dates.now.getDate(),
139
+ dates.now.getHours(),
140
+ dates.now.getMinutes() + 20
141
+ );
142
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
143
+ await factory.reportClientConnection({
144
+ sdk: userData.user_one.sdk,
145
+ connected_at: newConnectAt,
146
+ jwt_exp: jwtExp,
147
+ client_id: userData.user_one.client_id,
148
+ user_id: userData.user_one.user_id,
149
+ user_agent: userData.user_one.user_agent
150
+ });
151
+
152
+ const sdk = await factory.db
153
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_one.user_id }} AND client_id = ${{ type: 'varchar', value: userData.user_one.client_id }}`.rows<event_types.ClientConnection>();
154
+ expect(sdk).toHaveLength(1);
155
+ expect(new Date((sdk[0].connected_at as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
156
+ newConnectAt.toISOString()
157
+ );
158
+ expect(new Date((sdk[0].jwt_exp! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
159
+ jwtExp.toISOString()
160
+ );
161
+ expect(sdk[0].disconnected_at).toBeNull();
162
+ const cleaned = removeVolatileFields(sdk);
163
+ expect(cleaned).toMatchSnapshot();
164
+ });
165
+
166
+ it('Should update a connection event and make it disconnected', async () => {
167
+ const disconnectAt = new Date(
168
+ dates.now.getFullYear(),
169
+ dates.now.getMonth(),
170
+ dates.now.getDate(),
171
+ dates.now.getHours(),
172
+ dates.now.getMinutes() + 20
173
+ );
174
+ const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
175
+
176
+ await factory.reportClientDisconnection({
177
+ disconnected_at: disconnectAt,
178
+ jwt_exp: jwtExp,
179
+ client_id: userData.user_three.client_id,
180
+ user_id: userData.user_three.user_id,
181
+ user_agent: userData.user_three.user_agent,
182
+ connected_at: dates.yesterday
183
+ });
184
+
185
+ const sdk = await factory.db
186
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_three.user_id }}`.rows<event_types.ClientConnection>();
187
+ expect(sdk).toHaveLength(1);
188
+ console.log(sdk[0]);
189
+ expect(new Date((sdk[0].disconnected_at! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
190
+ disconnectAt.toISOString()
191
+ );
192
+ const cleaned = removeVolatileFields(sdk);
193
+ expect(cleaned).toMatchSnapshot();
194
+ });
195
+
196
+ it('Should create a connection event if its after a day', async () => {
197
+ const newConnectAt = new Date(
198
+ dates.now.getFullYear(),
199
+ dates.now.getMonth(),
200
+ dates.now.getDate() + 1,
201
+ dates.now.getHours()
202
+ );
203
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
204
+
205
+ await factory.reportClientConnection({
206
+ sdk: userData.user_week.sdk,
207
+ connected_at: newConnectAt,
208
+ jwt_exp: jwtExp,
209
+ client_id: userData.user_week.client_id,
210
+ user_id: userData.user_week.user_id,
211
+ user_agent: userData.user_week.user_agent
212
+ });
213
+
214
+ const sdk = await factory.db
215
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_week.user_id }}`.rows<event_types.ClientConnection>();
216
+ expect(sdk).toHaveLength(2);
217
+ const cleaned = removeVolatileFields(sdk);
218
+ expect(cleaned).toMatchSnapshot();
219
+ });
220
+
221
+ it('Should delete rows older than specified range', async () => {
222
+ await deleteData(factory);
223
+ await loadData(userData, factory);
224
+ await factory.deleteOldConnectionData({
225
+ date: dates.weekAgo
226
+ });
227
+ const sdk = await factory.getClientConnectionReports({
228
+ start: dates.monthAgo,
229
+ end: dates.now
230
+ });
231
+ expect(sdk).toMatchSnapshot();
232
+ });
233
+ });
package/test/src/util.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
- import { normalizePostgresStorageConfig } from '../../src//types/types.js';
4
- import { PostgresMigrationAgent } from '../../src/migrations/PostgresMigrationAgent.js';
5
- import {
6
- postgresTestSetup,
7
- PostgresTestStorageFactoryGenerator
8
- } from '../../src/storage/PostgresTestStorageFactoryGenerator.js';
3
+ import { normalizePostgresStorageConfig, PostgresMigrationAgent } from '../../src/index.js';
9
4
  import { env } from './env.js';
5
+ import { postgresTestSetup } from '../../src/utils/test-utils.js';
10
6
 
11
7
  const __filename = fileURLToPath(import.meta.url);
12
8
  const __dirname = path.dirname(__filename);
@@ -37,3 +33,4 @@ export const POSTGRES_STORAGE_SETUP = postgresTestSetup({
37
33
  });
38
34
 
39
35
  export const POSTGRES_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.factory;
36
+ export const POSTGRES_REPORT_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.reportFactory;
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostgresTestStorageFactoryGenerator.js","sourceRoot":"","sources":["../../src/storage/PostgresTestStorageFactoryGenerator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAiE,MAAM,yBAAyB,CAAC;AACnH,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,8BAA8B,EAAgC,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAWjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,cAA0C,EAAE,EAAE;IAC9E,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,YAAqB;QAC3B,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,OAAO,EAAE,SAAkB;KAC5B,CAAC;IAEF,MAAM,uBAAuB,GAAG,8BAA8B,CAAC,WAAW,CAAC,CAAC;IAE5E,MAAM,OAAO,GAAG,KAAK,EAAE,SAAyC,EAAE,EAAE;;;YAClE,MAAY,gBAAgB,kCAA8B,IAAI,SAAS,CAAC,gBAAgB,EAAE,OAAA,CAAC;YAC3F,MAAY,cAAc,kCAAG,cAAc,CAAC,cAAc;gBACxD,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC;gBAC5C,CAAC,CAAC,IAAI,sBAAsB,CAAC,WAAW,CAAC,OAAA,CAAC;YAC5C,gBAAgB,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;YAExD,MAAM,kBAAkB,GAAG,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAA+B,CAAC;YAEpG,MAAM,gBAAgB,CAAC,OAAO,CAAC;gBAC7B,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI;gBAC9C,gBAAgB,EAAE;oBAChB,eAAe,EAAE,kBAAkB;iBACpC;aACF,CAAC,CAAC;YAEH,IAAI,SAAS,IAAI,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;gBACnD,MAAM,gBAAgB,CAAC,OAAO,CAAC;oBAC7B,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;oBAC5C,gBAAgB,EAAE;wBAChB,eAAe,EAAE,kBAAkB;qBACpC;iBACF,CAAC,CAAC;YACL,CAAC;;;;;;;;;;;KACF,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,KAAK,EAAE,OAA4B,EAAE,EAAE;YAC9C,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;oBACzB,MAAM,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACnD,CAAC;gBAED,OAAO,IAAI,4BAA4B,CAAC;oBACtC,MAAM,EAAE,uBAAuB;oBAC/B,gBAAgB,EAAE,OAAO;iBAC1B,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;gBACZ,iFAAiF;gBACjF,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;gBAC5B,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QACD,OAAO;KACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,cAA0C,EAAE,EAAE;IAChG,OAAO,iBAAiB,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC;AACnD,CAAC,CAAC"}