@devmoods/express-extras 0.40.0 → 0.41.0
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/dist/postgres.d.ts +28 -21
- package/dist/postgres.js +34 -16
- package/package.json +11 -10
package/dist/postgres.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import pg, {
|
|
2
|
-
export
|
|
1
|
+
import pg, { type PoolClient, type PoolConfig, type QueryResult, type QueryResultRow } from 'pg';
|
|
2
|
+
export declare const escapePostgresIdentifier: typeof pg.escapeIdentifier;
|
|
3
|
+
export declare const escapePostgresLiteral: typeof pg.escapeLiteral;
|
|
4
|
+
export declare const formatSql: (statement: SqlStatement | string) => string;
|
|
5
|
+
export interface PostgresOptions extends PoolConfig, ConnectionOptions {
|
|
3
6
|
onError?: (error: Error, client: any) => void;
|
|
4
7
|
url?: PoolConfig['connectionString'];
|
|
5
8
|
}
|
|
@@ -13,14 +16,18 @@ export declare class IntegrityError extends Error {
|
|
|
13
16
|
constructor(message: string);
|
|
14
17
|
}
|
|
15
18
|
type Value = null | string | number | Date;
|
|
19
|
+
export interface ConnectionOptions {
|
|
20
|
+
onLogStatement?: (statement: SqlStatement | string, values?: unknown[]) => void;
|
|
21
|
+
}
|
|
16
22
|
export declare class Connection {
|
|
23
|
+
private options;
|
|
17
24
|
client: PoolClient;
|
|
18
|
-
constructor(client: PoolClient);
|
|
19
|
-
query<T extends QueryResultRow>(text: string |
|
|
20
|
-
all<T>(text: string |
|
|
21
|
-
insert<T extends QueryResultRow>(text: string |
|
|
22
|
-
first<T>(text: string |
|
|
23
|
-
get<T>(text: string |
|
|
25
|
+
constructor(client: PoolClient, options: ConnectionOptions);
|
|
26
|
+
query<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
|
|
27
|
+
all<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T[]>;
|
|
28
|
+
insert<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
|
|
29
|
+
first<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T | null>;
|
|
30
|
+
get<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T>;
|
|
24
31
|
}
|
|
25
32
|
export declare class Transaction extends Connection {
|
|
26
33
|
}
|
|
@@ -32,27 +39,27 @@ export declare class Transaction extends Connection {
|
|
|
32
39
|
* @param config
|
|
33
40
|
*/
|
|
34
41
|
export declare function createPostgres({ onError, url, ...config }?: PostgresOptions): {
|
|
35
|
-
insert: <T extends QueryResultRow>(text: string |
|
|
36
|
-
query: <T extends QueryResultRow>(text: string |
|
|
37
|
-
first: <T>(text: string |
|
|
38
|
-
all: <T>(text: string |
|
|
39
|
-
get: <T>(text: string |
|
|
42
|
+
insert: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
|
|
43
|
+
query: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
|
|
44
|
+
first: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T | null>;
|
|
45
|
+
all: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T[]>;
|
|
46
|
+
get: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T>;
|
|
40
47
|
withConnection: <T>(callback: (connection: Connection) => Promise<T>) => Promise<T>;
|
|
41
48
|
transaction: <T>(callback: (connection: Transaction) => Promise<T> | T) => Promise<T>;
|
|
42
49
|
instance: pg.Pool;
|
|
43
50
|
clear: (force?: boolean) => Promise<void>;
|
|
44
51
|
end(): Promise<void>;
|
|
45
52
|
};
|
|
46
|
-
declare class
|
|
53
|
+
export declare class SqlStatement {
|
|
47
54
|
stringsAndValues: [string, unknown | undefined][];
|
|
48
55
|
constructor(strings: string[], values: unknown[]);
|
|
49
56
|
_extract(strings: string[], values: unknown[]): void;
|
|
50
|
-
append(statement:
|
|
57
|
+
append(statement: SqlStatement | string): this;
|
|
51
58
|
get values(): ({} | null)[];
|
|
52
59
|
get text(): string;
|
|
53
60
|
}
|
|
54
61
|
export declare const sql: {
|
|
55
|
-
(strings: TemplateStringsArray, ...values: unknown[]):
|
|
62
|
+
(strings: TemplateStringsArray, ...values: unknown[]): SqlStatement;
|
|
56
63
|
identifier(name: string): {
|
|
57
64
|
name: string;
|
|
58
65
|
raw: boolean;
|
|
@@ -61,14 +68,14 @@ export declare const sql: {
|
|
|
61
68
|
name: string;
|
|
62
69
|
raw: boolean;
|
|
63
70
|
};
|
|
64
|
-
literal(name: string): {
|
|
71
|
+
literal(name: string | null): {
|
|
65
72
|
name: string;
|
|
66
73
|
raw: boolean;
|
|
67
74
|
};
|
|
68
75
|
spreadUpdate: typeof spreadUpdate;
|
|
69
76
|
spreadInsert: typeof spreadInsert;
|
|
70
77
|
};
|
|
71
|
-
declare function spreadUpdate(data: Record<string, any>):
|
|
72
|
-
declare function spreadInsert<TData extends Record<string, any>>(data: TData | TData[]):
|
|
73
|
-
export declare function joinStatements(statements:
|
|
74
|
-
export {
|
|
78
|
+
declare function spreadUpdate(data: Record<string, any>): SqlStatement;
|
|
79
|
+
declare function spreadInsert<TData extends Record<string, any>>(data: TData | TData[]): SqlStatement;
|
|
80
|
+
export declare function joinStatements(statements: SqlStatement[], separator: string): SqlStatement;
|
|
81
|
+
export {};
|
package/dist/postgres.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import pg, {
|
|
1
|
+
import pg, {} from 'pg';
|
|
2
|
+
import { format } from 'sql-formatter';
|
|
2
3
|
import { readEnv } from './utils.js';
|
|
4
|
+
export const escapePostgresIdentifier = pg.escapeIdentifier;
|
|
5
|
+
export const escapePostgresLiteral = pg.escapeLiteral;
|
|
6
|
+
export const formatSql = (statement) => {
|
|
7
|
+
const value = statement instanceof SqlStatement ? statement.text : statement;
|
|
8
|
+
return format(value, {
|
|
9
|
+
language: 'postgresql',
|
|
10
|
+
keywordCase: 'upper',
|
|
11
|
+
tabWidth: 2,
|
|
12
|
+
useTabs: false,
|
|
13
|
+
});
|
|
14
|
+
};
|
|
3
15
|
export class DoesNotExistError extends Error {
|
|
4
16
|
constructor(message) {
|
|
5
17
|
super(message);
|
|
@@ -19,11 +31,13 @@ export class IntegrityError extends Error {
|
|
|
19
31
|
}
|
|
20
32
|
}
|
|
21
33
|
export class Connection {
|
|
22
|
-
constructor(client) {
|
|
34
|
+
constructor(client, options) {
|
|
35
|
+
this.options = options;
|
|
23
36
|
this.client = client;
|
|
24
37
|
}
|
|
25
38
|
async query(text, values) {
|
|
26
39
|
try {
|
|
40
|
+
this.options.onLogStatement?.(text, values);
|
|
27
41
|
return await this.client.query(text, values);
|
|
28
42
|
}
|
|
29
43
|
catch (error) {
|
|
@@ -34,7 +48,7 @@ export class Connection {
|
|
|
34
48
|
}
|
|
35
49
|
}
|
|
36
50
|
async all(text, values) {
|
|
37
|
-
const result = await this.
|
|
51
|
+
const result = await this.query(text, values);
|
|
38
52
|
if (result.rows) {
|
|
39
53
|
return result.rows;
|
|
40
54
|
/* c8 ignore next 2*/
|
|
@@ -45,11 +59,11 @@ export class Connection {
|
|
|
45
59
|
return await this.query(text, values);
|
|
46
60
|
}
|
|
47
61
|
async first(text, values) {
|
|
48
|
-
const result = await this.
|
|
62
|
+
const result = await this.query(text, values);
|
|
49
63
|
return result.rows && result.rows.length ? result.rows[0] : null;
|
|
50
64
|
}
|
|
51
65
|
async get(text, values) {
|
|
52
|
-
const result = await this.
|
|
66
|
+
const result = await this.query(text, values);
|
|
53
67
|
if (result.rowCount === 0) {
|
|
54
68
|
throw new DoesNotExistError('Row matching query not found');
|
|
55
69
|
}
|
|
@@ -76,7 +90,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
76
90
|
async function withConnection(callback) {
|
|
77
91
|
const client = await pool.connect();
|
|
78
92
|
try {
|
|
79
|
-
const result = await callback(new Connection(client));
|
|
93
|
+
const result = await callback(new Connection(client, { onLogStatement: config.onLogStatement }));
|
|
80
94
|
return result;
|
|
81
95
|
}
|
|
82
96
|
finally {
|
|
@@ -102,7 +116,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
102
116
|
const client = await pool.connect();
|
|
103
117
|
try {
|
|
104
118
|
await client.query('BEGIN');
|
|
105
|
-
const result = await callback(new Transaction(client));
|
|
119
|
+
const result = await callback(new Transaction(client, { onLogStatement: config.onLogStatement }));
|
|
106
120
|
await client.query('COMMIT');
|
|
107
121
|
return result;
|
|
108
122
|
}
|
|
@@ -158,7 +172,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
158
172
|
const isRaw = (value) => {
|
|
159
173
|
return (typeof value === 'object' && value !== null && 'raw' in value && !!value.raw);
|
|
160
174
|
};
|
|
161
|
-
class
|
|
175
|
+
export class SqlStatement {
|
|
162
176
|
constructor(strings, values) {
|
|
163
177
|
this.stringsAndValues = [];
|
|
164
178
|
this._extract(strings, values);
|
|
@@ -170,7 +184,7 @@ class SQLStatement {
|
|
|
170
184
|
if (isRaw(value)) {
|
|
171
185
|
this.stringsAndValues.push([string + value.name, undefined]);
|
|
172
186
|
}
|
|
173
|
-
else if (value instanceof
|
|
187
|
+
else if (value instanceof SqlStatement) {
|
|
174
188
|
this.stringsAndValues.push([string, undefined]);
|
|
175
189
|
this.stringsAndValues.push(...value.stringsAndValues);
|
|
176
190
|
}
|
|
@@ -180,7 +194,7 @@ class SQLStatement {
|
|
|
180
194
|
}
|
|
181
195
|
}
|
|
182
196
|
append(statement) {
|
|
183
|
-
if (statement instanceof
|
|
197
|
+
if (statement instanceof SqlStatement) {
|
|
184
198
|
this.stringsAndValues.push(...statement.stringsAndValues);
|
|
185
199
|
}
|
|
186
200
|
else {
|
|
@@ -209,14 +223,16 @@ class SQLStatement {
|
|
|
209
223
|
}
|
|
210
224
|
}
|
|
211
225
|
export const sql = (strings, ...values) => {
|
|
212
|
-
return new
|
|
226
|
+
return new SqlStatement(Array.from(strings), values);
|
|
213
227
|
};
|
|
214
228
|
function spreadUpdate(data) {
|
|
215
229
|
const entries = Object.entries(data);
|
|
216
230
|
if (entries.length === 0) {
|
|
217
231
|
return sql ``;
|
|
218
232
|
}
|
|
219
|
-
return joinStatements(entries
|
|
233
|
+
return joinStatements(entries
|
|
234
|
+
.filter(([, value]) => typeof value !== 'undefined')
|
|
235
|
+
.map(([key, value]) => sql `${sql.identifier(key)} = ${value == null ? sql.raw('NULL') : value}`), ', ');
|
|
220
236
|
}
|
|
221
237
|
function spreadInsert(data) {
|
|
222
238
|
const rows = Array.isArray(data) ? data : [data];
|
|
@@ -225,7 +241,7 @@ function spreadInsert(data) {
|
|
|
225
241
|
}
|
|
226
242
|
const statement = sql `(`;
|
|
227
243
|
statement.append(Object.keys(rows[0])
|
|
228
|
-
.map((k) =>
|
|
244
|
+
.map((k) => escapePostgresIdentifier(k))
|
|
229
245
|
.join(', '));
|
|
230
246
|
statement.append(sql `) VALUES `);
|
|
231
247
|
const toInsert = [];
|
|
@@ -243,7 +259,7 @@ function spreadInsert(data) {
|
|
|
243
259
|
* Inject an identifier into the template string
|
|
244
260
|
*/
|
|
245
261
|
sql.identifier = (name) => {
|
|
246
|
-
return { name:
|
|
262
|
+
return { name: escapePostgresIdentifier(name), raw: true };
|
|
247
263
|
};
|
|
248
264
|
/**
|
|
249
265
|
* Inject a raw value into the template string
|
|
@@ -255,7 +271,10 @@ sql.raw = (name) => {
|
|
|
255
271
|
* Inject a literal value into the template string
|
|
256
272
|
*/
|
|
257
273
|
sql.literal = (name) => {
|
|
258
|
-
|
|
274
|
+
if (name === null) {
|
|
275
|
+
return { name: 'NULL', raw: true };
|
|
276
|
+
}
|
|
277
|
+
return { name: escapePostgresLiteral(name), raw: true };
|
|
259
278
|
};
|
|
260
279
|
sql.spreadUpdate = spreadUpdate;
|
|
261
280
|
sql.spreadInsert = spreadInsert;
|
|
@@ -270,4 +289,3 @@ export function joinStatements(statements, separator) {
|
|
|
270
289
|
}
|
|
271
290
|
return statement;
|
|
272
291
|
}
|
|
273
|
-
export { escapeIdentifier, escapeLiteral };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,31 +20,31 @@
|
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@devmoods/eslint-config": "^1.15.0",
|
|
22
22
|
"@types/koa-compose": "^3.2.8",
|
|
23
|
-
"@types/node": "22.
|
|
23
|
+
"@types/node": "22.10.1",
|
|
24
24
|
"@types/supertest": "^6.0.2",
|
|
25
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
26
|
-
"@typescript-eslint/parser": "^8.
|
|
27
|
-
"@vitest/coverage-v8": "^2.1.
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
26
|
+
"@typescript-eslint/parser": "^8.16.0",
|
|
27
|
+
"@vitest/coverage-v8": "^2.1.6",
|
|
28
28
|
"cookiejar": "^2.1.4",
|
|
29
29
|
"eslint": "^8.57.0",
|
|
30
30
|
"eslint-plugin-import": "^2.31.0",
|
|
31
31
|
"eslint-plugin-prettier": "^5.2.1",
|
|
32
32
|
"faktory-worker": "^4.6.0",
|
|
33
|
-
"prettier": "3.
|
|
33
|
+
"prettier": "3.4.1",
|
|
34
34
|
"supertest": "7.0.0",
|
|
35
35
|
"ts-node": "10.9.2",
|
|
36
36
|
"tsx": "^4.19.2",
|
|
37
37
|
"typescript": "5.7.2",
|
|
38
|
-
"vitest": "^2.1.
|
|
38
|
+
"vitest": "^2.1.6",
|
|
39
39
|
"vitest-fail-on-console": "^0.7.1"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@devmoods/fetch": "^4.0.1",
|
|
43
|
-
"@sentry/node": "^8.
|
|
44
|
-
"@sentry/profiling-node": "^8.
|
|
43
|
+
"@sentry/node": "^8.41.0",
|
|
44
|
+
"@sentry/profiling-node": "^8.41.0",
|
|
45
45
|
"@types/bcrypt": "^5.0.2",
|
|
46
46
|
"@types/body-parser": "^1.19.5",
|
|
47
|
-
"@types/cookie-parser": "^1.4.
|
|
47
|
+
"@types/cookie-parser": "^1.4.8",
|
|
48
48
|
"@types/express": "^5.0.0",
|
|
49
49
|
"@types/jsonwebtoken": "^9.0.7",
|
|
50
50
|
"@types/nodemailer": "^6.4.17",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"nodemailer": "^6.9.16",
|
|
66
66
|
"pg": "^8.13.1",
|
|
67
67
|
"redis": "^4.7.0",
|
|
68
|
+
"sql-formatter": "^15.4.6",
|
|
68
69
|
"uuid": "^11.0.3"
|
|
69
70
|
},
|
|
70
71
|
"prettier": {
|