@devmoods/express-extras 0.39.1 → 0.40.1
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/middleware.d.ts +5 -1
- package/dist/postgres.d.ts +30 -2
- package/dist/postgres.js +117 -2
- package/package.json +11 -12
package/dist/middleware.d.ts
CHANGED
|
@@ -35,7 +35,11 @@ export declare function notFound(): RequestHandler;
|
|
|
35
35
|
* Register middlewares that must come after the app routes.
|
|
36
36
|
*/
|
|
37
37
|
export declare function after(): RequestHandler;
|
|
38
|
+
type ResponseHeaders = Record<string, string>;
|
|
39
|
+
type AnyResponseBody = Record<string, any> | string;
|
|
40
|
+
type HttpResponseCode = number;
|
|
41
|
+
type ResponseWrapper<TResponseBody extends AnyResponseBody> = TResponseBody | HttpResponseCode | [HttpResponseCode, TResponseBody] | [HttpResponseCode, TResponseBody, ResponseHeaders];
|
|
38
42
|
/**
|
|
39
43
|
* Alias of `route()`
|
|
40
44
|
*/
|
|
41
|
-
export declare function asyncMiddleware<RequestBody, RequestQuery = object, ResponseBody =
|
|
45
|
+
export declare function asyncMiddleware<RequestBody, RequestQuery = object, ResponseBody extends AnyResponseBody = object>(fn: (req: Request<Record<string, string>, ResponseBody, RequestBody, RequestQuery, object>, res: Response<ResponseBody, object>, next: NextFunction) => ResponseWrapper<ResponseBody> | Promise<ResponseWrapper<ResponseBody>>): RequestHandler<Record<string, string>, ResponseBody, RequestBody, RequestQuery>;
|
package/dist/postgres.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pg, { type PoolClient, type PoolConfig, type QueryResult, type QueryResultRow } from 'pg';
|
|
2
|
-
|
|
2
|
+
export declare const escapePostgresIdentifier: typeof pg.escapeIdentifier;
|
|
3
|
+
export declare const escapePostgresLiteral: typeof pg.escapeLiteral;
|
|
3
4
|
export interface PostgresOptions extends PoolConfig {
|
|
4
5
|
onError?: (error: Error, client: any) => void;
|
|
5
6
|
url?: PoolConfig['connectionString'];
|
|
@@ -44,5 +45,32 @@ export declare function createPostgres({ onError, url, ...config }?: PostgresOpt
|
|
|
44
45
|
clear: (force?: boolean) => Promise<void>;
|
|
45
46
|
end(): Promise<void>;
|
|
46
47
|
};
|
|
47
|
-
|
|
48
|
+
declare class SQLStatement {
|
|
49
|
+
stringsAndValues: [string, unknown | undefined][];
|
|
50
|
+
constructor(strings: string[], values: unknown[]);
|
|
51
|
+
_extract(strings: string[], values: unknown[]): void;
|
|
52
|
+
append(statement: SQLStatement | string): this;
|
|
53
|
+
get values(): ({} | null)[];
|
|
54
|
+
get text(): string;
|
|
55
|
+
}
|
|
56
|
+
export declare const sql: {
|
|
57
|
+
(strings: TemplateStringsArray, ...values: unknown[]): SQLStatement;
|
|
58
|
+
identifier(name: string): {
|
|
59
|
+
name: string;
|
|
60
|
+
raw: boolean;
|
|
61
|
+
};
|
|
62
|
+
raw(name: string): {
|
|
63
|
+
name: string;
|
|
64
|
+
raw: boolean;
|
|
65
|
+
};
|
|
66
|
+
literal(name: string): {
|
|
67
|
+
name: string;
|
|
68
|
+
raw: boolean;
|
|
69
|
+
};
|
|
70
|
+
spreadUpdate: typeof spreadUpdate;
|
|
71
|
+
spreadInsert: typeof spreadInsert;
|
|
72
|
+
};
|
|
73
|
+
declare function spreadUpdate(data: Record<string, any>): SQLStatement;
|
|
74
|
+
declare function spreadInsert<TData extends Record<string, any>>(data: TData | TData[]): SQLStatement;
|
|
75
|
+
export declare function joinStatements(statements: SQLStatement[], separator: string): SQLStatement;
|
|
48
76
|
export {};
|
package/dist/postgres.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pg, {} from 'pg';
|
|
2
|
-
import { SQL } from 'sql-template-strings';
|
|
3
2
|
import { readEnv } from './utils.js';
|
|
3
|
+
export const escapePostgresIdentifier = pg.escapeIdentifier;
|
|
4
|
+
export const escapePostgresLiteral = pg.escapeLiteral;
|
|
4
5
|
export class DoesNotExistError extends Error {
|
|
5
6
|
constructor(message) {
|
|
6
7
|
super(message);
|
|
@@ -156,4 +157,118 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
156
157
|
},
|
|
157
158
|
};
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
+
const isRaw = (value) => {
|
|
161
|
+
return (typeof value === 'object' && value !== null && 'raw' in value && !!value.raw);
|
|
162
|
+
};
|
|
163
|
+
class SQLStatement {
|
|
164
|
+
constructor(strings, values) {
|
|
165
|
+
this.stringsAndValues = [];
|
|
166
|
+
this._extract(strings, values);
|
|
167
|
+
}
|
|
168
|
+
_extract(strings, values) {
|
|
169
|
+
for (let i = 0; i < strings.length; i++) {
|
|
170
|
+
const string = strings[i];
|
|
171
|
+
const value = values[i];
|
|
172
|
+
if (isRaw(value)) {
|
|
173
|
+
this.stringsAndValues.push([string + value.name, undefined]);
|
|
174
|
+
}
|
|
175
|
+
else if (value instanceof SQLStatement) {
|
|
176
|
+
this.stringsAndValues.push([string, undefined]);
|
|
177
|
+
this.stringsAndValues.push(...value.stringsAndValues);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.stringsAndValues.push([string, value]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
append(statement) {
|
|
185
|
+
if (statement instanceof SQLStatement) {
|
|
186
|
+
this.stringsAndValues.push(...statement.stringsAndValues);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
this.stringsAndValues.push([statement, undefined]);
|
|
190
|
+
}
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
get values() {
|
|
194
|
+
return this.stringsAndValues
|
|
195
|
+
.map(([, value]) => value)
|
|
196
|
+
.filter((x) => x !== undefined);
|
|
197
|
+
}
|
|
198
|
+
get text() {
|
|
199
|
+
let i = 1;
|
|
200
|
+
let result = '';
|
|
201
|
+
for (const [string, value] of this.stringsAndValues) {
|
|
202
|
+
if (value == null) {
|
|
203
|
+
result += string;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
result += `${string}$${i}`;
|
|
207
|
+
i += 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export const sql = (strings, ...values) => {
|
|
214
|
+
return new SQLStatement(Array.from(strings), values);
|
|
215
|
+
};
|
|
216
|
+
function spreadUpdate(data) {
|
|
217
|
+
const entries = Object.entries(data);
|
|
218
|
+
if (entries.length === 0) {
|
|
219
|
+
return sql ``;
|
|
220
|
+
}
|
|
221
|
+
return joinStatements(entries.map(([key, value]) => sql `${sql.identifier(key)} = ${value}`), ', ');
|
|
222
|
+
}
|
|
223
|
+
function spreadInsert(data) {
|
|
224
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
225
|
+
if (rows.length === 0) {
|
|
226
|
+
return sql ``;
|
|
227
|
+
}
|
|
228
|
+
const statement = sql `(`;
|
|
229
|
+
statement.append(Object.keys(rows[0])
|
|
230
|
+
.map((k) => escapePostgresIdentifier(k))
|
|
231
|
+
.join(', '));
|
|
232
|
+
statement.append(sql `) VALUES `);
|
|
233
|
+
const toInsert = [];
|
|
234
|
+
for (const row of rows) {
|
|
235
|
+
const values = Object.values(row);
|
|
236
|
+
if (values.length === 0) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
toInsert.push(sql `(${joinStatements(values.map((value) => sql `${value ?? sql.raw('NULL')}`), ', ')})`);
|
|
240
|
+
}
|
|
241
|
+
statement.append(joinStatements(toInsert, ', '));
|
|
242
|
+
return statement;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Inject an identifier into the template string
|
|
246
|
+
*/
|
|
247
|
+
sql.identifier = (name) => {
|
|
248
|
+
return { name: escapePostgresIdentifier(name), raw: true };
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Inject a raw value into the template string
|
|
252
|
+
*/
|
|
253
|
+
sql.raw = (name) => {
|
|
254
|
+
return { name, raw: true };
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Inject a literal value into the template string
|
|
258
|
+
*/
|
|
259
|
+
sql.literal = (name) => {
|
|
260
|
+
return { name: escapePostgresLiteral(name), raw: true };
|
|
261
|
+
};
|
|
262
|
+
sql.spreadUpdate = spreadUpdate;
|
|
263
|
+
sql.spreadInsert = spreadInsert;
|
|
264
|
+
export function joinStatements(statements, separator) {
|
|
265
|
+
let i = 0;
|
|
266
|
+
const statement = sql ``;
|
|
267
|
+
for (const o of statements) {
|
|
268
|
+
if (i++ !== 0) {
|
|
269
|
+
statement.append(separator);
|
|
270
|
+
}
|
|
271
|
+
statement.append(o);
|
|
272
|
+
}
|
|
273
|
+
return statement;
|
|
274
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
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.9.3",
|
|
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.15.0",
|
|
26
|
+
"@typescript-eslint/parser": "^8.15.0",
|
|
27
|
+
"@vitest/coverage-v8": "^2.1.5",
|
|
28
28
|
"cookiejar": "^2.1.4",
|
|
29
29
|
"eslint": "^8.57.0",
|
|
30
30
|
"eslint-plugin-import": "^2.31.0",
|
|
@@ -34,20 +34,20 @@
|
|
|
34
34
|
"supertest": "7.0.0",
|
|
35
35
|
"ts-node": "10.9.2",
|
|
36
36
|
"tsx": "^4.19.2",
|
|
37
|
-
"typescript": "5.
|
|
38
|
-
"vitest": "^2.1.
|
|
37
|
+
"typescript": "5.7.2",
|
|
38
|
+
"vitest": "^2.1.5",
|
|
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.40.0",
|
|
44
|
+
"@sentry/profiling-node": "^8.40.0",
|
|
45
45
|
"@types/bcrypt": "^5.0.2",
|
|
46
46
|
"@types/body-parser": "^1.19.5",
|
|
47
47
|
"@types/cookie-parser": "^1.4.7",
|
|
48
48
|
"@types/express": "^5.0.0",
|
|
49
49
|
"@types/jsonwebtoken": "^9.0.7",
|
|
50
|
-
"@types/nodemailer": "^6.4.
|
|
50
|
+
"@types/nodemailer": "^6.4.17",
|
|
51
51
|
"@types/pg": "^8.11.10",
|
|
52
52
|
"@types/superagent": "^8.1.9",
|
|
53
53
|
"@types/uuid": "^10.0.0",
|
|
@@ -65,8 +65,7 @@
|
|
|
65
65
|
"nodemailer": "^6.9.16",
|
|
66
66
|
"pg": "^8.13.1",
|
|
67
67
|
"redis": "^4.7.0",
|
|
68
|
-
"
|
|
69
|
-
"uuid": "^11.0.2"
|
|
68
|
+
"uuid": "^11.0.3"
|
|
70
69
|
},
|
|
71
70
|
"prettier": {
|
|
72
71
|
"singleQuote": true
|