@powersync/service-module-postgres 0.0.0-dev-20240918092408

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 (87) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +67 -0
  3. package/README.md +3 -0
  4. package/dist/api/PostgresRouteAPIAdapter.d.ts +22 -0
  5. package/dist/api/PostgresRouteAPIAdapter.js +273 -0
  6. package/dist/api/PostgresRouteAPIAdapter.js.map +1 -0
  7. package/dist/auth/SupabaseKeyCollector.d.ts +22 -0
  8. package/dist/auth/SupabaseKeyCollector.js +64 -0
  9. package/dist/auth/SupabaseKeyCollector.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +4 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/module/PostgresModule.d.ts +14 -0
  14. package/dist/module/PostgresModule.js +108 -0
  15. package/dist/module/PostgresModule.js.map +1 -0
  16. package/dist/replication/ConnectionManagerFactory.d.ts +10 -0
  17. package/dist/replication/ConnectionManagerFactory.js +21 -0
  18. package/dist/replication/ConnectionManagerFactory.js.map +1 -0
  19. package/dist/replication/PgManager.d.ts +25 -0
  20. package/dist/replication/PgManager.js +60 -0
  21. package/dist/replication/PgManager.js.map +1 -0
  22. package/dist/replication/PgRelation.d.ts +6 -0
  23. package/dist/replication/PgRelation.js +27 -0
  24. package/dist/replication/PgRelation.js.map +1 -0
  25. package/dist/replication/PostgresErrorRateLimiter.d.ts +11 -0
  26. package/dist/replication/PostgresErrorRateLimiter.js +43 -0
  27. package/dist/replication/PostgresErrorRateLimiter.js.map +1 -0
  28. package/dist/replication/WalStream.d.ts +53 -0
  29. package/dist/replication/WalStream.js +536 -0
  30. package/dist/replication/WalStream.js.map +1 -0
  31. package/dist/replication/WalStreamReplicationJob.d.ts +27 -0
  32. package/dist/replication/WalStreamReplicationJob.js +131 -0
  33. package/dist/replication/WalStreamReplicationJob.js.map +1 -0
  34. package/dist/replication/WalStreamReplicator.d.ts +13 -0
  35. package/dist/replication/WalStreamReplicator.js +36 -0
  36. package/dist/replication/WalStreamReplicator.js.map +1 -0
  37. package/dist/replication/replication-index.d.ts +5 -0
  38. package/dist/replication/replication-index.js +6 -0
  39. package/dist/replication/replication-index.js.map +1 -0
  40. package/dist/replication/replication-utils.d.ts +32 -0
  41. package/dist/replication/replication-utils.js +272 -0
  42. package/dist/replication/replication-utils.js.map +1 -0
  43. package/dist/types/types.d.ts +76 -0
  44. package/dist/types/types.js +110 -0
  45. package/dist/types/types.js.map +1 -0
  46. package/dist/utils/migration_lib.d.ts +11 -0
  47. package/dist/utils/migration_lib.js +64 -0
  48. package/dist/utils/migration_lib.js.map +1 -0
  49. package/dist/utils/pgwire_utils.d.ts +16 -0
  50. package/dist/utils/pgwire_utils.js +70 -0
  51. package/dist/utils/pgwire_utils.js.map +1 -0
  52. package/dist/utils/populate_test_data.d.ts +8 -0
  53. package/dist/utils/populate_test_data.js +65 -0
  54. package/dist/utils/populate_test_data.js.map +1 -0
  55. package/package.json +49 -0
  56. package/src/api/PostgresRouteAPIAdapter.ts +307 -0
  57. package/src/auth/SupabaseKeyCollector.ts +70 -0
  58. package/src/index.ts +5 -0
  59. package/src/module/PostgresModule.ts +122 -0
  60. package/src/replication/ConnectionManagerFactory.ts +28 -0
  61. package/src/replication/PgManager.ts +70 -0
  62. package/src/replication/PgRelation.ts +31 -0
  63. package/src/replication/PostgresErrorRateLimiter.ts +44 -0
  64. package/src/replication/WalStream.ts +639 -0
  65. package/src/replication/WalStreamReplicationJob.ts +142 -0
  66. package/src/replication/WalStreamReplicator.ts +45 -0
  67. package/src/replication/replication-index.ts +5 -0
  68. package/src/replication/replication-utils.ts +329 -0
  69. package/src/types/types.ts +159 -0
  70. package/src/utils/migration_lib.ts +79 -0
  71. package/src/utils/pgwire_utils.ts +73 -0
  72. package/src/utils/populate_test_data.ts +77 -0
  73. package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
  74. package/test/src/env.ts +7 -0
  75. package/test/src/large_batch.test.ts +195 -0
  76. package/test/src/pg_test.test.ts +450 -0
  77. package/test/src/schema_changes.test.ts +543 -0
  78. package/test/src/setup.ts +7 -0
  79. package/test/src/slow_tests.test.ts +335 -0
  80. package/test/src/util.ts +105 -0
  81. package/test/src/validation.test.ts +64 -0
  82. package/test/src/wal_stream.test.ts +319 -0
  83. package/test/src/wal_stream_utils.ts +121 -0
  84. package/test/tsconfig.json +28 -0
  85. package/tsconfig.json +31 -0
  86. package/tsconfig.tsbuildinfo +1 -0
  87. package/vitest.config.ts +9 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # @powersync/service-module-postgres
2
+
3
+ ## 0.0.0-dev-20240918092408
4
+
5
+ ### Patch Changes
6
+
7
+ - d51921f: - Introduced modules to the powersync service architecture
8
+ - Core functionality has been moved to "engine" classes. Modules can register additional functionality with these engines.
9
+ - The sync API functionality used by the routes has been abstracted to an interface. API routes are now managed by the RouterEngine.
10
+ - Replication is managed by the ReplicationEngine and new replication data sources can be registered to the engine by modules.
11
+ - Refactored existing Postgres replication as a module.
12
+ - Removed Postgres specific code from the core service packages.
13
+ - Updated dependencies [d51921f]
14
+ - @powersync/service-core@0.0.0-dev-20240918092408
15
+ - @powersync/service-sync-rules@0.0.0-dev-20240918092408
16
+ - @powersync/lib-services-framework@0.0.0-dev-20240918092408
17
+ - @powersync/service-jpgwire@0.0.0-dev-20240918092408
18
+ - @powersync/service-types@0.0.0-dev-20240918092408
package/LICENSE ADDED
@@ -0,0 +1,67 @@
1
+ # Functional Source License, Version 1.1, Apache 2.0 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-Apache-2.0
6
+
7
+ ## Notice
8
+
9
+ Copyright 2023-2024 Journey Mobile, Inc.
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software.
20
+
21
+ ### License Grant
22
+
23
+ Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below.
24
+
25
+ ### Permitted Purpose
26
+
27
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that:
28
+
29
+ 1. substitutes for the Software;
30
+ 2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or
31
+ 3. offers the same or substantially similar functionality as the Software.
32
+
33
+ Permitted Purposes specifically include using the Software:
34
+
35
+ 1. for your internal use and access;
36
+ 2. for non-commercial education;
37
+ 3. for non-commercial research; and
38
+ 4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions.
39
+
40
+ ### Patents
41
+
42
+ To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately.
43
+
44
+ ### Redistribution
45
+
46
+ The Terms and Conditions apply to all copies, modifications and derivatives of the Software.
47
+ If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software.
48
+
49
+ ### Disclaimer
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
52
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
53
+
54
+ ### Trademarks
55
+
56
+ Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names.
57
+
58
+ ## Grant of Future License
59
+
60
+ We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply:
61
+
62
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
63
+ You may obtain a copy of the License at
64
+
65
+ http://www.apache.org/licenses/LICENSE-2.0
66
+
67
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # PowerSync Service Module Postgres
2
+
3
+ Postgres replication module for PowerSync
@@ -0,0 +1,22 @@
1
+ import { api, ParseSyncRulesOptions } from '@powersync/service-core';
2
+ import * as pgwire from '@powersync/service-jpgwire';
3
+ import * as sync_rules from '@powersync/service-sync-rules';
4
+ import * as service_types from '@powersync/service-types';
5
+ import * as types from '../types/types.js';
6
+ export declare class PostgresRouteAPIAdapter implements api.RouteAPI {
7
+ protected config: types.ResolvedConnectionConfig;
8
+ protected pool: pgwire.PgClient;
9
+ connectionTag: string;
10
+ publicationName: string;
11
+ constructor(config: types.ResolvedConnectionConfig);
12
+ getParseSyncRulesOptions(): ParseSyncRulesOptions;
13
+ shutdown(): Promise<void>;
14
+ getSourceConfig(): Promise<service_types.configFile.DataSourceConfig>;
15
+ getConnectionStatus(): Promise<service_types.ConnectionStatusV2>;
16
+ executeQuery(query: string, params: any[]): Promise<service_types.internal_routes.ExecuteSqlResponse>;
17
+ getDebugTablesInfo(tablePatterns: sync_rules.TablePattern[], sqlSyncRules: sync_rules.SqlSyncRules): Promise<api.PatternResult[]>;
18
+ protected getDebugTableInfo(tablePattern: sync_rules.TablePattern, name: string, relationId: number | null, syncRules: sync_rules.SqlSyncRules): Promise<service_types.TableInfo>;
19
+ getReplicationLag(syncRulesId: string): Promise<number>;
20
+ getReplicationHead(): Promise<string>;
21
+ getConnectionSchema(): Promise<service_types.DatabaseSchema[]>;
22
+ }
@@ -0,0 +1,273 @@
1
+ import * as pgwire from '@powersync/service-jpgwire';
2
+ import * as sync_rules from '@powersync/service-sync-rules';
3
+ import * as service_types from '@powersync/service-types';
4
+ import * as replication_utils from '../replication/replication-utils.js';
5
+ import * as types from '../types/types.js';
6
+ import * as pg_utils from '../utils/pgwire_utils.js';
7
+ import { getDebugTableInfo } from '../replication/replication-utils.js';
8
+ import { PUBLICATION_NAME } from '../replication/WalStream.js';
9
+ export class PostgresRouteAPIAdapter {
10
+ constructor(config) {
11
+ this.config = config;
12
+ // TODO this should probably be configurable one day
13
+ this.publicationName = PUBLICATION_NAME;
14
+ this.pool = pgwire.connectPgWirePool(config, {
15
+ idleTimeout: 30000
16
+ });
17
+ this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG;
18
+ }
19
+ getParseSyncRulesOptions() {
20
+ return {
21
+ defaultSchema: 'public'
22
+ };
23
+ }
24
+ async shutdown() {
25
+ await this.pool.end();
26
+ }
27
+ async getSourceConfig() {
28
+ return this.config;
29
+ }
30
+ async getConnectionStatus() {
31
+ const base = {
32
+ id: this.config.id,
33
+ uri: types.baseUri(this.config)
34
+ };
35
+ try {
36
+ await pg_utils.retriedQuery(this.pool, `SELECT 'PowerSync connection test'`);
37
+ }
38
+ catch (e) {
39
+ return {
40
+ ...base,
41
+ connected: false,
42
+ errors: [{ level: 'fatal', message: e.message }]
43
+ };
44
+ }
45
+ try {
46
+ await replication_utils.checkSourceConfiguration(this.pool, this.publicationName);
47
+ }
48
+ catch (e) {
49
+ return {
50
+ ...base,
51
+ connected: true,
52
+ errors: [{ level: 'fatal', message: e.message }]
53
+ };
54
+ }
55
+ return {
56
+ ...base,
57
+ connected: true,
58
+ errors: []
59
+ };
60
+ }
61
+ async executeQuery(query, params) {
62
+ if (!this.config.debug_api) {
63
+ return service_types.internal_routes.ExecuteSqlResponse.encode({
64
+ results: {
65
+ columns: [],
66
+ rows: []
67
+ },
68
+ success: false,
69
+ error: 'SQL querying is not enabled'
70
+ });
71
+ }
72
+ try {
73
+ const result = await this.pool.query({
74
+ statement: query,
75
+ params: params.map(pg_utils.autoParameter)
76
+ });
77
+ return service_types.internal_routes.ExecuteSqlResponse.encode({
78
+ success: true,
79
+ results: {
80
+ columns: result.columns.map((c) => c.name),
81
+ rows: result.rows.map((row) => {
82
+ return row.map((value) => {
83
+ const sqlValue = sync_rules.toSyncRulesValue(value);
84
+ if (typeof sqlValue == 'bigint') {
85
+ return Number(value);
86
+ }
87
+ else if (sync_rules.isJsonValue(sqlValue)) {
88
+ return sqlValue;
89
+ }
90
+ else {
91
+ return null;
92
+ }
93
+ });
94
+ })
95
+ }
96
+ });
97
+ }
98
+ catch (e) {
99
+ return service_types.internal_routes.ExecuteSqlResponse.encode({
100
+ results: {
101
+ columns: [],
102
+ rows: []
103
+ },
104
+ success: false,
105
+ error: e.message
106
+ });
107
+ }
108
+ }
109
+ async getDebugTablesInfo(tablePatterns, sqlSyncRules) {
110
+ let result = [];
111
+ for (let tablePattern of tablePatterns) {
112
+ const schema = tablePattern.schema;
113
+ let patternResult = {
114
+ schema: schema,
115
+ pattern: tablePattern.tablePattern,
116
+ wildcard: tablePattern.isWildcard
117
+ };
118
+ result.push(patternResult);
119
+ if (tablePattern.isWildcard) {
120
+ patternResult.tables = [];
121
+ const prefix = tablePattern.tablePrefix;
122
+ const results = await pg_utils.retriedQuery(this.pool, {
123
+ statement: `SELECT c.oid AS relid, c.relname AS table_name
124
+ FROM pg_class c
125
+ JOIN pg_namespace n ON n.oid = c.relnamespace
126
+ WHERE n.nspname = $1
127
+ AND c.relkind = 'r'
128
+ AND c.relname LIKE $2`,
129
+ params: [
130
+ { type: 'varchar', value: schema },
131
+ { type: 'varchar', value: tablePattern.tablePattern }
132
+ ]
133
+ });
134
+ for (let row of pgwire.pgwireRows(results)) {
135
+ const name = row.table_name;
136
+ const relationId = row.relid;
137
+ if (!name.startsWith(prefix)) {
138
+ continue;
139
+ }
140
+ const details = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules);
141
+ patternResult.tables.push(details);
142
+ }
143
+ }
144
+ else {
145
+ const results = await pg_utils.retriedQuery(this.pool, {
146
+ statement: `SELECT c.oid AS relid, c.relname AS table_name
147
+ FROM pg_class c
148
+ JOIN pg_namespace n ON n.oid = c.relnamespace
149
+ WHERE n.nspname = $1
150
+ AND c.relkind = 'r'
151
+ AND c.relname = $2`,
152
+ params: [
153
+ { type: 'varchar', value: schema },
154
+ { type: 'varchar', value: tablePattern.tablePattern }
155
+ ]
156
+ });
157
+ if (results.rows.length == 0) {
158
+ // Table not found
159
+ patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules);
160
+ }
161
+ else {
162
+ const row = pgwire.pgwireRows(results)[0];
163
+ const name = row.table_name;
164
+ const relationId = row.relid;
165
+ patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules);
166
+ }
167
+ }
168
+ }
169
+ return result;
170
+ }
171
+ async getDebugTableInfo(tablePattern, name, relationId, syncRules) {
172
+ return getDebugTableInfo({
173
+ db: this.pool,
174
+ name: name,
175
+ publicationName: this.publicationName,
176
+ connectionTag: this.connectionTag,
177
+ tablePattern: tablePattern,
178
+ relationId: relationId,
179
+ syncRules: syncRules
180
+ });
181
+ }
182
+ async getReplicationLag(syncRulesId) {
183
+ const results = await pg_utils.retriedQuery(this.pool, {
184
+ statement: `SELECT
185
+ slot_name,
186
+ confirmed_flush_lsn,
187
+ pg_current_wal_lsn(),
188
+ (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance
189
+ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`,
190
+ params: [{ type: 'varchar', value: syncRulesId }]
191
+ });
192
+ const [row] = pgwire.pgwireRows(results);
193
+ if (row) {
194
+ return Number(row.lsn_distance);
195
+ }
196
+ throw new Error(`Could not determine replication lag for slot ${syncRulesId}`);
197
+ }
198
+ async getReplicationHead() {
199
+ const [{ lsn }] = pgwire.pgwireRows(await pg_utils.retriedQuery(this.pool, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`));
200
+ return String(lsn);
201
+ }
202
+ async getConnectionSchema() {
203
+ var _a;
204
+ // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts
205
+ const results = await pg_utils.retriedQuery(this.pool, `SELECT
206
+ tbl.schemaname,
207
+ tbl.tablename,
208
+ tbl.quoted_name,
209
+ json_agg(a ORDER BY attnum) as columns
210
+ FROM
211
+ (
212
+ SELECT
213
+ n.nspname as schemaname,
214
+ c.relname as tablename,
215
+ (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name
216
+ FROM
217
+ pg_catalog.pg_class c
218
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
219
+ WHERE
220
+ c.relkind = 'r'
221
+ AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast')
222
+ AND n.nspname not like 'pg_temp_%'
223
+ AND n.nspname not like 'pg_toast_temp_%'
224
+ AND c.relnatts > 0
225
+ AND has_schema_privilege(n.oid, 'USAGE') = true
226
+ AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true
227
+ ) as tbl
228
+ LEFT JOIN (
229
+ SELECT
230
+ attrelid,
231
+ attname,
232
+ format_type(atttypid, atttypmod) as data_type,
233
+ (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type,
234
+ attnum,
235
+ attisdropped
236
+ FROM
237
+ pg_attribute
238
+ ) as a ON (
239
+ a.attrelid = tbl.quoted_name::regclass
240
+ AND a.attnum > 0
241
+ AND NOT a.attisdropped
242
+ AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES')
243
+ )
244
+ GROUP BY schemaname, tablename, quoted_name`);
245
+ const rows = pgwire.pgwireRows(results);
246
+ let schemas = {};
247
+ for (let row of rows) {
248
+ const schema = (schemas[_a = row.schemaname] ?? (schemas[_a] = {
249
+ name: row.schemaname,
250
+ tables: []
251
+ }));
252
+ const table = {
253
+ name: row.tablename,
254
+ columns: []
255
+ };
256
+ schema.tables.push(table);
257
+ const columnInfo = JSON.parse(row.columns);
258
+ for (let column of columnInfo) {
259
+ let pg_type = column.pg_type;
260
+ if (pg_type.startsWith('_')) {
261
+ pg_type = `${pg_type.substring(1)}[]`;
262
+ }
263
+ table.columns.push({
264
+ name: column.attname,
265
+ type: column.data_type,
266
+ pg_type: pg_type
267
+ });
268
+ }
269
+ }
270
+ return Object.values(schemas);
271
+ }
272
+ }
273
+ //# sourceMappingURL=PostgresRouteAPIAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresRouteAPIAdapter.js","sourceRoot":"","sources":["../../src/api/PostgresRouteAPIAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { auth } from '@powersync/service-core';
2
+ import * as jose from 'jose';
3
+ import * as types from '../types/types.js';
4
+ /**
5
+ * Fetches key from the Supabase database.
6
+ *
7
+ * Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
8
+ * before receiving a valid token.
9
+ */
10
+ export declare class SupabaseKeyCollector implements auth.KeyCollector {
11
+ private pool;
12
+ private keyOptions;
13
+ constructor(connectionConfig: types.ResolvedConnectionConfig);
14
+ shutdown(): Promise<void>;
15
+ getKeys(): Promise<{
16
+ keys: never[];
17
+ errors: jose.errors.JWKSNoMatchingKey[];
18
+ } | {
19
+ keys: auth.KeySpec[];
20
+ errors: never[];
21
+ }>;
22
+ }
@@ -0,0 +1,64 @@
1
+ import { auth } from '@powersync/service-core';
2
+ import * as pgwire from '@powersync/service-jpgwire';
3
+ import * as jose from 'jose';
4
+ import * as pgwire_utils from '../utils/pgwire_utils.js';
5
+ /**
6
+ * Fetches key from the Supabase database.
7
+ *
8
+ * Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
9
+ * before receiving a valid token.
10
+ */
11
+ export class SupabaseKeyCollector {
12
+ constructor(connectionConfig) {
13
+ this.keyOptions = {
14
+ requiresAudience: ['authenticated'],
15
+ maxLifetimeSeconds: 86400 * 7 + 1200 // 1 week + 20 minutes margin
16
+ };
17
+ this.pool = pgwire.connectPgWirePool(connectionConfig, {
18
+ // To avoid overloading the source database with open connections,
19
+ // limit to a single connection, and close the connection shortly
20
+ // after using it.
21
+ idleTimeout: 5000,
22
+ maxSize: 1
23
+ });
24
+ }
25
+ shutdown() {
26
+ return this.pool.end();
27
+ }
28
+ async getKeys() {
29
+ let row;
30
+ try {
31
+ const rows = pgwire.pgwireRows(await pgwire_utils.retriedQuery(this.pool, `SELECT current_setting('app.settings.jwt_secret') as jwt_secret`));
32
+ row = rows[0];
33
+ }
34
+ catch (e) {
35
+ if (e.message?.includes('unrecognized configuration parameter')) {
36
+ throw new jose.errors.JOSEError(`Generate a new JWT secret on Supabase. Cause: ${e.message}`);
37
+ }
38
+ else {
39
+ throw e;
40
+ }
41
+ }
42
+ const secret = row?.jwt_secret;
43
+ if (secret == null) {
44
+ return {
45
+ keys: [],
46
+ errors: [new jose.errors.JWKSNoMatchingKey()]
47
+ };
48
+ }
49
+ else {
50
+ const key = {
51
+ kty: 'oct',
52
+ alg: 'HS256',
53
+ // While the secret is valid base64, the base64-encoded form is the secret value.
54
+ k: Buffer.from(secret, 'utf8').toString('base64url')
55
+ };
56
+ const imported = await auth.KeySpec.importKey(key, this.keyOptions);
57
+ return {
58
+ keys: [imported],
59
+ errors: []
60
+ };
61
+ }
62
+ }
63
+ }
64
+ //# sourceMappingURL=SupabaseKeyCollector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SupabaseKeyCollector.js","sourceRoot":"","sources":["../../src/auth/SupabaseKeyCollector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,KAAK,YAAY,MAAM,0BAA0B,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IAQ/B,YAAY,gBAAgD;QALpD,eAAU,GAAoB;YACpC,gBAAgB,EAAE,CAAC,eAAe,CAAC;YACnC,kBAAkB,EAAE,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,6BAA6B;SACnE,CAAC;QAGA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,EAAE;YACrD,kEAAkE;YAClE,iEAAiE;YACjE,kBAAkB;YAClB,WAAW,EAAE,IAAK;YAClB,OAAO,EAAE,CAAC;SACX,CAAC,CAAC;IACL,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,GAA2B,CAAC;QAChC,IAAI;YACF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAC5B,MAAM,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,iEAAiE,CAAC,CAC9G,CAAC;YACF,GAAG,GAAG,IAAI,CAAC,CAAC,CAAQ,CAAC;SACtB;QAAC,OAAO,CAAC,EAAE;YACV,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,sCAAsC,CAAC,EAAE;gBAC/D,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;aAC/F;iBAAM;gBACL,MAAM,CAAC,CAAC;aACT;SACF;QACD,MAAM,MAAM,GAAG,GAAG,EAAE,UAAgC,CAAC;QACrD,IAAI,MAAM,IAAI,IAAI,EAAE;YAClB,OAAO;gBACL,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;aAC9C,CAAC;SACH;aAAM;YACL,MAAM,GAAG,GAAa;gBACpB,GAAG,EAAE,KAAK;gBACV,GAAG,EAAE,OAAO;gBACZ,iFAAiF;gBACjF,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;aACrD,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACpE,OAAO;gBACL,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAChB,MAAM,EAAE,EAAE;aACX,CAAC;SACH;IACH,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import { PostgresModule } from './module/PostgresModule.js';
2
+ export declare const module: PostgresModule;
3
+ export default module;
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { PostgresModule } from './module/PostgresModule.js';
2
+ export const module = new PostgresModule();
3
+ export default module;
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;AAE3C,eAAe,MAAM,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { api, replication, system, TearDownOptions } from '@powersync/service-core';
2
+ import * as types from '../types/types.js';
3
+ export declare class PostgresModule extends replication.ReplicationModule<types.PostgresConnectionConfig> {
4
+ constructor();
5
+ initialize(context: system.ServiceContextContainer): Promise<void>;
6
+ protected createRouteAPIAdapter(): api.RouteAPI;
7
+ protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator;
8
+ /**
9
+ * Combines base config with normalized connection settings
10
+ */
11
+ private resolveConfig;
12
+ teardown(options: TearDownOptions): Promise<void>;
13
+ private registerSupabaseAuth;
14
+ }
@@ -0,0 +1,108 @@
1
+ import { auth, ConfigurationFileSyncRulesProvider, replication } from '@powersync/service-core';
2
+ import * as jpgwire from '@powersync/service-jpgwire';
3
+ import * as types from '../types/types.js';
4
+ import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js';
5
+ import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js';
6
+ import { WalStreamReplicator } from '../replication/WalStreamReplicator.js';
7
+ import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js';
8
+ import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js';
9
+ import { cleanUpReplicationSlot } from '../replication/replication-utils.js';
10
+ import { PgManager } from '../replication/PgManager.js';
11
+ export class PostgresModule extends replication.ReplicationModule {
12
+ constructor() {
13
+ super({
14
+ name: 'Postgres',
15
+ type: types.POSTGRES_CONNECTION_TYPE,
16
+ configSchema: types.PostgresConnectionConfig
17
+ });
18
+ }
19
+ async initialize(context) {
20
+ await super.initialize(context);
21
+ // Record replicated bytes using global jpgwire metrics.
22
+ if (context.configuration.base_config.client_auth?.supabase) {
23
+ this.registerSupabaseAuth(context);
24
+ }
25
+ if (context.metrics) {
26
+ jpgwire.setMetricsRecorder({
27
+ addBytesRead(bytes) {
28
+ context.metrics.data_replicated_bytes.add(bytes);
29
+ }
30
+ });
31
+ }
32
+ }
33
+ createRouteAPIAdapter() {
34
+ return new PostgresRouteAPIAdapter(this.resolveConfig(this.decodedConfig));
35
+ }
36
+ createReplicator(context) {
37
+ const normalisedConfig = this.resolveConfig(this.decodedConfig);
38
+ const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules);
39
+ const connectionFactory = new ConnectionManagerFactory(normalisedConfig);
40
+ return new WalStreamReplicator({
41
+ id: this.getDefaultId(normalisedConfig.database),
42
+ syncRuleProvider: syncRuleProvider,
43
+ storageEngine: context.storageEngine,
44
+ connectionFactory: connectionFactory,
45
+ rateLimiter: new PostgresErrorRateLimiter()
46
+ });
47
+ }
48
+ /**
49
+ * Combines base config with normalized connection settings
50
+ */
51
+ resolveConfig(config) {
52
+ return {
53
+ ...config,
54
+ ...types.normalizeConnectionConfig(config)
55
+ };
56
+ }
57
+ async teardown(options) {
58
+ const normalisedConfig = this.resolveConfig(this.decodedConfig);
59
+ const connectionManager = new PgManager(normalisedConfig, {
60
+ idleTimeout: 30000,
61
+ maxSize: 1
62
+ });
63
+ try {
64
+ if (options.syncRules) {
65
+ // TODO: In the future, once we have more replication types, we will need to check if these syncRules are for Postgres
66
+ for (let syncRules of options.syncRules) {
67
+ try {
68
+ await cleanUpReplicationSlot(syncRules.slot_name, connectionManager.pool);
69
+ }
70
+ catch (e) {
71
+ // Not really much we can do here for failures, most likely the database is no longer accessible
72
+ this.logger.warn(`Failed to fully clean up Postgres replication slot: ${syncRules.slot_name}`, e);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ finally {
78
+ await connectionManager.end();
79
+ }
80
+ }
81
+ // TODO: This should rather be done by registering the key collector in some kind of auth engine
82
+ registerSupabaseAuth(context) {
83
+ const { configuration } = context;
84
+ // Register the Supabase key collector(s)
85
+ configuration.connections
86
+ ?.map((baseConfig) => {
87
+ if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) {
88
+ return;
89
+ }
90
+ try {
91
+ return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig));
92
+ }
93
+ catch (ex) {
94
+ this.logger.warn('Failed to decode configuration.', ex);
95
+ }
96
+ })
97
+ .filter((c) => !!c)
98
+ .forEach((config) => {
99
+ const keyCollector = new SupabaseKeyCollector(config);
100
+ context.lifeCycleEngine.withLifecycle(keyCollector, {
101
+ // Close the internal pool
102
+ stop: (collector) => collector.shutdown()
103
+ });
104
+ configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector));
105
+ });
106
+ }
107
+ }
108
+ //# sourceMappingURL=PostgresModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresModule.js","sourceRoot":"","sources":["../../src/module/PostgresModule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { PgManager } from './PgManager.js';
2
+ import { NormalizedPostgresConnectionConfig } from '../types/types.js';
3
+ import { PgPoolOptions } from '@powersync/service-jpgwire';
4
+ export declare class ConnectionManagerFactory {
5
+ private readonly connectionManagers;
6
+ private readonly dbConnectionConfig;
7
+ constructor(dbConnectionConfig: NormalizedPostgresConnectionConfig);
8
+ create(poolOptions: PgPoolOptions): PgManager;
9
+ shutdown(): Promise<void>;
10
+ }