@powersync/lib-service-postgres 0.0.0-dev-20250116115804
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 +27 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dist/db/connection/AbstractPostgresConnection.d.ts +29 -0
- package/dist/db/connection/AbstractPostgresConnection.js +82 -0
- package/dist/db/connection/AbstractPostgresConnection.js.map +1 -0
- package/dist/db/connection/ConnectionSlot.d.ts +41 -0
- package/dist/db/connection/ConnectionSlot.js +122 -0
- package/dist/db/connection/ConnectionSlot.js.map +1 -0
- package/dist/db/connection/DatabaseClient.d.ts +62 -0
- package/dist/db/connection/DatabaseClient.js +209 -0
- package/dist/db/connection/DatabaseClient.js.map +1 -0
- package/dist/db/connection/WrappedConnection.d.ts +9 -0
- package/dist/db/connection/WrappedConnection.js +11 -0
- package/dist/db/connection/WrappedConnection.js.map +1 -0
- package/dist/db/db-index.d.ts +4 -0
- package/dist/db/db-index.js +5 -0
- package/dist/db/db-index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/locks/PostgresLockManager.d.ts +17 -0
- package/dist/locks/PostgresLockManager.js +112 -0
- package/dist/locks/PostgresLockManager.js.map +1 -0
- package/dist/locks/locks-index.d.ts +1 -0
- package/dist/locks/locks-index.js +2 -0
- package/dist/locks/locks-index.js.map +1 -0
- package/dist/types/types.d.ts +70 -0
- package/dist/types/types.js +119 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/pgwire_utils.d.ts +5 -0
- package/dist/utils/pgwire_utils.js +47 -0
- package/dist/utils/pgwire_utils.js.map +1 -0
- package/dist/utils/utils-index.d.ts +1 -0
- package/dist/utils/utils-index.js +2 -0
- package/dist/utils/utils-index.js.map +1 -0
- package/package.json +42 -0
- package/src/db/connection/AbstractPostgresConnection.ts +109 -0
- package/src/db/connection/ConnectionSlot.ts +165 -0
- package/src/db/connection/DatabaseClient.ts +261 -0
- package/src/db/connection/WrappedConnection.ts +11 -0
- package/src/db/db-index.ts +4 -0
- package/src/index.ts +11 -0
- package/src/locks/PostgresLockManager.ts +128 -0
- package/src/locks/locks-index.ts +1 -0
- package/src/types/types.ts +148 -0
- package/src/utils/pgwire_utils.ts +48 -0
- package/src/utils/utils-index.ts +1 -0
- package/test/src/config.test.ts +12 -0
- package/test/tsconfig.json +18 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseClient.js","sourceRoot":"","sources":["../../../src/db/connection/DatabaseClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
2
|
+
import { AbstractPostgresConnection } from './AbstractPostgresConnection.js';
|
|
3
|
+
/**
|
|
4
|
+
* Provides helper functionality to transaction contexts given an existing PGWire connection
|
|
5
|
+
*/
|
|
6
|
+
export declare class WrappedConnection extends AbstractPostgresConnection {
|
|
7
|
+
protected baseConnection: pgwire.PgConnection;
|
|
8
|
+
constructor(baseConnection: pgwire.PgConnection);
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AbstractPostgresConnection } from './AbstractPostgresConnection.js';
|
|
2
|
+
/**
|
|
3
|
+
* Provides helper functionality to transaction contexts given an existing PGWire connection
|
|
4
|
+
*/
|
|
5
|
+
export class WrappedConnection extends AbstractPostgresConnection {
|
|
6
|
+
constructor(baseConnection) {
|
|
7
|
+
super();
|
|
8
|
+
this.baseConnection = baseConnection;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=WrappedConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WrappedConnection.js","sourceRoot":"","sources":["../../../src/db/connection/WrappedConnection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAE7E;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,0BAA0B;IAC/D,YAAsB,cAAmC;QACvD,KAAK,EAAE,CAAC;QADY,mBAAc,GAAd,cAAc,CAAqB;IAEzD,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-index.js","sourceRoot":"","sources":["../../src/db/db-index.ts"],"names":[],"mappings":"AAAA,cAAc,4CAA4C,CAAC;AAC3D,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './db/db-index.js';
|
|
2
|
+
export * as db from './db/db-index.js';
|
|
3
|
+
export * from './locks/locks-index.js';
|
|
4
|
+
export * as locks from './locks/locks-index.js';
|
|
5
|
+
export * from './types/types.js';
|
|
6
|
+
export * as types from './types/types.js';
|
|
7
|
+
export * from './utils/utils-index.js';
|
|
8
|
+
export * as utils from './utils/utils-index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './db/db-index.js';
|
|
2
|
+
export * as db from './db/db-index.js';
|
|
3
|
+
export * from './locks/locks-index.js';
|
|
4
|
+
export * as locks from './locks/locks-index.js';
|
|
5
|
+
export * from './types/types.js';
|
|
6
|
+
export * as types from './types/types.js';
|
|
7
|
+
export * from './utils/utils-index.js';
|
|
8
|
+
export * as utils from './utils/utils-index.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEvC,cAAc,wBAAwB,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAEhD,cAAc,kBAAkB,CAAC;AACjC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAE1C,cAAc,wBAAwB,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as framework from '@powersync/lib-services-framework';
|
|
2
|
+
import { DatabaseClient } from '../db/db-index.js';
|
|
3
|
+
export interface PostgresLockManagerParams extends framework.locks.LockManagerParams {
|
|
4
|
+
db: DatabaseClient;
|
|
5
|
+
}
|
|
6
|
+
export declare class PostgresLockManager extends framework.locks.AbstractLockManager {
|
|
7
|
+
protected params: PostgresLockManagerParams;
|
|
8
|
+
constructor(params: PostgresLockManagerParams);
|
|
9
|
+
protected get db(): DatabaseClient;
|
|
10
|
+
get timeout(): number;
|
|
11
|
+
get name(): string;
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
protected acquireHandle(): Promise<framework.LockHandle | null>;
|
|
14
|
+
protected _acquireId(): Promise<string | null>;
|
|
15
|
+
protected refreshHandle(lockId: string): Promise<void>;
|
|
16
|
+
protected releaseHandle(lockId: string): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as framework from '@powersync/lib-services-framework';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { sql } from '../db/db-index.js';
|
|
4
|
+
const DEFAULT_LOCK_TIMEOUT = 60000; // 1 minute
|
|
5
|
+
export class PostgresLockManager extends framework.locks.AbstractLockManager {
|
|
6
|
+
constructor(params) {
|
|
7
|
+
super(params);
|
|
8
|
+
this.params = params;
|
|
9
|
+
}
|
|
10
|
+
get db() {
|
|
11
|
+
return this.params.db;
|
|
12
|
+
}
|
|
13
|
+
get timeout() {
|
|
14
|
+
return this.params.timeout ?? DEFAULT_LOCK_TIMEOUT;
|
|
15
|
+
}
|
|
16
|
+
get name() {
|
|
17
|
+
return this.params.name;
|
|
18
|
+
}
|
|
19
|
+
async init() {
|
|
20
|
+
/**
|
|
21
|
+
* Locks are required for migrations, which means this table can't be
|
|
22
|
+
* created inside a migration. This ensures the locks table is present.
|
|
23
|
+
*/
|
|
24
|
+
await this.db.query(sql `
|
|
25
|
+
CREATE TABLE IF NOT EXISTS locks (
|
|
26
|
+
name TEXT PRIMARY KEY,
|
|
27
|
+
lock_id UUID NOT NULL,
|
|
28
|
+
ts TIMESTAMPTZ NOT NULL
|
|
29
|
+
);
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
async acquireHandle() {
|
|
33
|
+
const id = await this._acquireId();
|
|
34
|
+
if (!id) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
refresh: () => this.refreshHandle(id),
|
|
39
|
+
release: () => this.releaseHandle(id)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async _acquireId() {
|
|
43
|
+
const now = new Date();
|
|
44
|
+
const nowISO = now.toISOString();
|
|
45
|
+
const expiredTs = new Date(now.getTime() - this.timeout).toISOString();
|
|
46
|
+
const lockId = uuidv4();
|
|
47
|
+
try {
|
|
48
|
+
// Attempt to acquire or refresh the lock
|
|
49
|
+
const res = await this.db.queryRows(sql `
|
|
50
|
+
INSERT INTO
|
|
51
|
+
locks (name, lock_id, ts)
|
|
52
|
+
VALUES
|
|
53
|
+
(
|
|
54
|
+
${{ type: 'varchar', value: this.name }},
|
|
55
|
+
${{ type: 'uuid', value: lockId }},
|
|
56
|
+
${{ type: 1184, value: nowISO }}
|
|
57
|
+
)
|
|
58
|
+
ON CONFLICT (name) DO UPDATE
|
|
59
|
+
SET
|
|
60
|
+
lock_id = CASE
|
|
61
|
+
WHEN locks.ts <= ${{ type: 1184, value: expiredTs }} THEN ${{ type: 'uuid', value: lockId }}
|
|
62
|
+
ELSE locks.lock_id
|
|
63
|
+
END,
|
|
64
|
+
ts = CASE
|
|
65
|
+
WHEN locks.ts <= ${{ type: 1184, value: expiredTs }} THEN ${{
|
|
66
|
+
type: 1184,
|
|
67
|
+
value: nowISO
|
|
68
|
+
}}
|
|
69
|
+
ELSE locks.ts
|
|
70
|
+
END
|
|
71
|
+
RETURNING
|
|
72
|
+
lock_id;
|
|
73
|
+
`);
|
|
74
|
+
if (res.length == 0 || res[0].lock_id !== lockId) {
|
|
75
|
+
// Lock is active and could not be acquired
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return lockId;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
console.error('Error acquiring lock:', err);
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async refreshHandle(lockId) {
|
|
86
|
+
const res = await this.db.query(sql `
|
|
87
|
+
UPDATE locks
|
|
88
|
+
SET
|
|
89
|
+
ts = ${{ type: 1184, value: new Date().toISOString() }}
|
|
90
|
+
WHERE
|
|
91
|
+
lock_id = ${{ type: 'uuid', value: lockId }}
|
|
92
|
+
RETURNING
|
|
93
|
+
lock_id;
|
|
94
|
+
`);
|
|
95
|
+
if (res.rows.length === 0) {
|
|
96
|
+
throw new Error('Lock not found, could not refresh');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async releaseHandle(lockId) {
|
|
100
|
+
const res = await this.db.query(sql `
|
|
101
|
+
DELETE FROM locks
|
|
102
|
+
WHERE
|
|
103
|
+
lock_id = ${{ type: 'uuid', value: lockId }}
|
|
104
|
+
RETURNING
|
|
105
|
+
lock_id;
|
|
106
|
+
`);
|
|
107
|
+
if (res.rows.length == 0) {
|
|
108
|
+
throw new Error('Lock not found, could not release');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=PostgresLockManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresLockManager.js","sourceRoot":"","sources":["../../src/locks/PostgresLockManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PostgresLockManager.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locks-index.js","sourceRoot":"","sources":["../../src/locks/locks-index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type * as jpgwire from '@powersync/service-jpgwire';
|
|
2
|
+
import * as t from 'ts-codec';
|
|
3
|
+
export interface NormalizedBasePostgresConnectionConfig extends jpgwire.NormalizedConnectionConfig {
|
|
4
|
+
}
|
|
5
|
+
export declare const POSTGRES_CONNECTION_TYPE: "postgresql";
|
|
6
|
+
export declare const BasePostgresConnectionConfig: t.ObjectCodec<{
|
|
7
|
+
/** Unique identifier for the connection - optional when a single connection is present. */
|
|
8
|
+
id: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
9
|
+
/** Additional meta tag for connection */
|
|
10
|
+
tag: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
11
|
+
type: t.LiteralCodec<"postgresql">;
|
|
12
|
+
uri: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
13
|
+
hostname: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
14
|
+
port: t.OptionalCodec<t.Codec<number, string | number, string, t.CodecProps>>;
|
|
15
|
+
username: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
16
|
+
password: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
17
|
+
database: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
18
|
+
/** Defaults to verify-full */
|
|
19
|
+
sslmode: t.OptionalCodec<t.Codec<"verify-full" | "verify-ca" | "disable", "verify-full" | "verify-ca" | "disable", string, t.CodecProps>>;
|
|
20
|
+
/** Required for verify-ca, optional for verify-full */
|
|
21
|
+
cacert: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
22
|
+
client_certificate: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
23
|
+
client_private_key: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
24
|
+
/** Specify to use a servername for TLS that is different from hostname. */
|
|
25
|
+
tls_servername: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
26
|
+
/**
|
|
27
|
+
* Block connections in any of these IP ranges.
|
|
28
|
+
*
|
|
29
|
+
* Use 'local' to block anything not in public unicast ranges.
|
|
30
|
+
*/
|
|
31
|
+
reject_ip_ranges: t.OptionalCodec<t.Codec<string[], string[], string, t.CodecProps>>;
|
|
32
|
+
/**
|
|
33
|
+
* Prefix for the slot name. Defaults to "powersync_"
|
|
34
|
+
*/
|
|
35
|
+
slot_name_prefix: t.OptionalCodec<t.Codec<string, string, string, t.CodecProps>>;
|
|
36
|
+
}>;
|
|
37
|
+
export type BasePostgresConnectionConfig = t.Encoded<typeof BasePostgresConnectionConfig>;
|
|
38
|
+
export type BasePostgresConnectionConfigDecoded = t.Decoded<typeof BasePostgresConnectionConfig>;
|
|
39
|
+
/**
|
|
40
|
+
* Validate and normalize connection options.
|
|
41
|
+
*
|
|
42
|
+
* Returns destructured options.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeConnectionConfig(options: BasePostgresConnectionConfigDecoded): {
|
|
45
|
+
id: string;
|
|
46
|
+
tag: string;
|
|
47
|
+
hostname: string;
|
|
48
|
+
port: number;
|
|
49
|
+
database: string;
|
|
50
|
+
username: string;
|
|
51
|
+
password: string;
|
|
52
|
+
sslmode: "verify-full" | "verify-ca" | "disable";
|
|
53
|
+
cacert: string | undefined;
|
|
54
|
+
tls_servername: string | undefined;
|
|
55
|
+
lookup: import("net").LookupFunction | undefined;
|
|
56
|
+
client_certificate: string | undefined;
|
|
57
|
+
client_private_key: string | undefined;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Check whether the port is in a "safe" range.
|
|
61
|
+
*
|
|
62
|
+
* We do not support connecting to "privileged" ports.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validatePort(port: string | number): number;
|
|
65
|
+
/**
|
|
66
|
+
* Construct a postgres URI, without username, password or ssl options.
|
|
67
|
+
*
|
|
68
|
+
* Only contains hostname, port, database.
|
|
69
|
+
*/
|
|
70
|
+
export declare function baseUri(options: NormalizedBasePostgresConnectionConfig): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { makeHostnameLookupFunction } from '@powersync/lib-services-framework';
|
|
2
|
+
import * as service_types from '@powersync/service-types';
|
|
3
|
+
import * as t from 'ts-codec';
|
|
4
|
+
import * as urijs from 'uri-js';
|
|
5
|
+
export const POSTGRES_CONNECTION_TYPE = 'postgresql';
|
|
6
|
+
export const BasePostgresConnectionConfig = t.object({
|
|
7
|
+
/** Unique identifier for the connection - optional when a single connection is present. */
|
|
8
|
+
id: t.string.optional(),
|
|
9
|
+
/** Additional meta tag for connection */
|
|
10
|
+
tag: t.string.optional(),
|
|
11
|
+
type: t.literal(POSTGRES_CONNECTION_TYPE),
|
|
12
|
+
uri: t.string.optional(),
|
|
13
|
+
hostname: t.string.optional(),
|
|
14
|
+
port: service_types.configFile.portCodec.optional(),
|
|
15
|
+
username: t.string.optional(),
|
|
16
|
+
password: t.string.optional(),
|
|
17
|
+
database: t.string.optional(),
|
|
18
|
+
/** Defaults to verify-full */
|
|
19
|
+
sslmode: t.literal('verify-full').or(t.literal('verify-ca')).or(t.literal('disable')).optional(),
|
|
20
|
+
/** Required for verify-ca, optional for verify-full */
|
|
21
|
+
cacert: t.string.optional(),
|
|
22
|
+
client_certificate: t.string.optional(),
|
|
23
|
+
client_private_key: t.string.optional(),
|
|
24
|
+
/** Specify to use a servername for TLS that is different from hostname. */
|
|
25
|
+
tls_servername: t.string.optional(),
|
|
26
|
+
/**
|
|
27
|
+
* Block connections in any of these IP ranges.
|
|
28
|
+
*
|
|
29
|
+
* Use 'local' to block anything not in public unicast ranges.
|
|
30
|
+
*/
|
|
31
|
+
reject_ip_ranges: t.array(t.string).optional(),
|
|
32
|
+
/**
|
|
33
|
+
* Prefix for the slot name. Defaults to "powersync_"
|
|
34
|
+
*/
|
|
35
|
+
slot_name_prefix: t.string.optional()
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Validate and normalize connection options.
|
|
39
|
+
*
|
|
40
|
+
* Returns destructured options.
|
|
41
|
+
*/
|
|
42
|
+
export function normalizeConnectionConfig(options) {
|
|
43
|
+
let uri;
|
|
44
|
+
if (options.uri) {
|
|
45
|
+
uri = urijs.parse(options.uri);
|
|
46
|
+
if (uri.scheme != 'postgresql' && uri.scheme != 'postgres') {
|
|
47
|
+
`Invalid URI - protocol must be postgresql, got ${uri.scheme}`;
|
|
48
|
+
}
|
|
49
|
+
else if (uri.scheme != 'postgresql') {
|
|
50
|
+
uri.scheme = 'postgresql';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
uri = urijs.parse('postgresql:///');
|
|
55
|
+
}
|
|
56
|
+
const hostname = options.hostname ?? uri.host ?? '';
|
|
57
|
+
const port = validatePort(options.port ?? uri.port ?? 5432);
|
|
58
|
+
const database = options.database ?? uri.path?.substring(1) ?? '';
|
|
59
|
+
const [uri_username, uri_password] = (uri.userinfo ?? '').split(':');
|
|
60
|
+
const username = options.username ?? uri_username ?? '';
|
|
61
|
+
const password = options.password ?? uri_password ?? '';
|
|
62
|
+
const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI
|
|
63
|
+
const cacert = options.cacert;
|
|
64
|
+
if (sslmode == 'verify-ca' && cacert == null) {
|
|
65
|
+
throw new Error('Explicit cacert is required for sslmode=verify-ca');
|
|
66
|
+
}
|
|
67
|
+
if (hostname == '') {
|
|
68
|
+
throw new Error(`hostname required`);
|
|
69
|
+
}
|
|
70
|
+
if (username == '') {
|
|
71
|
+
throw new Error(`username required`);
|
|
72
|
+
}
|
|
73
|
+
if (password == '') {
|
|
74
|
+
throw new Error(`password required`);
|
|
75
|
+
}
|
|
76
|
+
if (database == '') {
|
|
77
|
+
throw new Error(`database required`);
|
|
78
|
+
}
|
|
79
|
+
const lookupOptions = { reject_ip_ranges: options.reject_ip_ranges ?? [] };
|
|
80
|
+
const lookup = makeHostnameLookupFunction(hostname, lookupOptions);
|
|
81
|
+
return {
|
|
82
|
+
id: options.id ?? 'default',
|
|
83
|
+
tag: options.tag ?? 'default',
|
|
84
|
+
hostname,
|
|
85
|
+
port,
|
|
86
|
+
database,
|
|
87
|
+
username,
|
|
88
|
+
password,
|
|
89
|
+
sslmode,
|
|
90
|
+
cacert,
|
|
91
|
+
tls_servername: options.tls_servername ?? undefined,
|
|
92
|
+
lookup,
|
|
93
|
+
client_certificate: options.client_certificate ?? undefined,
|
|
94
|
+
client_private_key: options.client_private_key ?? undefined
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check whether the port is in a "safe" range.
|
|
99
|
+
*
|
|
100
|
+
* We do not support connecting to "privileged" ports.
|
|
101
|
+
*/
|
|
102
|
+
export function validatePort(port) {
|
|
103
|
+
if (typeof port == 'string') {
|
|
104
|
+
port = parseInt(port);
|
|
105
|
+
}
|
|
106
|
+
if (port < 1024) {
|
|
107
|
+
throw new Error(`Port ${port} not supported`);
|
|
108
|
+
}
|
|
109
|
+
return port;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Construct a postgres URI, without username, password or ssl options.
|
|
113
|
+
*
|
|
114
|
+
* Only contains hostname, port, database.
|
|
115
|
+
*/
|
|
116
|
+
export function baseUri(options) {
|
|
117
|
+
return `postgresql://${options.hostname}:${options.port}/${options.database}`;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
2
|
+
export declare function escapeIdentifier(identifier: string): string;
|
|
3
|
+
export declare function autoParameter(arg: any): pgwire.StatementParam;
|
|
4
|
+
export declare function retriedQuery(db: pgwire.PgClient, ...statements: pgwire.Statement[]): Promise<pgwire.PgResult>;
|
|
5
|
+
export declare function retriedQuery(db: pgwire.PgClient, query: string): Promise<pgwire.PgResult>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Adapted from https://github.com/kagis/pgwire/blob/0dc927f9f8990a903f238737326e53ba1c8d094f/mod.js#L2218
|
|
2
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
3
|
+
export function escapeIdentifier(identifier) {
|
|
4
|
+
return `"${identifier.replace(/"/g, '""').replace(/\./g, '"."')}"`;
|
|
5
|
+
}
|
|
6
|
+
export function autoParameter(arg) {
|
|
7
|
+
if (arg == null) {
|
|
8
|
+
return { type: 'varchar', value: null };
|
|
9
|
+
}
|
|
10
|
+
else if (typeof arg == 'string') {
|
|
11
|
+
return { type: 'varchar', value: arg };
|
|
12
|
+
}
|
|
13
|
+
else if (typeof arg == 'number') {
|
|
14
|
+
if (Number.isInteger(arg)) {
|
|
15
|
+
return { type: 'int8', value: arg };
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return { type: 'float8', value: arg };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else if (typeof arg == 'boolean') {
|
|
22
|
+
return { type: 'bool', value: arg };
|
|
23
|
+
}
|
|
24
|
+
else if (typeof arg == 'bigint') {
|
|
25
|
+
return { type: 'int8', value: arg };
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`Unsupported query parameter: ${typeof arg}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retry a simple query - up to 2 attempts total.
|
|
33
|
+
*/
|
|
34
|
+
export async function retriedQuery(db, ...args) {
|
|
35
|
+
for (let tries = 2;; tries--) {
|
|
36
|
+
try {
|
|
37
|
+
return await db.query(...args);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (tries == 1) {
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
logger.warn('Query error, retrying', e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=pgwire_utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pgwire_utils.js","sourceRoot":"","sources":["../../src/utils/pgwire_utils.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAI1G,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAQ;IACpC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC1C,CAAC;SAAM,IAAI,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACzC,CAAC;SAAM,IAAI,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC;QAClC,IAAI,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACxC,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,GAAG,IAAI,SAAS,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACtC,CAAC;SAAM,IAAI,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAKD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAmB,EAAE,GAAG,IAAW;IACpE,KAAK,IAAI,KAAK,GAAG,CAAC,GAAI,KAAK,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,CAAC;YACV,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './pgwire_utils.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-index.js","sourceRoot":"","sources":["../../src/utils/utils-index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@powersync/lib-service-postgres",
|
|
3
|
+
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
|
+
"types": "dist/index.d.ts",
|
|
5
|
+
"version": "0.0.0-dev-20250116115804",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./types": {
|
|
19
|
+
"import": "./dist/types/types.js",
|
|
20
|
+
"require": "./dist/types/types.js",
|
|
21
|
+
"default": "./dist/types/types.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"p-defer": "^4.0.1",
|
|
26
|
+
"ts-codec": "^1.3.0",
|
|
27
|
+
"uri-js": "^4.4.1",
|
|
28
|
+
"uuid": "^9.0.1",
|
|
29
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20250116115804",
|
|
30
|
+
"@powersync/service-jpgwire": "0.0.0-dev-20250116115804",
|
|
31
|
+
"@powersync/service-types": "0.0.0-dev-20250116115804"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/uuid": "^9.0.4"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -b",
|
|
38
|
+
"build:tests": "tsc -b test/tsconfig.json",
|
|
39
|
+
"clean": "rm -rf ./dist && tsc -b --clean",
|
|
40
|
+
"test": "vitest"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as framework from '@powersync/lib-services-framework';
|
|
2
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
3
|
+
import * as t from 'ts-codec';
|
|
4
|
+
|
|
5
|
+
export type DecodedSQLQueryExecutor<T extends t.Codec<any, any>> = {
|
|
6
|
+
first: () => Promise<t.Decoded<T> | null>;
|
|
7
|
+
rows: () => Promise<t.Decoded<T>[]>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export abstract class AbstractPostgresConnection<
|
|
11
|
+
Listener extends framework.DisposableListener = framework.DisposableListener
|
|
12
|
+
> extends framework.DisposableObserver<Listener> {
|
|
13
|
+
protected abstract baseConnection: pgwire.PgClient;
|
|
14
|
+
|
|
15
|
+
stream(...args: pgwire.Statement[]): AsyncIterableIterator<pgwire.PgChunk> {
|
|
16
|
+
return this.baseConnection.stream(...args);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
query(script: string, options?: pgwire.PgSimpleQueryOptions): Promise<pgwire.PgResult>;
|
|
20
|
+
query(...args: pgwire.Statement[]): Promise<pgwire.PgResult>;
|
|
21
|
+
query(...args: any[]): Promise<pgwire.PgResult> {
|
|
22
|
+
return this.baseConnection.query(...args);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Template string helper which can be used to execute template SQL strings.
|
|
27
|
+
*/
|
|
28
|
+
sql(strings: TemplateStringsArray, ...params: pgwire.StatementParam[]) {
|
|
29
|
+
const { statement, params: queryParams } = sql(strings, ...params);
|
|
30
|
+
|
|
31
|
+
const rows = <T>(): Promise<T[]> =>
|
|
32
|
+
this.queryRows({
|
|
33
|
+
statement,
|
|
34
|
+
params: queryParams
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const first = async <T>(): Promise<T | null> => {
|
|
38
|
+
const [f] = await rows<T>();
|
|
39
|
+
return f;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
execute: () =>
|
|
44
|
+
this.query({
|
|
45
|
+
statement,
|
|
46
|
+
params
|
|
47
|
+
}),
|
|
48
|
+
rows,
|
|
49
|
+
first,
|
|
50
|
+
decoded: <T extends t.Codec<any, any>>(codec: T): DecodedSQLQueryExecutor<T> => {
|
|
51
|
+
return {
|
|
52
|
+
first: async () => {
|
|
53
|
+
const result = await first();
|
|
54
|
+
return result && codec.decode(result);
|
|
55
|
+
},
|
|
56
|
+
rows: async () => {
|
|
57
|
+
const results = await rows();
|
|
58
|
+
return results.map((r) => {
|
|
59
|
+
return codec.decode(r);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
queryRows<T>(script: string, options?: pgwire.PgSimpleQueryOptions): Promise<T[]>;
|
|
68
|
+
queryRows<T>(...args: pgwire.Statement[] | [...pgwire.Statement[], pgwire.PgExtendedQueryOptions]): Promise<T[]>;
|
|
69
|
+
async queryRows(...args: any[]) {
|
|
70
|
+
return pgwire.pgwireRows(await this.query(...args));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async *streamRows<T>(...args: pgwire.Statement[]): AsyncIterableIterator<T[]> {
|
|
74
|
+
let columns: Array<keyof T> = [];
|
|
75
|
+
|
|
76
|
+
for await (const chunk of this.stream(...args)) {
|
|
77
|
+
if (chunk.tag == 'RowDescription') {
|
|
78
|
+
columns = chunk.payload.map((c, index) => {
|
|
79
|
+
return c.name as keyof T;
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!chunk.rows.length) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
yield chunk.rows.map((row) => {
|
|
89
|
+
let q: Partial<T> = {};
|
|
90
|
+
for (const [index, c] of columns.entries()) {
|
|
91
|
+
q[c] = row[index];
|
|
92
|
+
}
|
|
93
|
+
return q as T;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Template string helper function which generates PGWire statements.
|
|
101
|
+
*/
|
|
102
|
+
export const sql = (strings: TemplateStringsArray, ...params: pgwire.StatementParam[]): pgwire.Statement => {
|
|
103
|
+
const paramPlaceholders = new Array(params.length).fill('').map((value, index) => `$${index + 1}`);
|
|
104
|
+
const joinedQueryStatement = strings.map((query, index) => `${query} ${paramPlaceholders[index] ?? ''}`).join(' ');
|
|
105
|
+
return {
|
|
106
|
+
statement: joinedQueryStatement,
|
|
107
|
+
params
|
|
108
|
+
};
|
|
109
|
+
};
|