Skip to content

Commit ad92104

Browse files
committed
feat: provide bridge as a class
1 parent 1b0a8de commit ad92104

File tree

3 files changed

+115
-98
lines changed

3 files changed

+115
-98
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Wraps [`postgres`](https://www.npmjs.com/package/postgres) API in a [`pg`](https
99

1010
```ts
1111
import postgres from 'postgres';
12-
import { bridge } from 'postgres-bridge';
12+
import { PostgresBridge } from 'postgres-bridge';
1313

1414
// pg.Pool Configuration
1515
const configuration = {
@@ -20,7 +20,7 @@ const configuration = {
2020
connectionTimeoutMillis: 2000,
2121
};
2222

23-
const pool = bridge(postgres, configuration);
23+
const pool = new PostgresBridge(postgres, configuration);
2424

2525
const connection = await pool.connect();
2626

@@ -42,6 +42,10 @@ Supported features:
4242
* `connect` event
4343
* `notice` event
4444

45+
Known incompatibilities:
46+
47+
* `connection.processID` not implemented
48+
4549
Please submit PR if you require additional compatibility.
4650

4751
## Development

src/bridge.ts

Lines changed: 107 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {
22
EventEmitter,
33
} from 'node:events';
4-
import genericPool from 'generic-pool';
4+
import genericPool, {
5+
type Pool as GenericPool,
6+
} from 'generic-pool';
57
import type Postgres from 'postgres';
68
import {
79
type Sql,
@@ -36,96 +38,107 @@ type QueryResult = {
3638
rows: Row[],
3739
};
3840

39-
export const bridge = (postgres: typeof Postgres, poolConfiguration: PgPool) => {
40-
const poolEvents = new EventEmitter();
41-
42-
const pool = genericPool.createPool<AnySql & {events: EventEmitter, }>({
43-
create: async () => {
44-
const connectionEvents = new EventEmitter();
45-
46-
const connection = postgres({
47-
database: poolConfiguration.database,
48-
host: poolConfiguration.host ?? 'localhost',
49-
idle_timeout: poolConfiguration.idleTimeoutMillis ? poolConfiguration.idleTimeoutMillis / 1_000 : 0,
50-
max: 1,
51-
onnotice: (notice) => {
52-
connectionEvents.emit('notice', {
53-
code: notice.code,
54-
file: notice.file,
55-
line: notice.line,
56-
message: notice.message,
57-
routine: notice.routine,
58-
severity: notice.severity,
59-
where: notice.where,
60-
});
61-
},
62-
password: poolConfiguration.password,
63-
port: poolConfiguration.port ?? 5_432,
64-
ssl: poolConfiguration.ssl,
65-
username: poolConfiguration.user,
66-
}) as AnySql & {events: EventEmitter, };
67-
68-
connection.events = connectionEvents;
69-
70-
return connection;
71-
},
72-
destroy: (client: Sql<{}>) => {
73-
return client.end({
74-
timeout: 5,
75-
});
76-
},
77-
}, {
78-
max: poolConfiguration.max ?? 10,
79-
min: poolConfiguration.min ?? 0,
80-
});
81-
82-
const compatiblePool = {
83-
connect: async () => {
84-
const connection = await pool.acquire();
85-
86-
const compatibleConnection = {
87-
end: async () => {
88-
await pool.destroy(connection);
89-
},
90-
off: connection.events.off.bind(connection.events),
91-
on: connection.events.on.bind(connection.events),
92-
query: async (sql: string): Promise<QueryResult> => {
93-
// https://github.com/porsager/postgres#result-array
94-
const resultArray = await connection.unsafe(sql);
95-
96-
return {
97-
command: resultArray.command as Command,
98-
fields: resultArray.columns?.map((column) => {
99-
return {
100-
dataTypeID: column.type,
101-
name: column.name,
102-
};
103-
}) ?? [],
104-
rowCount: resultArray.count,
105-
rows: Array.from(resultArray),
106-
};
107-
},
108-
release: async () => {
109-
await pool.release(connection);
110-
},
111-
};
112-
113-
poolEvents.emit('connect', compatibleConnection);
114-
115-
return compatibleConnection;
116-
},
117-
get idleCount () {
118-
return pool.available;
119-
},
120-
off: poolEvents.off.bind(poolEvents),
121-
on: poolEvents.on.bind(poolEvents),
122-
get totalCount () {
123-
return pool.size;
124-
},
125-
get waitingCount () {
126-
return pool.pending;
127-
},
128-
};
129-
130-
return compatiblePool;
131-
};
41+
export class PostgresBridge {
42+
private readonly poolEvents: EventEmitter;
43+
44+
private readonly pool: GenericPool<AnySql & {events: EventEmitter, }>;
45+
46+
public constructor (postgres: typeof Postgres, poolConfiguration: PgPool) {
47+
this.poolEvents = new EventEmitter();
48+
49+
this.pool = genericPool.createPool<AnySql & {events: EventEmitter, }>({
50+
create: async () => {
51+
const connectionEvents = new EventEmitter();
52+
53+
const connection = postgres({
54+
database: poolConfiguration.database,
55+
host: poolConfiguration.host ?? 'localhost',
56+
idle_timeout: poolConfiguration.idleTimeoutMillis ? poolConfiguration.idleTimeoutMillis / 1_000 : 0,
57+
max: 1,
58+
onnotice: (notice) => {
59+
connectionEvents.emit('notice', {
60+
code: notice.code,
61+
file: notice.file,
62+
line: notice.line,
63+
message: notice.message,
64+
routine: notice.routine,
65+
severity: notice.severity,
66+
where: notice.where,
67+
});
68+
},
69+
password: poolConfiguration.password,
70+
port: poolConfiguration.port ?? 5_432,
71+
ssl: poolConfiguration.ssl,
72+
username: poolConfiguration.user,
73+
}) as AnySql & {events: EventEmitter, };
74+
75+
connection.events = connectionEvents;
76+
77+
return connection;
78+
},
79+
destroy: (client: Sql<{}>) => {
80+
return client.end({
81+
timeout: 5,
82+
});
83+
},
84+
}, {
85+
max: poolConfiguration.max ?? 10,
86+
min: poolConfiguration.min ?? 0,
87+
});
88+
}
89+
90+
public async connect () {
91+
const connection = await this.pool.acquire();
92+
93+
const compatibleConnection = {
94+
end: async () => {
95+
await this.pool.destroy(connection);
96+
},
97+
off: connection.events.off.bind(connection.events),
98+
on: connection.events.on.bind(connection.events),
99+
query: async (sql: string): Promise<QueryResult> => {
100+
// https://github.com/porsager/postgres#result-array
101+
const resultArray = await connection.unsafe(sql);
102+
103+
return {
104+
command: resultArray.command as Command,
105+
fields: resultArray.columns?.map((column) => {
106+
return {
107+
dataTypeID: column.type,
108+
name: column.name,
109+
};
110+
}) ?? [],
111+
rowCount: resultArray.count,
112+
rows: Array.from(resultArray),
113+
};
114+
},
115+
release: async () => {
116+
await this.pool.release(connection);
117+
},
118+
};
119+
120+
this.poolEvents.emit('connect', compatibleConnection);
121+
122+
return compatibleConnection;
123+
}
124+
125+
public get idleCount () {
126+
return this.pool.available;
127+
}
128+
129+
public off (eventName: string, listener: (...args: any[]) => void) {
130+
return this.poolEvents.off(eventName, listener);
131+
}
132+
133+
public on (eventName: string, listener: (...args: any[]) => void) {
134+
return this.poolEvents.on(eventName, listener);
135+
}
136+
137+
public get totalCount () {
138+
return this.pool.size;
139+
}
140+
141+
public get waitingCount () {
142+
return this.pool.pending;
143+
}
144+
}

test/postgres-bridge/bridge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
import postgres from 'postgres';
66
import * as sinon from 'sinon';
77
import {
8-
bridge,
8+
PostgresBridge,
99
} from '../../src/bridge';
1010

1111
const clients = [
@@ -18,7 +18,7 @@ const createPool = (clientName: string, poolConfiguration) => {
1818
return new Pool(poolConfiguration);
1919
}
2020

21-
return bridge(postgres, {
21+
return new PostgresBridge(postgres, {
2222
...poolConfiguration,
2323
});
2424
};

0 commit comments

Comments
 (0)