@devmoods/express-extras 0.40.1 → 0.42.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/auth.d.ts +8 -2
- package/dist/auth.js +27 -10
- package/dist/csrf.d.ts +1 -0
- package/dist/csrf.js +9 -2
- package/dist/middleware.d.ts +1 -1
- package/dist/postgres.d.ts +25 -19
- package/dist/postgres.js +40 -12
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +4 -1
- package/dist/validate.d.ts +5 -5
- package/package.json +12 -11
package/dist/auth.d.ts
CHANGED
|
@@ -30,7 +30,10 @@ export interface AuthProviderContext<UserModel extends AnyUser> {
|
|
|
30
30
|
redis: AuthOptions<UserModel>['redis'];
|
|
31
31
|
setCurrentUser: (user: UserModel) => void;
|
|
32
32
|
createTemporaryToken(user: UserModel): Promise<string>;
|
|
33
|
-
middleware: (
|
|
33
|
+
middleware: (options?: AuthMiddlewareOptions<UserModel>) => RequestHandler;
|
|
34
|
+
login: (res: Response, user: UserModel) => Promise<{
|
|
35
|
+
token: string;
|
|
36
|
+
}>;
|
|
34
37
|
}
|
|
35
38
|
export type AnyUser = Record<string, any>;
|
|
36
39
|
export interface AuthProvider<UserModel extends AnyUser> {
|
|
@@ -45,9 +48,12 @@ export interface AuthOptions<UserModel extends AnyUser> {
|
|
|
45
48
|
export interface CreateAuth<UserModel extends AnyUser> {
|
|
46
49
|
createRouter: () => Router;
|
|
47
50
|
useCurrentUser: () => UserModel | undefined;
|
|
48
|
-
middleware: (
|
|
51
|
+
middleware: (options: AuthMiddlewareOptions<UserModel>) => RequestHandler;
|
|
49
52
|
createTemporaryToken(user: UserModel): Promise<string>;
|
|
50
53
|
}
|
|
54
|
+
export type AuthMiddlewareOptions<UserModel extends AnyUser> = false | {
|
|
55
|
+
isAuthorized?: (user: UserModel) => Promise<boolean> | boolean;
|
|
56
|
+
};
|
|
51
57
|
export declare function createAuth<UserModel extends AnyUser>({ getUserByToken, filterUser, redis, providers, }: AuthOptions<UserModel>): CreateAuth<UserModel>;
|
|
52
58
|
/**
|
|
53
59
|
* Extract a Bearer token from the HTTP Authorization header
|
package/dist/auth.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Router, } from 'express';
|
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
5
|
import { v4 } from 'uuid';
|
|
6
6
|
import { setContext, useContext } from './context.js';
|
|
7
|
-
import { BadRequest, NotFound, Unauthorized } from './errors.js';
|
|
7
|
+
import { ApiError, BadRequest, Forbidden, NotFound, Unauthorized, } from './errors.js';
|
|
8
8
|
import { getLogger } from './logger.js';
|
|
9
9
|
import { route } from './middleware.js';
|
|
10
10
|
import {} from './redis.js';
|
|
@@ -57,10 +57,7 @@ export function EmailAndPasswordAuthProvider(options) {
|
|
|
57
57
|
}), route(async ({ body: { email, password } }, res) => {
|
|
58
58
|
try {
|
|
59
59
|
const user = await options.login(email, (realPassword) => checkIfPasswordsMatch(realPassword, password));
|
|
60
|
-
const token =
|
|
61
|
-
await context.redis.set(token, '1', { EX: TOKEN_EXPIRES_IN });
|
|
62
|
-
setAccessTokenCookie(res, token);
|
|
63
|
-
context.setCurrentUser(user);
|
|
60
|
+
const { token } = await context.login(res, user);
|
|
64
61
|
logger.info('access token created');
|
|
65
62
|
return [201, { token }];
|
|
66
63
|
}
|
|
@@ -146,12 +143,20 @@ export function EmailAndPasswordAuthProvider(options) {
|
|
|
146
143
|
export function createAuth({ getUserByToken, filterUser, redis, providers, }) {
|
|
147
144
|
const useCurrentUser = () => useContext('currentUser');
|
|
148
145
|
const setCurrentUser = (user) => setContext('currentUser', user);
|
|
146
|
+
const login = async (res, user) => {
|
|
147
|
+
const token = createAuthToken(filterUser(user));
|
|
148
|
+
await redis.set(token, '1', { EX: TOKEN_EXPIRES_IN });
|
|
149
|
+
setAccessTokenCookie(res, token);
|
|
150
|
+
setCurrentUser(user);
|
|
151
|
+
return { token };
|
|
152
|
+
};
|
|
149
153
|
const providerContext = {
|
|
150
154
|
filterUser,
|
|
151
155
|
redis,
|
|
152
156
|
setCurrentUser,
|
|
153
157
|
createTemporaryToken,
|
|
154
158
|
middleware,
|
|
159
|
+
login,
|
|
155
160
|
};
|
|
156
161
|
const createRouter = () => {
|
|
157
162
|
const router = Router();
|
|
@@ -201,19 +206,24 @@ export function createAuth({ getUserByToken, filterUser, redis, providers, }) {
|
|
|
201
206
|
}
|
|
202
207
|
return router;
|
|
203
208
|
};
|
|
204
|
-
function middleware(
|
|
209
|
+
function middleware(options = {}) {
|
|
210
|
+
let authenticate = true;
|
|
211
|
+
if (typeof options === 'boolean') {
|
|
212
|
+
authenticate = options;
|
|
213
|
+
options = {};
|
|
214
|
+
}
|
|
205
215
|
return async (req, res, next) => {
|
|
206
216
|
try {
|
|
207
217
|
const token = extractAccessTokenFromRequest(req);
|
|
208
218
|
if (!token) {
|
|
209
|
-
if (!
|
|
219
|
+
if (!authenticate) {
|
|
210
220
|
return next();
|
|
211
221
|
}
|
|
212
222
|
throw new InvalidToken('token missing');
|
|
213
223
|
}
|
|
214
224
|
const valid = await redis.get(token);
|
|
215
225
|
if (!valid) {
|
|
216
|
-
if (!
|
|
226
|
+
if (!authenticate) {
|
|
217
227
|
logger.warn('request with invalid access token');
|
|
218
228
|
return next();
|
|
219
229
|
}
|
|
@@ -221,13 +231,20 @@ export function createAuth({ getUserByToken, filterUser, redis, providers, }) {
|
|
|
221
231
|
}
|
|
222
232
|
const decoded = decodeAuthToken(token);
|
|
223
233
|
const user = await getUserByToken(decoded);
|
|
234
|
+
if (options.isAuthorized && !(await options.isAuthorized(user))) {
|
|
235
|
+
throw Forbidden('Permission denied');
|
|
236
|
+
}
|
|
224
237
|
setCurrentUser(user);
|
|
225
238
|
next();
|
|
226
239
|
}
|
|
227
240
|
catch (error) {
|
|
228
241
|
logger.error(error);
|
|
229
|
-
|
|
230
|
-
|
|
242
|
+
if (error instanceof ApiError) {
|
|
243
|
+
next(error);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
next(Unauthorized('Invalid token'));
|
|
247
|
+
}
|
|
231
248
|
}
|
|
232
249
|
};
|
|
233
250
|
}
|
package/dist/csrf.d.ts
CHANGED
package/dist/csrf.js
CHANGED
|
@@ -9,6 +9,13 @@ const defaultCookieName = () => {
|
|
|
9
9
|
? '__Host-devmoods.x-csrf-token'
|
|
10
10
|
: 'devmoods.x-csrf-token';
|
|
11
11
|
};
|
|
12
|
+
export const getCsrfSecret = () => {
|
|
13
|
+
const secret = readEnv('CSRF_SECRET', '');
|
|
14
|
+
if (!secret) {
|
|
15
|
+
throw new Error('CSRF_SECRET is not set');
|
|
16
|
+
}
|
|
17
|
+
return secret;
|
|
18
|
+
};
|
|
12
19
|
export function csrf({ excempt = false, sessionIdentifier, cookieName, } = {}) {
|
|
13
20
|
return (req, res, next) => {
|
|
14
21
|
const existingOptions = useCsrfOptions();
|
|
@@ -34,7 +41,7 @@ function useCsrfOptions() {
|
|
|
34
41
|
});
|
|
35
42
|
}
|
|
36
43
|
const generateTokenAndHash = (req, { overwrite = false } = {}) => {
|
|
37
|
-
const secret =
|
|
44
|
+
const secret = getCsrfSecret();
|
|
38
45
|
const { sessionIdentifier, cookieName } = useCsrfOptions();
|
|
39
46
|
const csrfCookie = req.cookies[cookieName];
|
|
40
47
|
if (typeof csrfCookie === 'string' && !overwrite) {
|
|
@@ -84,7 +91,7 @@ const validateRequest = (req) => {
|
|
|
84
91
|
}
|
|
85
92
|
const [csrfToken, csrfTokenHash] = csrfCookie.split('|');
|
|
86
93
|
const csrfTokenFromRequest = req.headers['x-csrf-token'];
|
|
87
|
-
const secret =
|
|
94
|
+
const secret = getCsrfSecret();
|
|
88
95
|
if (!csrfTokenFromRequest) {
|
|
89
96
|
throw Forbidden('Missing CSRF token');
|
|
90
97
|
}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare function after(): RequestHandler;
|
|
|
38
38
|
type ResponseHeaders = Record<string, string>;
|
|
39
39
|
type AnyResponseBody = Record<string, any> | string;
|
|
40
40
|
type HttpResponseCode = number;
|
|
41
|
-
type ResponseWrapper<TResponseBody extends AnyResponseBody> = TResponseBody | HttpResponseCode | [HttpResponseCode, TResponseBody] | [HttpResponseCode, TResponseBody, ResponseHeaders];
|
|
41
|
+
type ResponseWrapper<TResponseBody extends AnyResponseBody> = TResponseBody | TResponseBody[] | HttpResponseCode | [HttpResponseCode, TResponseBody] | [HttpResponseCode, TResponseBody, ResponseHeaders];
|
|
42
42
|
/**
|
|
43
43
|
* Alias of `route()`
|
|
44
44
|
*/
|
package/dist/postgres.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import pg, { type PoolClient, type PoolConfig, type QueryResult, type QueryResultRow } from 'pg';
|
|
2
2
|
export declare const escapePostgresIdentifier: typeof pg.escapeIdentifier;
|
|
3
3
|
export declare const escapePostgresLiteral: typeof pg.escapeLiteral;
|
|
4
|
-
export
|
|
4
|
+
export declare const formatSql: (statement: SqlStatement | string) => string;
|
|
5
|
+
export interface PostgresOptions extends PoolConfig, ConnectionOptions {
|
|
5
6
|
onError?: (error: Error, client: any) => void;
|
|
6
7
|
url?: PoolConfig['connectionString'];
|
|
7
8
|
}
|
|
@@ -15,14 +16,19 @@ export declare class IntegrityError extends Error {
|
|
|
15
16
|
constructor(message: string);
|
|
16
17
|
}
|
|
17
18
|
type Value = null | string | number | Date;
|
|
19
|
+
export interface ConnectionOptions {
|
|
20
|
+
onLogStatement?: (statement: SqlStatement | string, values: unknown[] | undefined, duration: number) => void;
|
|
21
|
+
}
|
|
18
22
|
export declare class Connection {
|
|
23
|
+
private options;
|
|
19
24
|
client: PoolClient;
|
|
20
|
-
constructor(client: PoolClient);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
constructor(client: PoolClient, options: ConnectionOptions);
|
|
26
|
+
private onLogStatement;
|
|
27
|
+
query<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
|
|
28
|
+
all<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T[]>;
|
|
29
|
+
insert<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
|
|
30
|
+
first<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T | null>;
|
|
31
|
+
get<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T>;
|
|
26
32
|
}
|
|
27
33
|
export declare class Transaction extends Connection {
|
|
28
34
|
}
|
|
@@ -34,27 +40,27 @@ export declare class Transaction extends Connection {
|
|
|
34
40
|
* @param config
|
|
35
41
|
*/
|
|
36
42
|
export declare function createPostgres({ onError, url, ...config }?: PostgresOptions): {
|
|
37
|
-
insert: <T extends QueryResultRow>(text: string |
|
|
38
|
-
query: <T extends QueryResultRow>(text: string |
|
|
39
|
-
first: <T>(text: string |
|
|
40
|
-
all: <T>(text: string |
|
|
41
|
-
get: <T>(text: string |
|
|
43
|
+
insert: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
|
|
44
|
+
query: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
|
|
45
|
+
first: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T | null>;
|
|
46
|
+
all: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T[]>;
|
|
47
|
+
get: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T>;
|
|
42
48
|
withConnection: <T>(callback: (connection: Connection) => Promise<T>) => Promise<T>;
|
|
43
49
|
transaction: <T>(callback: (connection: Transaction) => Promise<T> | T) => Promise<T>;
|
|
44
50
|
instance: pg.Pool;
|
|
45
51
|
clear: (force?: boolean) => Promise<void>;
|
|
46
52
|
end(): Promise<void>;
|
|
47
53
|
};
|
|
48
|
-
declare class
|
|
54
|
+
export declare class SqlStatement {
|
|
49
55
|
stringsAndValues: [string, unknown | undefined][];
|
|
50
56
|
constructor(strings: string[], values: unknown[]);
|
|
51
57
|
_extract(strings: string[], values: unknown[]): void;
|
|
52
|
-
append(statement:
|
|
58
|
+
append(statement: SqlStatement | string): this;
|
|
53
59
|
get values(): ({} | null)[];
|
|
54
60
|
get text(): string;
|
|
55
61
|
}
|
|
56
62
|
export declare const sql: {
|
|
57
|
-
(strings: TemplateStringsArray, ...values: unknown[]):
|
|
63
|
+
(strings: TemplateStringsArray, ...values: unknown[]): SqlStatement;
|
|
58
64
|
identifier(name: string): {
|
|
59
65
|
name: string;
|
|
60
66
|
raw: boolean;
|
|
@@ -63,14 +69,14 @@ export declare const sql: {
|
|
|
63
69
|
name: string;
|
|
64
70
|
raw: boolean;
|
|
65
71
|
};
|
|
66
|
-
literal(name: string): {
|
|
72
|
+
literal(name: string | null): {
|
|
67
73
|
name: string;
|
|
68
74
|
raw: boolean;
|
|
69
75
|
};
|
|
70
76
|
spreadUpdate: typeof spreadUpdate;
|
|
71
77
|
spreadInsert: typeof spreadInsert;
|
|
72
78
|
};
|
|
73
|
-
declare function spreadUpdate(data: Record<string, any>):
|
|
74
|
-
declare function spreadInsert<TData extends Record<string, any>>(data: TData | TData[]):
|
|
75
|
-
export declare function joinStatements(statements:
|
|
79
|
+
declare function spreadUpdate(data: Record<string, any>): SqlStatement;
|
|
80
|
+
declare function spreadInsert<TData extends Record<string, any>>(data: TData | TData[]): SqlStatement;
|
|
81
|
+
export declare function joinStatements(statements: SqlStatement[], separator: string): SqlStatement;
|
|
76
82
|
export {};
|
package/dist/postgres.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import pg, {} from 'pg';
|
|
2
|
-
import {
|
|
2
|
+
import { format } from 'sql-formatter';
|
|
3
|
+
import { readEnv, startTimer } from './utils.js';
|
|
3
4
|
export const escapePostgresIdentifier = pg.escapeIdentifier;
|
|
4
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
|
+
};
|
|
5
15
|
export class DoesNotExistError extends Error {
|
|
6
16
|
constructor(message) {
|
|
7
17
|
super(message);
|
|
@@ -21,10 +31,20 @@ export class IntegrityError extends Error {
|
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
export class Connection {
|
|
24
|
-
constructor(client) {
|
|
34
|
+
constructor(client, options) {
|
|
35
|
+
this.options = options;
|
|
25
36
|
this.client = client;
|
|
26
37
|
}
|
|
38
|
+
onLogStatement(text, values, duration) {
|
|
39
|
+
if (this.options.onLogStatement) {
|
|
40
|
+
return this.options.onLogStatement(text, values, duration);
|
|
41
|
+
}
|
|
42
|
+
if (readEnv.boolean('LOG_SQL', false)) {
|
|
43
|
+
console.log(`\n${formatSql(text)}\n\nValues: ${JSON.stringify(values)}\nDuration: ${duration}ms\n`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
27
46
|
async query(text, values) {
|
|
47
|
+
const timer = startTimer();
|
|
28
48
|
try {
|
|
29
49
|
return await this.client.query(text, values);
|
|
30
50
|
}
|
|
@@ -34,9 +54,12 @@ export class Connection {
|
|
|
34
54
|
}
|
|
35
55
|
throw error;
|
|
36
56
|
}
|
|
57
|
+
finally {
|
|
58
|
+
this.onLogStatement?.(text, values, timer.getDuration());
|
|
59
|
+
}
|
|
37
60
|
}
|
|
38
61
|
async all(text, values) {
|
|
39
|
-
const result = await this.
|
|
62
|
+
const result = await this.query(text, values);
|
|
40
63
|
if (result.rows) {
|
|
41
64
|
return result.rows;
|
|
42
65
|
/* c8 ignore next 2*/
|
|
@@ -47,11 +70,11 @@ export class Connection {
|
|
|
47
70
|
return await this.query(text, values);
|
|
48
71
|
}
|
|
49
72
|
async first(text, values) {
|
|
50
|
-
const result = await this.
|
|
73
|
+
const result = await this.query(text, values);
|
|
51
74
|
return result.rows && result.rows.length ? result.rows[0] : null;
|
|
52
75
|
}
|
|
53
76
|
async get(text, values) {
|
|
54
|
-
const result = await this.
|
|
77
|
+
const result = await this.query(text, values);
|
|
55
78
|
if (result.rowCount === 0) {
|
|
56
79
|
throw new DoesNotExistError('Row matching query not found');
|
|
57
80
|
}
|
|
@@ -78,7 +101,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
78
101
|
async function withConnection(callback) {
|
|
79
102
|
const client = await pool.connect();
|
|
80
103
|
try {
|
|
81
|
-
const result = await callback(new Connection(client));
|
|
104
|
+
const result = await callback(new Connection(client, { onLogStatement: config.onLogStatement }));
|
|
82
105
|
return result;
|
|
83
106
|
}
|
|
84
107
|
finally {
|
|
@@ -104,7 +127,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
104
127
|
const client = await pool.connect();
|
|
105
128
|
try {
|
|
106
129
|
await client.query('BEGIN');
|
|
107
|
-
const result = await callback(new Transaction(client));
|
|
130
|
+
const result = await callback(new Transaction(client, { onLogStatement: config.onLogStatement }));
|
|
108
131
|
await client.query('COMMIT');
|
|
109
132
|
return result;
|
|
110
133
|
}
|
|
@@ -160,7 +183,7 @@ export function createPostgres({ onError, url = readEnv('POSTGRES_URL'), ...conf
|
|
|
160
183
|
const isRaw = (value) => {
|
|
161
184
|
return (typeof value === 'object' && value !== null && 'raw' in value && !!value.raw);
|
|
162
185
|
};
|
|
163
|
-
class
|
|
186
|
+
export class SqlStatement {
|
|
164
187
|
constructor(strings, values) {
|
|
165
188
|
this.stringsAndValues = [];
|
|
166
189
|
this._extract(strings, values);
|
|
@@ -172,7 +195,7 @@ class SQLStatement {
|
|
|
172
195
|
if (isRaw(value)) {
|
|
173
196
|
this.stringsAndValues.push([string + value.name, undefined]);
|
|
174
197
|
}
|
|
175
|
-
else if (value instanceof
|
|
198
|
+
else if (value instanceof SqlStatement) {
|
|
176
199
|
this.stringsAndValues.push([string, undefined]);
|
|
177
200
|
this.stringsAndValues.push(...value.stringsAndValues);
|
|
178
201
|
}
|
|
@@ -182,7 +205,7 @@ class SQLStatement {
|
|
|
182
205
|
}
|
|
183
206
|
}
|
|
184
207
|
append(statement) {
|
|
185
|
-
if (statement instanceof
|
|
208
|
+
if (statement instanceof SqlStatement) {
|
|
186
209
|
this.stringsAndValues.push(...statement.stringsAndValues);
|
|
187
210
|
}
|
|
188
211
|
else {
|
|
@@ -211,14 +234,16 @@ class SQLStatement {
|
|
|
211
234
|
}
|
|
212
235
|
}
|
|
213
236
|
export const sql = (strings, ...values) => {
|
|
214
|
-
return new
|
|
237
|
+
return new SqlStatement(Array.from(strings), values);
|
|
215
238
|
};
|
|
216
239
|
function spreadUpdate(data) {
|
|
217
240
|
const entries = Object.entries(data);
|
|
218
241
|
if (entries.length === 0) {
|
|
219
242
|
return sql ``;
|
|
220
243
|
}
|
|
221
|
-
return joinStatements(entries
|
|
244
|
+
return joinStatements(entries
|
|
245
|
+
.filter(([, value]) => typeof value !== 'undefined')
|
|
246
|
+
.map(([key, value]) => sql `${sql.identifier(key)} = ${value == null ? sql.raw('NULL') : value}`), ', ');
|
|
222
247
|
}
|
|
223
248
|
function spreadInsert(data) {
|
|
224
249
|
const rows = Array.isArray(data) ? data : [data];
|
|
@@ -257,6 +282,9 @@ sql.raw = (name) => {
|
|
|
257
282
|
* Inject a literal value into the template string
|
|
258
283
|
*/
|
|
259
284
|
sql.literal = (name) => {
|
|
285
|
+
if (name === null) {
|
|
286
|
+
return { name: 'NULL', raw: true };
|
|
287
|
+
}
|
|
260
288
|
return { name: escapePostgresLiteral(name), raw: true };
|
|
261
289
|
};
|
|
262
290
|
sql.spreadUpdate = spreadUpdate;
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -4,8 +4,11 @@ import {} from 'node:events';
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
export function startTimer() {
|
|
7
|
-
|
|
7
|
+
let start = process.hrtime();
|
|
8
8
|
return {
|
|
9
|
+
reset() {
|
|
10
|
+
start = process.hrtime();
|
|
11
|
+
},
|
|
9
12
|
getDuration() {
|
|
10
13
|
const elapsed = process.hrtime(start);
|
|
11
14
|
return (elapsed[0] * 1e9 + elapsed[1]) / 1e9;
|
package/dist/validate.d.ts
CHANGED
|
@@ -39,12 +39,12 @@ export type ValidationResult = {
|
|
|
39
39
|
[key: string]: any;
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
-
export declare function compileValidateSchema(schema: ValidationSchema, options?: Options): (data:
|
|
43
|
-
isValid:
|
|
44
|
-
errors: null;
|
|
42
|
+
export declare function compileValidateSchema(schema: ValidationSchema, options?: Options): (data: unknown) => {
|
|
43
|
+
readonly isValid: true;
|
|
44
|
+
readonly errors: null;
|
|
45
45
|
} | {
|
|
46
|
-
isValid:
|
|
47
|
-
errors: {
|
|
46
|
+
readonly isValid: false;
|
|
47
|
+
readonly errors: {
|
|
48
48
|
[key: string]: any[];
|
|
49
49
|
};
|
|
50
50
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.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.17.0",
|
|
26
|
+
"@typescript-eslint/parser": "^8.17.0",
|
|
27
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
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.8",
|
|
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.42.0",
|
|
44
|
+
"@sentry/profiling-node": "^8.42.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",
|
|
@@ -58,13 +58,14 @@
|
|
|
58
58
|
"commander": "^12.1.0",
|
|
59
59
|
"compose-middleware": "^5.0.1",
|
|
60
60
|
"cookie-parser": "^1.4.7",
|
|
61
|
-
"dotenv": "^16.4.
|
|
61
|
+
"dotenv": "^16.4.6",
|
|
62
62
|
"express": "^4.21.1",
|
|
63
63
|
"helmet": "^8.0.0",
|
|
64
64
|
"jsonwebtoken": "^9.0.2",
|
|
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": {
|