@slonik/driver 43.0.6

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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Slonik Driver
2
+
3
+ Slonik driver is an abstraction layer that provides a consistent API for interacting using different PostgreSQL clients.
4
+
5
+ This package is made available for developers who wish to create their own Slonik driver.
@@ -0,0 +1,2 @@
1
+ export declare const Logger: import("roarr").Logger<import("roarr/dist/types").JsonObject>;
2
+ //# sourceMappingURL=Logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,+DAEjB,CAAC"}
package/dist/Logger.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Logger = void 0;
4
+ const roarr_1 = require("roarr");
5
+ exports.Logger = roarr_1.Roarr.child({
6
+ package: '@slonik/driver',
7
+ });
8
+ //# sourceMappingURL=Logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":";;;AAAA,iCAA8B;AAEjB,QAAA,MAAM,GAAG,aAAK,CAAC,KAAK,CAAC;IAChC,OAAO,EAAE,gBAAgB;CAC1B,CAAC,CAAC"}
@@ -0,0 +1,109 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ import { type Field } from '@slonik/types';
5
+ import EventEmitter from 'node:events';
6
+ import { type Readable } from 'node:stream';
7
+ import { type ConnectionOptions as TlsConnectionOptions } from 'node:tls';
8
+ import { type StrictEventEmitter } from 'strict-event-emitter-types';
9
+ type StreamDataEvent<T> = {
10
+ data: T;
11
+ fields: readonly Field[];
12
+ };
13
+ export interface DriverStream<T> extends Readable {
14
+ on(event: 'data', listener: (chunk: StreamDataEvent<T>) => void): this;
15
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
16
+ [Symbol.asyncIterator]: () => AsyncIterableIterator<StreamDataEvent<T>>;
17
+ }
18
+ type BasicConnection = {
19
+ readonly query: (query: string) => Promise<void>;
20
+ };
21
+ /**
22
+ * @property name Value of "pg_type"."typname" (e.g. "int8", "timestamp", "timestamptz").
23
+ */
24
+ export type DriverTypeParser<T = unknown> = {
25
+ readonly name: string;
26
+ readonly parse: (value: string) => T;
27
+ };
28
+ export type DriverConfiguration = {
29
+ readonly connectionTimeout: number | 'DISABLE_TIMEOUT';
30
+ readonly connectionUri: string;
31
+ readonly gracefulTerminationTimeout?: number;
32
+ readonly idleInTransactionSessionTimeout: number | 'DISABLE_TIMEOUT';
33
+ readonly idleTimeout?: number | 'DISABLE_TIMEOUT';
34
+ readonly maximumPoolSize?: number;
35
+ readonly resetConnection?: (connection: BasicConnection) => Promise<void>;
36
+ readonly ssl?: TlsConnectionOptions;
37
+ readonly statementTimeout: number | 'DISABLE_TIMEOUT';
38
+ readonly typeParsers: readonly DriverTypeParser[];
39
+ };
40
+ export type DriverNotice = {
41
+ message: string;
42
+ };
43
+ export type DriverEventEmitter = StrictEventEmitter<EventEmitter, {
44
+ error: (error: Error) => void;
45
+ }>;
46
+ export type DriverClientEventEmitter = StrictEventEmitter<EventEmitter, {
47
+ acquire: () => void;
48
+ destroy: () => void;
49
+ error: (error: Error) => void;
50
+ notice: (event: DriverNotice) => void;
51
+ release: () => void;
52
+ }>;
53
+ export type DriverClientState = 'ACQUIRED' | 'DESTROYED' | 'IDLE' | 'PENDING_DESTROY' | 'PENDING_RELEASE';
54
+ export type DriverClient = {
55
+ acquire: () => void;
56
+ destroy: () => Promise<void>;
57
+ id: () => string;
58
+ off: DriverClientEventEmitter['off'];
59
+ on: DriverClientEventEmitter['on'];
60
+ query: (query: string, values?: unknown[]) => Promise<DriverQueryResult>;
61
+ release: () => Promise<void>;
62
+ removeListener: DriverClientEventEmitter['removeListener'];
63
+ state: () => DriverClientState;
64
+ stream: (query: string, values?: unknown[]) => DriverStream<DriverStreamResult>;
65
+ };
66
+ export type Driver = {
67
+ createClient: () => Promise<DriverClient>;
68
+ };
69
+ export type DriverFactory = ({ driverConfiguration, }: {
70
+ driverConfiguration: DriverConfiguration;
71
+ }) => Promise<Driver>;
72
+ type DriverField = {
73
+ dataTypeId: number;
74
+ name: string;
75
+ };
76
+ export type DriverCommand = 'COPY' | 'DELETE' | 'INSERT' | 'SELECT' | 'UPDATE';
77
+ export type DriverQueryResult = {
78
+ readonly command: DriverCommand;
79
+ readonly fields: DriverField[];
80
+ readonly rowCount: number | null;
81
+ readonly rows: Array<Record<string, unknown>>;
82
+ };
83
+ export type DriverStreamResult = {
84
+ readonly fields: DriverField[];
85
+ readonly row: Record<string, unknown>;
86
+ };
87
+ type DriverSetup = ({ driverEventEmitter, driverConfiguration, }: {
88
+ driverConfiguration: DriverConfiguration;
89
+ driverEventEmitter: DriverEventEmitter;
90
+ }) => Promise<InternalPoolClientFactory>;
91
+ /**
92
+ * @property {Function} connect - Connect to the database. The client must not be used before this method is called.
93
+ * @property {Function} end - Disconnect from the database. The client must not be used after this method is called.
94
+ * @property {Function} query - Execute a SQL query.
95
+ */
96
+ type InternalPoolClient = {
97
+ connect: () => Promise<void>;
98
+ end: () => Promise<void>;
99
+ query: (query: string, values?: unknown[]) => Promise<DriverQueryResult>;
100
+ stream: (query: string, values?: unknown[]) => DriverStream<DriverStreamResult>;
101
+ };
102
+ type InternalPoolClientFactory = {
103
+ createPoolClient: ({ clientEventEmitter, }: {
104
+ clientEventEmitter: DriverClientEventEmitter;
105
+ }) => Promise<InternalPoolClient>;
106
+ };
107
+ export declare const createDriverFactory: (setup: DriverSetup) => DriverFactory;
108
+ export {};
109
+ //# sourceMappingURL=createDriverFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDriverFactory.d.ts","sourceRoot":"","sources":["../../src/factories/createDriverFactory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createDriverFactory = void 0;
7
+ const Logger_1 = require("../Logger");
8
+ const utilities_1 = require("@slonik/utilities");
9
+ const node_events_1 = __importDefault(require("node:events"));
10
+ const promises_1 = require("node:timers/promises");
11
+ const serialize_error_1 = require("serialize-error");
12
+ const createDriverFactory = (setup) => {
13
+ return async ({ driverConfiguration }) => {
14
+ const { resetConnection } = driverConfiguration;
15
+ const driverEventEmitter = new node_events_1.default();
16
+ const { createPoolClient } = await setup({
17
+ driverConfiguration,
18
+ driverEventEmitter,
19
+ });
20
+ return {
21
+ createClient: async () => {
22
+ const clientEventEmitter = new node_events_1.default();
23
+ // eslint-disable-next-line prefer-const
24
+ let destroy;
25
+ const onError = (error) => {
26
+ if (destroy) {
27
+ // eslint-disable-next-line promise/prefer-await-to-then
28
+ void destroy().catch(() => {
29
+ // Do nothing. The error has been emitted already.
30
+ // See "handles unexpected backend termination" test.
31
+ });
32
+ }
33
+ Logger_1.Logger.warn({
34
+ error: (0, serialize_error_1.serializeError)(error),
35
+ namespace: 'driverClient',
36
+ }, 'unhandled driver client error');
37
+ };
38
+ clientEventEmitter.on('error', onError);
39
+ const { query, stream, connect, end } = await createPoolClient({
40
+ clientEventEmitter,
41
+ });
42
+ let isAcquired = false;
43
+ let isDestroyed = false;
44
+ let idleTimeout = null;
45
+ let activeQueryPromise = null;
46
+ let destroyPromise = null;
47
+ let releasePromise = null;
48
+ const id = (0, utilities_1.generateUid)();
49
+ const clearIdleTimeout = () => {
50
+ if (idleTimeout) {
51
+ clearTimeout(idleTimeout);
52
+ idleTimeout = null;
53
+ }
54
+ };
55
+ const state = () => {
56
+ if (destroyPromise) {
57
+ return 'PENDING_DESTROY';
58
+ }
59
+ if (releasePromise) {
60
+ return 'PENDING_RELEASE';
61
+ }
62
+ if (isDestroyed) {
63
+ return 'DESTROYED';
64
+ }
65
+ if (isAcquired) {
66
+ return 'ACQUIRED';
67
+ }
68
+ return 'IDLE';
69
+ };
70
+ const internalDestroy = async () => {
71
+ const currentState = state();
72
+ if (currentState === 'PENDING_DESTROY') {
73
+ throw new Error('Client is pending destroy.');
74
+ }
75
+ if (currentState === 'DESTROYED') {
76
+ throw new Error('Client is destroyed.');
77
+ }
78
+ clearIdleTimeout();
79
+ if (activeQueryPromise) {
80
+ await Promise.race([
81
+ (0, promises_1.setTimeout)(driverConfiguration.gracefulTerminationTimeout),
82
+ activeQueryPromise,
83
+ ]);
84
+ }
85
+ if (releasePromise) {
86
+ await releasePromise;
87
+ }
88
+ isDestroyed = true;
89
+ clientEventEmitter.emit('destroy');
90
+ await end();
91
+ clientEventEmitter.off('error', onError);
92
+ };
93
+ destroy = async () => {
94
+ if (destroyPromise) {
95
+ return destroyPromise;
96
+ }
97
+ destroyPromise = internalDestroy();
98
+ return destroyPromise;
99
+ };
100
+ const internalRelease = async () => {
101
+ const currentState = state();
102
+ if (currentState === 'PENDING_DESTROY') {
103
+ throw new Error('Client is pending destroy.');
104
+ }
105
+ if (currentState === 'DESTROYED') {
106
+ throw new Error('Client is destroyed.');
107
+ }
108
+ if (currentState !== 'ACQUIRED') {
109
+ throw new Error('Client is not acquired.');
110
+ }
111
+ if (activeQueryPromise) {
112
+ throw new Error('Client has an active query.');
113
+ }
114
+ if (resetConnection) {
115
+ await resetConnection({
116
+ query: async (sql) => {
117
+ await query(sql);
118
+ },
119
+ });
120
+ }
121
+ if (driverConfiguration.idleTimeout !== 'DISABLE_TIMEOUT') {
122
+ clearIdleTimeout();
123
+ idleTimeout = setTimeout(() => {
124
+ void destroy();
125
+ idleTimeout = null;
126
+ }, driverConfiguration.idleTimeout).unref();
127
+ }
128
+ // eslint-disable-next-line require-atomic-updates
129
+ isAcquired = false;
130
+ releasePromise = null;
131
+ clientEventEmitter.emit('release');
132
+ };
133
+ const release = () => {
134
+ if (destroyPromise) {
135
+ return destroyPromise;
136
+ }
137
+ if (releasePromise) {
138
+ return releasePromise;
139
+ }
140
+ releasePromise = internalRelease();
141
+ return releasePromise;
142
+ };
143
+ const client = {
144
+ acquire: () => {
145
+ const currentState = state();
146
+ if (currentState === 'PENDING_DESTROY') {
147
+ throw new Error('Client is pending destroy.');
148
+ }
149
+ if (currentState === 'PENDING_RELEASE') {
150
+ throw new Error('Client is pending release.');
151
+ }
152
+ if (currentState === 'DESTROYED') {
153
+ throw new Error('Client is destroyed.');
154
+ }
155
+ if (currentState === 'ACQUIRED') {
156
+ throw new Error('Client is already acquired.');
157
+ }
158
+ clearIdleTimeout();
159
+ isAcquired = true;
160
+ clientEventEmitter.emit('acquire');
161
+ },
162
+ destroy,
163
+ id: () => id,
164
+ off: (event, listener) => {
165
+ return clientEventEmitter.off(event, listener);
166
+ },
167
+ on: (event, listener) => {
168
+ return clientEventEmitter.on(event, listener);
169
+ },
170
+ query: async (sql, values) => {
171
+ const currentState = state();
172
+ if (currentState === 'PENDING_DESTROY') {
173
+ throw new Error('Client is pending destroy.');
174
+ }
175
+ if (currentState === 'PENDING_RELEASE') {
176
+ throw new Error('Client is pending release.');
177
+ }
178
+ if (currentState === 'DESTROYED') {
179
+ throw new Error('Client is destroyed.');
180
+ }
181
+ if (currentState !== 'ACQUIRED') {
182
+ throw new Error('Client is not acquired.');
183
+ }
184
+ try {
185
+ activeQueryPromise = query(sql, values);
186
+ const result = await activeQueryPromise;
187
+ if (!activeQueryPromise) {
188
+ throw new Error('Expected `activeQueryPromise` to be set.');
189
+ }
190
+ return result;
191
+ }
192
+ finally {
193
+ // eslint-disable-next-line require-atomic-updates
194
+ activeQueryPromise = null;
195
+ }
196
+ },
197
+ release,
198
+ removeListener: (event, listener) => {
199
+ return clientEventEmitter.removeListener(event, listener);
200
+ },
201
+ state,
202
+ stream: (sql, values) => {
203
+ const currentState = state();
204
+ if (currentState === 'PENDING_DESTROY') {
205
+ throw new Error('Client is pending destroy.');
206
+ }
207
+ if (currentState === 'PENDING_RELEASE') {
208
+ throw new Error('Client is pending release.');
209
+ }
210
+ if (currentState === 'DESTROYED') {
211
+ throw new Error('Client is destroyed.');
212
+ }
213
+ if (currentState !== 'ACQUIRED') {
214
+ throw new Error('Client is not acquired.');
215
+ }
216
+ // TODO determine if streaming and do not allow to release the client until the stream is finished
217
+ return stream(sql, values);
218
+ },
219
+ };
220
+ await connect();
221
+ return client;
222
+ },
223
+ };
224
+ };
225
+ };
226
+ exports.createDriverFactory = createDriverFactory;
227
+ //# sourceMappingURL=createDriverFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDriverFactory.js","sourceRoot":"","sources":["../../src/factories/createDriverFactory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createDriverFactory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDriverFactory.test.d.ts","sourceRoot":"","sources":["../../src/factories/createDriverFactory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ava_1 = __importDefault(require("ava"));
7
+ (0, ava_1.default)('ok', (t) => {
8
+ t.pass();
9
+ });
10
+ //# sourceMappingURL=createDriverFactory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDriverFactory.test.js","sourceRoot":"","sources":["../../src/factories/createDriverFactory.test.ts"],"names":[],"mappings":";;;;;AAAA,8CAAuB;AAEvB,IAAA,aAAI,EAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;IACf,CAAC,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createDriverFactory, type Driver, type DriverClient, type DriverClientEventEmitter, type DriverClientState, type DriverCommand, type DriverConfiguration, type DriverEventEmitter, type DriverFactory, type DriverNotice, type DriverQueryResult, type DriverStream, type DriverStreamResult, type DriverTypeParser, } from './factories/createDriverFactory';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,iCAAiC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDriverFactory = void 0;
4
+ var createDriverFactory_1 = require("./factories/createDriverFactory");
5
+ Object.defineProperty(exports, "createDriverFactory", { enumerable: true, get: function () { return createDriverFactory_1.createDriverFactory; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uEAeyC;AAdvC,0HAAA,mBAAmB,OAAA"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "author": {
3
+ "email": "gajus@gajus.com",
4
+ "name": "Gajus Kuizinas",
5
+ "url": "http://gajus.com"
6
+ },
7
+ "ava": {
8
+ "extensions": [
9
+ "ts"
10
+ ],
11
+ "files": [
12
+ "src/**/*.test.ts"
13
+ ],
14
+ "require": [
15
+ "ts-node/register/transpile-only"
16
+ ]
17
+ },
18
+ "dependencies": {
19
+ "@slonik/utilities": "^43.0.6",
20
+ "@slonik/types": "^43.0.6",
21
+ "serialize-error": "^8.0.0",
22
+ "roarr": "^7.21.1",
23
+ "strict-event-emitter-types": "^2.0.0"
24
+ },
25
+ "description": "A Node.js PostgreSQL client with strict types, detailed logging and assertions.",
26
+ "devDependencies": {
27
+ "@types/node": "^18.15.3",
28
+ "ava": "^5.3.1",
29
+ "cspell": "^8.6.0",
30
+ "eslint": "^8.57.0",
31
+ "eslint-config-canonical": "^42.8.1",
32
+ "nyc": "^15.1.0",
33
+ "ts-node": "^10.9.1",
34
+ "typescript": "^5.4.3"
35
+ },
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "files": [
40
+ "./src",
41
+ "./dist"
42
+ ],
43
+ "keywords": [
44
+ "postgresql",
45
+ "promise",
46
+ "types"
47
+ ],
48
+ "license": "BSD-3-Clause",
49
+ "main": "./dist/index.js",
50
+ "exports": {
51
+ ".": {
52
+ "require": "./dist/index.js",
53
+ "types": "./dist/index.d.ts"
54
+ }
55
+ },
56
+ "name": "@slonik/driver",
57
+ "nyc": {
58
+ "all": true,
59
+ "exclude": [
60
+ "**/*.d.ts"
61
+ ],
62
+ "include": [
63
+ "src/**/*.ts"
64
+ ],
65
+ "reporter": [
66
+ "html",
67
+ "text-summary"
68
+ ],
69
+ "require": [
70
+ "ts-node/register/transpile-only"
71
+ ],
72
+ "silent": true,
73
+ "sourceMap": false
74
+ },
75
+ "peerDependencies": {
76
+ "zod": "^3"
77
+ },
78
+ "repository": {
79
+ "type": "git",
80
+ "url": "https://github.com/gajus/slonik"
81
+ },
82
+ "scripts": {
83
+ "build": "rm -fr ./dist && tsc --project ./tsconfig.json",
84
+ "lint": "npm run lint:cspell && npm run lint:eslint && npm run lint:tsc",
85
+ "lint:cspell": "cspell . --no-progress --gitignore",
86
+ "lint:eslint": "eslint --cache ./src",
87
+ "lint:tsc": "tsc --noEmit",
88
+ "test": "nyc ava --verbose --serial"
89
+ },
90
+ "types": "./dist/index.d.ts",
91
+ "version": "43.0.6"
92
+ }
package/src/Logger.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Roarr } from 'roarr';
2
+
3
+ export const Logger = Roarr.child({
4
+ package: '@slonik/driver',
5
+ });
@@ -0,0 +1,5 @@
1
+ import test from 'ava';
2
+
3
+ test('ok', (t) => {
4
+ t.pass();
5
+ });
@@ -0,0 +1,439 @@
1
+ import { Logger } from '../Logger';
2
+ import { type Field } from '@slonik/types';
3
+ import { generateUid } from '@slonik/utilities';
4
+ import EventEmitter from 'node:events';
5
+ import { type Readable } from 'node:stream';
6
+ import { setTimeout as delay } from 'node:timers/promises';
7
+ import { type ConnectionOptions as TlsConnectionOptions } from 'node:tls';
8
+ import { serializeError } from 'serialize-error';
9
+ import { type StrictEventEmitter } from 'strict-event-emitter-types';
10
+
11
+ type StreamDataEvent<T> = { data: T; fields: readonly Field[] };
12
+
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
14
+ export interface DriverStream<T> extends Readable {
15
+ // eslint-disable-next-line @typescript-eslint/method-signature-style
16
+ on(event: 'data', listener: (chunk: StreamDataEvent<T>) => void): this;
17
+ // eslint-disable-next-line @typescript-eslint/method-signature-style
18
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
19
+
20
+ [Symbol.asyncIterator]: () => AsyncIterableIterator<StreamDataEvent<T>>;
21
+ }
22
+
23
+ type BasicConnection = {
24
+ readonly query: (query: string) => Promise<void>;
25
+ };
26
+
27
+ /**
28
+ * @property name Value of "pg_type"."typname" (e.g. "int8", "timestamp", "timestamptz").
29
+ */
30
+ export type DriverTypeParser<T = unknown> = {
31
+ readonly name: string;
32
+ readonly parse: (value: string) => T;
33
+ };
34
+
35
+ export type DriverConfiguration = {
36
+ readonly connectionTimeout: number | 'DISABLE_TIMEOUT';
37
+ readonly connectionUri: string;
38
+ readonly gracefulTerminationTimeout?: number;
39
+ readonly idleInTransactionSessionTimeout: number | 'DISABLE_TIMEOUT';
40
+ readonly idleTimeout?: number | 'DISABLE_TIMEOUT';
41
+ readonly maximumPoolSize?: number;
42
+ readonly resetConnection?: (connection: BasicConnection) => Promise<void>;
43
+ readonly ssl?: TlsConnectionOptions;
44
+ readonly statementTimeout: number | 'DISABLE_TIMEOUT';
45
+ readonly typeParsers: readonly DriverTypeParser[];
46
+ };
47
+
48
+ export type DriverNotice = {
49
+ message: string;
50
+ };
51
+
52
+ export type DriverEventEmitter = StrictEventEmitter<
53
+ EventEmitter,
54
+ {
55
+ error: (error: Error) => void;
56
+ }
57
+ >;
58
+
59
+ export type DriverClientEventEmitter = StrictEventEmitter<
60
+ EventEmitter,
61
+ {
62
+ acquire: () => void;
63
+ destroy: () => void;
64
+ error: (error: Error) => void;
65
+ notice: (event: DriverNotice) => void;
66
+ release: () => void;
67
+ }
68
+ >;
69
+
70
+ export type DriverClientState =
71
+ | 'ACQUIRED'
72
+ | 'DESTROYED'
73
+ | 'IDLE'
74
+ | 'PENDING_DESTROY'
75
+ | 'PENDING_RELEASE';
76
+
77
+ export type DriverClient = {
78
+ acquire: () => void;
79
+ destroy: () => Promise<void>;
80
+ id: () => string;
81
+ off: DriverClientEventEmitter['off'];
82
+ on: DriverClientEventEmitter['on'];
83
+ query: (query: string, values?: unknown[]) => Promise<DriverQueryResult>;
84
+ release: () => Promise<void>;
85
+ removeListener: DriverClientEventEmitter['removeListener'];
86
+ state: () => DriverClientState;
87
+ stream: (
88
+ query: string,
89
+ values?: unknown[],
90
+ ) => DriverStream<DriverStreamResult>;
91
+ };
92
+
93
+ export type Driver = {
94
+ createClient: () => Promise<DriverClient>;
95
+ };
96
+
97
+ export type DriverFactory = ({
98
+ driverConfiguration,
99
+ }: {
100
+ driverConfiguration: DriverConfiguration;
101
+ }) => Promise<Driver>;
102
+
103
+ type DriverField = {
104
+ dataTypeId: number;
105
+ name: string;
106
+ };
107
+
108
+ export type DriverCommand = 'COPY' | 'DELETE' | 'INSERT' | 'SELECT' | 'UPDATE';
109
+
110
+ export type DriverQueryResult = {
111
+ readonly command: DriverCommand;
112
+ readonly fields: DriverField[];
113
+ readonly rowCount: number | null;
114
+ readonly rows: Array<Record<string, unknown>>;
115
+ };
116
+
117
+ export type DriverStreamResult = {
118
+ readonly fields: DriverField[];
119
+ readonly row: Record<string, unknown>;
120
+ };
121
+
122
+ type DriverSetup = ({
123
+ driverEventEmitter,
124
+ driverConfiguration,
125
+ }: {
126
+ driverConfiguration: DriverConfiguration;
127
+ driverEventEmitter: DriverEventEmitter;
128
+ }) => Promise<InternalPoolClientFactory>;
129
+
130
+ /**
131
+ * @property {Function} connect - Connect to the database. The client must not be used before this method is called.
132
+ * @property {Function} end - Disconnect from the database. The client must not be used after this method is called.
133
+ * @property {Function} query - Execute a SQL query.
134
+ */
135
+ type InternalPoolClient = {
136
+ connect: () => Promise<void>;
137
+ end: () => Promise<void>;
138
+ query: (query: string, values?: unknown[]) => Promise<DriverQueryResult>;
139
+ stream: (
140
+ query: string,
141
+ values?: unknown[],
142
+ ) => DriverStream<DriverStreamResult>;
143
+ };
144
+
145
+ type InternalPoolClientFactory = {
146
+ createPoolClient: ({
147
+ clientEventEmitter,
148
+ }: {
149
+ clientEventEmitter: DriverClientEventEmitter;
150
+ }) => Promise<InternalPoolClient>;
151
+ };
152
+
153
+ export const createDriverFactory = (setup: DriverSetup): DriverFactory => {
154
+ return async ({ driverConfiguration }): Promise<Driver> => {
155
+ const { resetConnection } = driverConfiguration;
156
+
157
+ const driverEventEmitter: DriverEventEmitter = new EventEmitter();
158
+
159
+ const { createPoolClient } = await setup({
160
+ driverConfiguration,
161
+ driverEventEmitter,
162
+ });
163
+
164
+ return {
165
+ createClient: async () => {
166
+ const clientEventEmitter: DriverClientEventEmitter = new EventEmitter();
167
+
168
+ // eslint-disable-next-line prefer-const
169
+ let destroy: () => Promise<void>;
170
+
171
+ const onError = (error: Error) => {
172
+ if (destroy) {
173
+ // eslint-disable-next-line promise/prefer-await-to-then
174
+ void destroy().catch(() => {
175
+ // Do nothing. The error has been emitted already.
176
+ // See "handles unexpected backend termination" test.
177
+ });
178
+ }
179
+
180
+ Logger.warn(
181
+ {
182
+ error: serializeError(error),
183
+ namespace: 'driverClient',
184
+ },
185
+ 'unhandled driver client error',
186
+ );
187
+ };
188
+
189
+ clientEventEmitter.on('error', onError);
190
+
191
+ const { query, stream, connect, end } = await createPoolClient({
192
+ clientEventEmitter,
193
+ });
194
+
195
+ let isAcquired = false;
196
+ let isDestroyed = false;
197
+ let idleTimeout: NodeJS.Timeout | null = null;
198
+
199
+ let activeQueryPromise: Promise<DriverQueryResult> | null = null;
200
+ let destroyPromise: Promise<void> | null = null;
201
+ let releasePromise: Promise<void> | null = null;
202
+
203
+ const id = generateUid();
204
+
205
+ const clearIdleTimeout = () => {
206
+ if (idleTimeout) {
207
+ clearTimeout(idleTimeout);
208
+
209
+ idleTimeout = null;
210
+ }
211
+ };
212
+
213
+ const state = () => {
214
+ if (destroyPromise) {
215
+ return 'PENDING_DESTROY';
216
+ }
217
+
218
+ if (releasePromise) {
219
+ return 'PENDING_RELEASE';
220
+ }
221
+
222
+ if (isDestroyed) {
223
+ return 'DESTROYED';
224
+ }
225
+
226
+ if (isAcquired) {
227
+ return 'ACQUIRED';
228
+ }
229
+
230
+ return 'IDLE';
231
+ };
232
+
233
+ const internalDestroy = async () => {
234
+ const currentState = state();
235
+
236
+ if (currentState === 'PENDING_DESTROY') {
237
+ throw new Error('Client is pending destroy.');
238
+ }
239
+
240
+ if (currentState === 'DESTROYED') {
241
+ throw new Error('Client is destroyed.');
242
+ }
243
+
244
+ clearIdleTimeout();
245
+
246
+ if (activeQueryPromise) {
247
+ await Promise.race([
248
+ delay(driverConfiguration.gracefulTerminationTimeout),
249
+ activeQueryPromise,
250
+ ]);
251
+ }
252
+
253
+ if (releasePromise) {
254
+ await releasePromise;
255
+ }
256
+
257
+ isDestroyed = true;
258
+
259
+ clientEventEmitter.emit('destroy');
260
+
261
+ await end();
262
+
263
+ clientEventEmitter.off('error', onError);
264
+ };
265
+
266
+ destroy = async () => {
267
+ if (destroyPromise) {
268
+ return destroyPromise;
269
+ }
270
+
271
+ destroyPromise = internalDestroy();
272
+
273
+ return destroyPromise;
274
+ };
275
+
276
+ const internalRelease = async () => {
277
+ const currentState = state();
278
+
279
+ if (currentState === 'PENDING_DESTROY') {
280
+ throw new Error('Client is pending destroy.');
281
+ }
282
+
283
+ if (currentState === 'DESTROYED') {
284
+ throw new Error('Client is destroyed.');
285
+ }
286
+
287
+ if (currentState !== 'ACQUIRED') {
288
+ throw new Error('Client is not acquired.');
289
+ }
290
+
291
+ if (activeQueryPromise) {
292
+ throw new Error('Client has an active query.');
293
+ }
294
+
295
+ if (resetConnection) {
296
+ await resetConnection({
297
+ query: async (sql) => {
298
+ await query(sql);
299
+ },
300
+ });
301
+ }
302
+
303
+ if (driverConfiguration.idleTimeout !== 'DISABLE_TIMEOUT') {
304
+ clearIdleTimeout();
305
+
306
+ idleTimeout = setTimeout(() => {
307
+ void destroy();
308
+
309
+ idleTimeout = null;
310
+ }, driverConfiguration.idleTimeout).unref();
311
+ }
312
+
313
+ // eslint-disable-next-line require-atomic-updates
314
+ isAcquired = false;
315
+
316
+ releasePromise = null;
317
+
318
+ clientEventEmitter.emit('release');
319
+ };
320
+
321
+ const release = () => {
322
+ if (destroyPromise) {
323
+ return destroyPromise;
324
+ }
325
+
326
+ if (releasePromise) {
327
+ return releasePromise;
328
+ }
329
+
330
+ releasePromise = internalRelease();
331
+
332
+ return releasePromise;
333
+ };
334
+
335
+ const client = {
336
+ acquire: () => {
337
+ const currentState = state();
338
+
339
+ if (currentState === 'PENDING_DESTROY') {
340
+ throw new Error('Client is pending destroy.');
341
+ }
342
+
343
+ if (currentState === 'PENDING_RELEASE') {
344
+ throw new Error('Client is pending release.');
345
+ }
346
+
347
+ if (currentState === 'DESTROYED') {
348
+ throw new Error('Client is destroyed.');
349
+ }
350
+
351
+ if (currentState === 'ACQUIRED') {
352
+ throw new Error('Client is already acquired.');
353
+ }
354
+
355
+ clearIdleTimeout();
356
+
357
+ isAcquired = true;
358
+
359
+ clientEventEmitter.emit('acquire');
360
+ },
361
+ destroy,
362
+ id: () => id,
363
+ off: (event, listener) => {
364
+ return clientEventEmitter.off(event, listener);
365
+ },
366
+ on: (event, listener) => {
367
+ return clientEventEmitter.on(event, listener);
368
+ },
369
+ query: async (sql, values) => {
370
+ const currentState = state();
371
+
372
+ if (currentState === 'PENDING_DESTROY') {
373
+ throw new Error('Client is pending destroy.');
374
+ }
375
+
376
+ if (currentState === 'PENDING_RELEASE') {
377
+ throw new Error('Client is pending release.');
378
+ }
379
+
380
+ if (currentState === 'DESTROYED') {
381
+ throw new Error('Client is destroyed.');
382
+ }
383
+
384
+ if (currentState !== 'ACQUIRED') {
385
+ throw new Error('Client is not acquired.');
386
+ }
387
+
388
+ try {
389
+ activeQueryPromise = query(sql, values);
390
+
391
+ const result = await activeQueryPromise;
392
+
393
+ if (!activeQueryPromise) {
394
+ throw new Error('Expected `activeQueryPromise` to be set.');
395
+ }
396
+
397
+ return result;
398
+ } finally {
399
+ // eslint-disable-next-line require-atomic-updates
400
+ activeQueryPromise = null;
401
+ }
402
+ },
403
+ release,
404
+ removeListener: (event, listener) => {
405
+ return clientEventEmitter.removeListener(event, listener);
406
+ },
407
+ state,
408
+ stream: (sql, values) => {
409
+ const currentState = state();
410
+
411
+ if (currentState === 'PENDING_DESTROY') {
412
+ throw new Error('Client is pending destroy.');
413
+ }
414
+
415
+ if (currentState === 'PENDING_RELEASE') {
416
+ throw new Error('Client is pending release.');
417
+ }
418
+
419
+ if (currentState === 'DESTROYED') {
420
+ throw new Error('Client is destroyed.');
421
+ }
422
+
423
+ if (currentState !== 'ACQUIRED') {
424
+ throw new Error('Client is not acquired.');
425
+ }
426
+
427
+ // TODO determine if streaming and do not allow to release the client until the stream is finished
428
+
429
+ return stream(sql, values);
430
+ },
431
+ };
432
+
433
+ await connect();
434
+
435
+ return client;
436
+ },
437
+ };
438
+ };
439
+ };
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export {
2
+ createDriverFactory,
3
+ type Driver,
4
+ type DriverClient,
5
+ type DriverClientEventEmitter,
6
+ type DriverClientState,
7
+ type DriverCommand,
8
+ type DriverConfiguration,
9
+ type DriverEventEmitter,
10
+ type DriverFactory,
11
+ type DriverNotice,
12
+ type DriverQueryResult,
13
+ type DriverStream,
14
+ type DriverStreamResult,
15
+ type DriverTypeParser,
16
+ } from './factories/createDriverFactory';