@devmoods/express-extras 0.67.0 → 0.69.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/emailAndPassword.js +2 -2
- package/dist/auth/google.js +0 -1
- package/dist/auth/passwordless.js +2 -2
- package/dist/auth/token.d.ts +13 -13
- package/dist/auth/token.js +14 -13
- package/dist/auth.js +14 -10
- package/dist/channels.js +11 -7
- package/dist/cli.js +1 -0
- package/dist/composeMiddleware.js +4 -4
- package/dist/context.js +1 -0
- package/dist/csrf.js +1 -1
- package/dist/faktory.js +1 -1
- package/dist/listen.js +3 -2
- package/dist/logger.js +4 -5
- package/dist/middleware.js +8 -4
- package/dist/rate-limit.js +1 -0
- package/dist/router.d.ts +1 -1
- package/dist/routes.js +8 -5
- package/dist/streams.js +1 -0
- package/dist/validate.js +9 -8
- package/dist/vite/defineViteConfig.js +3 -2
- package/dist/vite/ssr.js +14 -9
- package/dist/vitest/matchers.js +1 -2
- package/dist/vitest/testClient.js +1 -1
- package/dist/webapi.js +4 -3
- package/package.json +19 -20
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { startTimer } from '@devmoods/observability';
|
|
2
2
|
import argon2 from 'argon2';
|
|
3
3
|
import bcrypt from 'bcrypt';
|
|
4
|
-
import {} from 'express';
|
|
5
4
|
import { createTokenResponse, tokenSchema } from './token.js';
|
|
6
5
|
import {} from './types.js';
|
|
7
6
|
import { Unauthorized } from '../errors.js';
|
|
@@ -57,8 +56,9 @@ export function EmailAndPasswordAuthProvider(options) {
|
|
|
57
56
|
}
|
|
58
57
|
context.setCurrentUser(user);
|
|
59
58
|
await options.onPasswordChanged?.(user, await hashPassword(password), {
|
|
60
|
-
/*
|
|
59
|
+
/* v8 ignore next -- @preserve */
|
|
61
60
|
ip: req.ip ?? '',
|
|
61
|
+
/* v8 ignore next -- @preserve */
|
|
62
62
|
userAgent: req.headers['user-agent'] ?? '',
|
|
63
63
|
});
|
|
64
64
|
logger.info('user changed password');
|
package/dist/auth/google.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DoesNotExistError } from '@devmoods/postgres';
|
|
2
|
-
import {} from 'express';
|
|
3
2
|
import { createTokenResponse } from './token.js';
|
|
4
3
|
import {} from './types.js';
|
|
5
4
|
import { getLogger } from '../logger.js';
|
|
@@ -11,10 +10,11 @@ export function PasswordlessAuthProvider(options) {
|
|
|
11
10
|
return await options.getUserByEmail(email);
|
|
12
11
|
}
|
|
13
12
|
catch (error) {
|
|
13
|
+
/* v8 ignore next -- @preserve */
|
|
14
14
|
if (error instanceof DoesNotExistError) {
|
|
15
15
|
return null;
|
|
16
|
-
/* c8 ignore next 3 */
|
|
17
16
|
}
|
|
17
|
+
/* v8 ignore next -- @preserve */
|
|
18
18
|
throw error;
|
|
19
19
|
}
|
|
20
20
|
};
|
package/dist/auth/token.d.ts
CHANGED
|
@@ -6,28 +6,28 @@ export declare function createTokenResponse(accessToken: AccessToken): readonly
|
|
|
6
6
|
}, {
|
|
7
7
|
readonly 'Cache-Control': "no-store";
|
|
8
8
|
}];
|
|
9
|
-
export type AccessToken = ReturnType<typeof createAccessToken
|
|
9
|
+
export type AccessToken = Awaited<ReturnType<typeof createAccessToken>>;
|
|
10
10
|
/**
|
|
11
11
|
* Returns a signed JWT token.
|
|
12
12
|
*/
|
|
13
|
-
export declare function createAccessToken(payload: any, version?: string, secret?: string): {
|
|
13
|
+
export declare function createAccessToken(payload: any, version?: string, secret?: string): Promise<{
|
|
14
14
|
token: string;
|
|
15
15
|
tokenType: "Bearer";
|
|
16
16
|
expiresIn: number;
|
|
17
|
-
}
|
|
17
|
+
}>;
|
|
18
18
|
/**
|
|
19
19
|
* Decode the given JWT token.
|
|
20
20
|
*/
|
|
21
|
-
export declare function decodeAccessToken(token: string, secret?: string): {
|
|
22
|
-
[
|
|
23
|
-
iss?: string
|
|
24
|
-
sub?: string
|
|
25
|
-
aud?: string | string[]
|
|
26
|
-
|
|
27
|
-
nbf?: number
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
21
|
+
export declare function decodeAccessToken(token: string, secret?: string): Promise<{
|
|
22
|
+
[propName: string]: unknown;
|
|
23
|
+
iss?: string;
|
|
24
|
+
sub?: string;
|
|
25
|
+
aud?: string | string[];
|
|
26
|
+
jti?: string;
|
|
27
|
+
nbf?: number;
|
|
28
|
+
exp?: number;
|
|
29
|
+
iat?: number;
|
|
30
|
+
}>;
|
|
31
31
|
/**
|
|
32
32
|
* Extract a Bearer token from the HTTP Authorization header
|
|
33
33
|
* of `req`.
|
package/dist/auth/token.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { config } from '@devmoods/config';
|
|
2
2
|
import {} from 'express';
|
|
3
|
-
import
|
|
3
|
+
import { SignJWT, jwtVerify } from 'jose';
|
|
4
4
|
import { v4 } from 'uuid';
|
|
5
5
|
import { InvalidToken } from './errors.js';
|
|
6
6
|
import {} from '../validate.js';
|
|
@@ -24,24 +24,24 @@ export function createTokenResponse(accessToken) {
|
|
|
24
24
|
/**
|
|
25
25
|
* Returns a signed JWT token.
|
|
26
26
|
*/
|
|
27
|
-
export function createAccessToken(payload, version = '1', secret = config.AUTH_JWT_SECRET) {
|
|
27
|
+
export async function createAccessToken(payload, version = '1', secret = config.AUTH_JWT_SECRET) {
|
|
28
28
|
const expiresIn = config.AUTH_TOKEN_EXPIRES;
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const secretKey = new TextEncoder().encode(secret);
|
|
30
|
+
const token = await new SignJWT({ v: version, ...payload })
|
|
31
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
32
|
+
.setExpirationTime(`${expiresIn}s`)
|
|
33
|
+
.setIssuedAt(new Date())
|
|
34
|
+
.sign(secretKey);
|
|
32
35
|
return { token, tokenType: 'Bearer', expiresIn };
|
|
33
36
|
}
|
|
34
37
|
/**
|
|
35
38
|
* Decode the given JWT token.
|
|
36
39
|
*/
|
|
37
|
-
export function decodeAccessToken(token, secret = config.AUTH_JWT_SECRET) {
|
|
40
|
+
export async function decodeAccessToken(token, secret = config.AUTH_JWT_SECRET) {
|
|
38
41
|
try {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw new Error('decoded value not an object');
|
|
43
|
-
}
|
|
44
|
-
return { ...decoded };
|
|
42
|
+
const secretKey = new TextEncoder().encode(secret);
|
|
43
|
+
const { payload } = await jwtVerify(token.trim(), secretKey);
|
|
44
|
+
return { ...payload };
|
|
45
45
|
}
|
|
46
46
|
catch (error) {
|
|
47
47
|
throw new InvalidToken(`Failed to decode auth token: ${error.message}`, {
|
|
@@ -65,10 +65,11 @@ export function parseAuthorizationHeader(req) {
|
|
|
65
65
|
}
|
|
66
66
|
// No token given, this is not necessarilly an error case
|
|
67
67
|
if (!token) {
|
|
68
|
+
/* v8 ignore next -- @preserve */
|
|
68
69
|
if (type) {
|
|
69
70
|
return type;
|
|
70
|
-
/* c8 ignore next 3 */
|
|
71
71
|
}
|
|
72
|
+
/* v8 ignore next -- @preserve */
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
74
75
|
return token;
|
package/dist/auth.js
CHANGED
|
@@ -22,15 +22,16 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
22
22
|
const useCurrentUser = () => useRequestContext('currentUser');
|
|
23
23
|
const setCurrentUser = (user) => setRequestContext('currentUser', user);
|
|
24
24
|
const login = async (req, res, user, isRefresh = false) => {
|
|
25
|
-
const accessToken = createAccessToken({ uid: user.id }, sessionTokenVersion);
|
|
25
|
+
const accessToken = await createAccessToken({ uid: user.id }, sessionTokenVersion);
|
|
26
26
|
const sessionId = sha256(accessToken.token);
|
|
27
27
|
await tokenStorage.set(sessionId, String(user.id), accessToken.expiresIn);
|
|
28
28
|
setAccessTokenCookie(res, accessToken.token);
|
|
29
29
|
setCurrentUser(user);
|
|
30
30
|
if (!isRefresh) {
|
|
31
31
|
await onUserLoggedIn?.(user, {
|
|
32
|
-
/*
|
|
32
|
+
/* v8 ignore next -- @preserve */
|
|
33
33
|
ip: req.ip ?? '',
|
|
34
|
+
/* v8 ignore next -- @preserve */
|
|
34
35
|
userAgent: req.headers['user-agent'] ?? '',
|
|
35
36
|
sessionId,
|
|
36
37
|
});
|
|
@@ -46,12 +47,12 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
46
47
|
};
|
|
47
48
|
// Make timing attacks harder
|
|
48
49
|
const jitter = (duration = 0) => {
|
|
49
|
-
/*
|
|
50
|
+
/* v8 ignore next -- @preserve */
|
|
50
51
|
if (process.env.NODE_ENV === 'test') {
|
|
51
52
|
return Promise.resolve();
|
|
52
53
|
}
|
|
54
|
+
/* v8 ignore next -- @preserve */
|
|
53
55
|
return new Promise((resolve) => void setTimeout(resolve, Math.max((0.5 - duration) * 1000, 200)));
|
|
54
|
-
/* c8 ignore end */
|
|
55
56
|
};
|
|
56
57
|
/**
|
|
57
58
|
* API for auth providers to use
|
|
@@ -138,7 +139,7 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
138
139
|
await logout(req, res);
|
|
139
140
|
throw new InvalidToken('session token expired');
|
|
140
141
|
}
|
|
141
|
-
const decoded = decodeAccessToken(token);
|
|
142
|
+
const decoded = await decodeAccessToken(token);
|
|
142
143
|
if (decoded.v !== sessionTokenVersion) {
|
|
143
144
|
await logout(req, res);
|
|
144
145
|
throw new InvalidToken('Invalid token version');
|
|
@@ -157,7 +158,7 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
157
158
|
const tokenLifetimeMs = decoded.exp * 1000 - decoded.iat * 1000;
|
|
158
159
|
const remainingMs = decoded.exp * 1000 - Date.now();
|
|
159
160
|
const shouldRefresh = remainingMs < tokenLifetimeMs * 0.25;
|
|
160
|
-
/*
|
|
161
|
+
/* v8 ignore next -- @preserve */
|
|
161
162
|
if (shouldRefresh) {
|
|
162
163
|
await providerContext.login(req, res, user, true);
|
|
163
164
|
}
|
|
@@ -166,18 +167,19 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
166
167
|
}
|
|
167
168
|
catch (error) {
|
|
168
169
|
logger.error(error);
|
|
170
|
+
/* v8 ignore next -- @preserve */
|
|
169
171
|
if (error instanceof InvalidToken) {
|
|
170
172
|
if (req.accepts('html')) {
|
|
171
173
|
return res.redirect(loginUrl);
|
|
172
174
|
}
|
|
173
175
|
return next(Unauthorized(`Invalid token: ${error.message}`, { cause: error }));
|
|
174
176
|
}
|
|
175
|
-
/*
|
|
177
|
+
/* v8 ignore next -- @preserve */
|
|
176
178
|
next(error);
|
|
177
179
|
}
|
|
178
180
|
};
|
|
179
181
|
}
|
|
180
|
-
function protect(isAuthorized
|
|
182
|
+
function protect(isAuthorized) {
|
|
181
183
|
return async function authorizationMiddleware(req, res, next) {
|
|
182
184
|
try {
|
|
183
185
|
const user = useCurrentUser();
|
|
@@ -197,13 +199,14 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
197
199
|
}
|
|
198
200
|
return next(Forbidden('Permission denied', { cause: error }));
|
|
199
201
|
}
|
|
202
|
+
/* v8 ignore next -- @preserve */
|
|
200
203
|
if (error instanceof InvalidToken) {
|
|
201
204
|
if (req.accepts('html')) {
|
|
202
205
|
return res.redirect(loginUrl);
|
|
203
206
|
}
|
|
204
207
|
return next(Unauthorized(`Invalid token: ${error.message}`, { cause: error }));
|
|
205
208
|
}
|
|
206
|
-
/*
|
|
209
|
+
/* v8 ignore next -- @preserve */
|
|
207
210
|
next(error);
|
|
208
211
|
}
|
|
209
212
|
};
|
|
@@ -268,7 +271,7 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
268
271
|
.update(JSON.stringify(decoded.payload))
|
|
269
272
|
.digest('base64')
|
|
270
273
|
.slice(0, 12);
|
|
271
|
-
/*
|
|
274
|
+
/* v8 ignore next -- @preserve */
|
|
272
275
|
if (expectedSignature !== decoded.signature) {
|
|
273
276
|
throw new InvalidToken('Invalid signature');
|
|
274
277
|
}
|
|
@@ -289,6 +292,7 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
289
292
|
}
|
|
290
293
|
return {
|
|
291
294
|
protect,
|
|
295
|
+
/* v8 ignore next -- @preserve */
|
|
292
296
|
before: () => middleware(false),
|
|
293
297
|
createRouter: () => createAuthRouter().getRouter(),
|
|
294
298
|
useCurrentUser,
|
package/dist/channels.js
CHANGED
|
@@ -8,7 +8,7 @@ import {} from './router.js';
|
|
|
8
8
|
import { writeChunk } from './streams.js';
|
|
9
9
|
const logger = getLogger();
|
|
10
10
|
const defaultGetIdFromMessage = (message) => {
|
|
11
|
-
/*
|
|
11
|
+
/* v8 ignore next -- @preserve */
|
|
12
12
|
if (typeof message === 'object' && message !== null && 'id' in message) {
|
|
13
13
|
return `${message.id}`;
|
|
14
14
|
}
|
|
@@ -24,7 +24,7 @@ export class RedisEventStreamBackend {
|
|
|
24
24
|
}
|
|
25
25
|
async createSubscriber() {
|
|
26
26
|
const subscriber = this.redis.duplicate();
|
|
27
|
-
/*
|
|
27
|
+
/* v8 ignore next -- @preserve */
|
|
28
28
|
subscriber.on('error', (error) => {
|
|
29
29
|
logger.error(error);
|
|
30
30
|
});
|
|
@@ -42,13 +42,14 @@ export function createEventStream(backend, options) {
|
|
|
42
42
|
const subscriber = await backend.createSubscriber();
|
|
43
43
|
const heartbeat = setInterval(() => {
|
|
44
44
|
try {
|
|
45
|
+
/* v8 ignore next -- @preserve */
|
|
45
46
|
if (!res.writableEnded) {
|
|
46
47
|
writeChunk(res, 'event: heartbeat\n', state);
|
|
47
48
|
writeChunk(res, 'data: ping\n\n', state);
|
|
48
49
|
}
|
|
49
|
-
/* c8 ignore next 3 */
|
|
50
50
|
}
|
|
51
51
|
catch (error) {
|
|
52
|
+
/* v8 ignore next -- @preserve */
|
|
52
53
|
logger.error(error);
|
|
53
54
|
}
|
|
54
55
|
}, options.heartbeatInterval ?? 15000);
|
|
@@ -61,6 +62,7 @@ export function createEventStream(backend, options) {
|
|
|
61
62
|
const listener = (value) => {
|
|
62
63
|
try {
|
|
63
64
|
requestContext.run(store, () => {
|
|
65
|
+
/* v8 ignore next -- @preserve */
|
|
64
66
|
if (!res.writableEnded) {
|
|
65
67
|
const id = getIdFromMessage?.(value);
|
|
66
68
|
if (id) {
|
|
@@ -70,9 +72,9 @@ export function createEventStream(backend, options) {
|
|
|
70
72
|
writeChunk(res, `data: ${JSON.stringify(value)}\n\n`, state);
|
|
71
73
|
}
|
|
72
74
|
});
|
|
73
|
-
/* c8 ignore next 3 */
|
|
74
75
|
}
|
|
75
76
|
catch (error) {
|
|
77
|
+
/* v8 ignore next -- @preserve */
|
|
76
78
|
logger.error(error);
|
|
77
79
|
}
|
|
78
80
|
};
|
|
@@ -81,11 +83,12 @@ export function createEventStream(backend, options) {
|
|
|
81
83
|
let parsedMessage;
|
|
82
84
|
try {
|
|
83
85
|
parsedMessage = JSON.parse(message);
|
|
84
|
-
/* c8 ignore next 3 */
|
|
85
86
|
}
|
|
86
87
|
catch (error) {
|
|
88
|
+
/* v8 ignore next -- @preserve */
|
|
87
89
|
logger.error(error);
|
|
88
90
|
}
|
|
91
|
+
/* v8 ignore next -- @preserve */
|
|
89
92
|
if (parsedMessage) {
|
|
90
93
|
listener(parsedMessage);
|
|
91
94
|
}
|
|
@@ -112,10 +115,11 @@ export function createEventStream(backend, options) {
|
|
|
112
115
|
});
|
|
113
116
|
};
|
|
114
117
|
const gracefulClose = () => {
|
|
115
|
-
/*
|
|
118
|
+
/* v8 ignore next -- @preserve */
|
|
116
119
|
if (closed) {
|
|
117
120
|
return;
|
|
118
121
|
}
|
|
122
|
+
/* v8 ignore next -- @preserve */
|
|
119
123
|
if (!res.writableEnded) {
|
|
120
124
|
writeChunk(res, 'event: close\n', state);
|
|
121
125
|
writeChunk(res, 'data: shutdown\n\n', state);
|
|
@@ -132,9 +136,9 @@ export function createEventStream(backend, options) {
|
|
|
132
136
|
res.on('finish', cleanup);
|
|
133
137
|
req.on('aborted', cleanup);
|
|
134
138
|
req.socket.on('error', cleanup);
|
|
135
|
-
/* c8 ignore next 3 */
|
|
136
139
|
}
|
|
137
140
|
catch (error) {
|
|
141
|
+
/* v8 ignore next -- @preserve */
|
|
138
142
|
next(error);
|
|
139
143
|
}
|
|
140
144
|
};
|
package/dist/cli.js
CHANGED
|
@@ -29,15 +29,15 @@ function generate(handlers) {
|
|
|
29
29
|
}
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
31
31
|
function next(err) {
|
|
32
|
-
/*
|
|
32
|
+
/* v8 ignore next -- @preserve */
|
|
33
33
|
if (pos < index) {
|
|
34
34
|
throw new TypeError('`next()` called multiple times');
|
|
35
35
|
}
|
|
36
|
-
/* c8 ignore end */
|
|
37
36
|
return dispatch(pos + 1, err);
|
|
38
37
|
}
|
|
39
38
|
try {
|
|
40
39
|
if (handler.length === 4) {
|
|
40
|
+
/* v8 ignore next -- @preserve */
|
|
41
41
|
if (err) {
|
|
42
42
|
return handler(err, req, res, next);
|
|
43
43
|
}
|
|
@@ -49,12 +49,12 @@ function generate(handlers) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
catch (e) {
|
|
52
|
-
/*
|
|
52
|
+
/* v8 ignore next -- @preserve */
|
|
53
53
|
if (index > pos) {
|
|
54
54
|
throw e;
|
|
55
55
|
}
|
|
56
|
+
/* v8 ignore next -- @preserve */
|
|
56
57
|
return next(e);
|
|
57
|
-
/* c8 ignore end */
|
|
58
58
|
}
|
|
59
59
|
return next(err);
|
|
60
60
|
}
|
package/dist/context.js
CHANGED
package/dist/csrf.js
CHANGED
package/dist/faktory.js
CHANGED
|
@@ -65,7 +65,7 @@ export async function createFaktoryWorker({ queues, jobs, faktory: createFactory
|
|
|
65
65
|
return job.execute(...args, jobContext);
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
-
/*
|
|
68
|
+
/* v8 ignore next -- @preserve */
|
|
69
69
|
const onSignal = async (signal) => {
|
|
70
70
|
logger.info('faktory worker received signal', { signal });
|
|
71
71
|
};
|
package/dist/listen.js
CHANGED
|
@@ -24,7 +24,7 @@ export function stoppable(server, gracePeriodMs = Infinity) {
|
|
|
24
24
|
/* c8 ignore next 1 */
|
|
25
25
|
(requestsPerSocket.get(req.socket) ?? 0) + 1);
|
|
26
26
|
res.once('finish', () => {
|
|
27
|
-
/*
|
|
27
|
+
/* v8 ignore next -- @preserve */
|
|
28
28
|
const pending = Math.max(0, (requestsPerSocket.get(req.socket) ?? 0) - 1);
|
|
29
29
|
requestsPerSocket.set(req.socket, pending);
|
|
30
30
|
if (stopped && pending === 0) {
|
|
@@ -71,7 +71,7 @@ export function stoppable(server, gracePeriodMs = Infinity) {
|
|
|
71
71
|
if (address == null) {
|
|
72
72
|
throw new Error('address is null, server not listening?');
|
|
73
73
|
}
|
|
74
|
-
/*
|
|
74
|
+
/* v8 ignore next -- @preserve */
|
|
75
75
|
if (typeof address === 'string') {
|
|
76
76
|
throw new Error('address was a string, should not happen');
|
|
77
77
|
}
|
|
@@ -139,6 +139,7 @@ export function createExpressServer(app, options = {}) {
|
|
|
139
139
|
process.on(signal, onSignal);
|
|
140
140
|
}
|
|
141
141
|
const healthTypes = ['live', 'ready'];
|
|
142
|
+
/* v8 ignore next -- @preserve */
|
|
142
143
|
if (!app.get('__healthAttached')) {
|
|
143
144
|
app.get('/health/:type?', before(), route(async (req) => {
|
|
144
145
|
const timestamp = new Date().toISOString();
|
package/dist/logger.js
CHANGED
|
@@ -17,25 +17,24 @@ export class JsonStreamAdapter {
|
|
|
17
17
|
export class SentryLoggerAdapter {
|
|
18
18
|
constructor() { }
|
|
19
19
|
captureError(error, attributes) {
|
|
20
|
-
/*
|
|
20
|
+
/* v8 ignore next -- @preserve */
|
|
21
21
|
if (error == null) {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
+
/* v8 ignore next -- @preserve */
|
|
24
25
|
if (Sentry.isInitialized()) {
|
|
25
26
|
Sentry.captureException(error, {
|
|
26
27
|
tags: attributes,
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
|
-
/* c8 ignore end */
|
|
30
30
|
}
|
|
31
31
|
write(log) {
|
|
32
32
|
const { message, ...attributes } = log;
|
|
33
|
-
/*
|
|
33
|
+
/* v8 ignore next -- @preserve */
|
|
34
34
|
if (Sentry.isInitialized()) {
|
|
35
35
|
const fn = log.level;
|
|
36
36
|
Sentry.logger[fn](message, attributes);
|
|
37
37
|
}
|
|
38
|
-
/* c8 ignore end */
|
|
39
38
|
return true;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
@@ -57,7 +56,7 @@ export function clearGlobalLoggerContext() {
|
|
|
57
56
|
_getGlobalContextFns.length = 0;
|
|
58
57
|
}
|
|
59
58
|
const nameArgIndex = process.argv.indexOf('--name');
|
|
60
|
-
/*
|
|
59
|
+
/* v8 ignore next -- @preserve */
|
|
61
60
|
const app = nameArgIndex > -1 ? process.argv.at(nameArgIndex + 1) : undefined;
|
|
62
61
|
const hostname = os.hostname();
|
|
63
62
|
const pid = process.pid;
|
package/dist/middleware.js
CHANGED
|
@@ -17,7 +17,7 @@ export { asyncMiddleware as route };
|
|
|
17
17
|
export { compose as composeMiddleware };
|
|
18
18
|
const CSP_NONCE_KEY = 'csp-nonce';
|
|
19
19
|
export function useCspNonce() {
|
|
20
|
-
/*
|
|
20
|
+
/* v8 ignore next -- @preserve */
|
|
21
21
|
return useRequestContext(CSP_NONCE_KEY) ?? '__CSP_NOT_SET__';
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
@@ -97,11 +97,15 @@ function logRequestDuration() {
|
|
|
97
97
|
path: req.path,
|
|
98
98
|
query: req.query,
|
|
99
99
|
ip: req.ip,
|
|
100
|
-
/*
|
|
100
|
+
/* v8 ignore next -- @preserve */
|
|
101
101
|
userAgent: req.headers['user-agent'] ?? 'unknown',
|
|
102
|
+
/* v8 ignore next -- @preserve */
|
|
102
103
|
accept: req.headers['accept'] ?? 'unknown',
|
|
104
|
+
/* v8 ignore next -- @preserve */
|
|
103
105
|
host: req.headers['host'] ?? 'unknown',
|
|
106
|
+
/* v8 ignore next -- @preserve */
|
|
104
107
|
referer: req.headers['referer'] ?? 'unknown',
|
|
108
|
+
/* v8 ignore next -- @preserve */
|
|
105
109
|
origin: req.headers['origin'] ?? 'unknown',
|
|
106
110
|
});
|
|
107
111
|
res.on('finish', () => {
|
|
@@ -205,7 +209,7 @@ export function asyncMiddleware(fn) {
|
|
|
205
209
|
else if (typeof result === 'number') {
|
|
206
210
|
res.status(result).end();
|
|
207
211
|
}
|
|
208
|
-
else
|
|
212
|
+
else {
|
|
209
213
|
// @ts-expect-error custom value
|
|
210
214
|
const successStatus = req.__successStatus;
|
|
211
215
|
if (successStatus != null) {
|
|
@@ -228,10 +232,10 @@ export function withTransaction(wrapper) {
|
|
|
228
232
|
res.on('error', reject);
|
|
229
233
|
next();
|
|
230
234
|
});
|
|
231
|
-
/* c8 ignore next 5 */
|
|
232
235
|
});
|
|
233
236
|
}
|
|
234
237
|
catch (error) {
|
|
238
|
+
/* v8 ignore next -- @preserve */
|
|
235
239
|
next(error);
|
|
236
240
|
}
|
|
237
241
|
};
|
package/dist/rate-limit.js
CHANGED
package/dist/router.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type ExtractParams<Path extends string> = Prettify<Path extends `${string
|
|
|
12
12
|
} & ExtractParams<`/${Rest}`> : Path extends `${string}:${infer Param}` ? {
|
|
13
13
|
[K in Param extends `${infer P}?` ? P : Param]: string;
|
|
14
14
|
} : {}>;
|
|
15
|
-
export type TypedRequestHandler<Params, ResponseBodySchema extends StandardSchemaV1 | undefined, RequestBodySchema extends StandardSchemaV1 | undefined, RequestQuery extends StandardSchemaV1 | undefined, RequestParamsSchema extends StandardSchemaV1 | undefined = undefined> = (req: Request<Params,
|
|
15
|
+
export type TypedRequestHandler<Params, ResponseBodySchema extends StandardSchemaV1 | undefined, RequestBodySchema extends StandardSchemaV1 | undefined, RequestQuery extends StandardSchemaV1 | undefined, RequestParamsSchema extends StandardSchemaV1 | undefined = undefined> = (req: Request<Params, any, RequestBodySchema extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<RequestBodySchema> : unknown, RequestQuery extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<RequestQuery> : Record<string, string | string[]>>, res: Response<ResponseBodySchema extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<ResponseBodySchema> : unknown>, next: NextFunction) => ResponseWrapper<ResponseBodySchema extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<ResponseBodySchema> : unknown> | Promise<ResponseWrapper<ResponseBodySchema extends StandardSchemaV1<any> ? StandardSchemaV1.InferOutput<ResponseBodySchema> : unknown>>;
|
|
16
16
|
export declare class TypedRouter {
|
|
17
17
|
#private;
|
|
18
18
|
router: import("express-serve-static-core").Router;
|
package/dist/routes.js
CHANGED
|
@@ -38,7 +38,7 @@ const buildPathParamsSchema = (pathParams) => {
|
|
|
38
38
|
const required = [];
|
|
39
39
|
const params = [];
|
|
40
40
|
for (const param of pathParams) {
|
|
41
|
-
/*
|
|
41
|
+
/* v8 ignore next -- @preserve */
|
|
42
42
|
if (param.in !== 'path') {
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
@@ -102,14 +102,14 @@ class ExpressPathParser {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
const pathRegexParser = (layerRegexPath, keys) => {
|
|
105
|
-
/*
|
|
105
|
+
/* v8 ignore next -- @preserve */
|
|
106
106
|
if (typeof layerRegexPath === 'string') {
|
|
107
107
|
return layerRegexPath;
|
|
108
108
|
}
|
|
109
109
|
if (layerRegexPath.fast_slash) {
|
|
110
110
|
return '';
|
|
111
111
|
}
|
|
112
|
-
/*
|
|
112
|
+
/* v8 ignore next -- @preserve */
|
|
113
113
|
if (layerRegexPath.fast_star) {
|
|
114
114
|
return '*';
|
|
115
115
|
}
|
|
@@ -122,10 +122,11 @@ const pathRegexParser = (layerRegexPath, keys) => {
|
|
|
122
122
|
if (match) {
|
|
123
123
|
return match[1].replace(/\\(.)/g, '$1').slice(1);
|
|
124
124
|
}
|
|
125
|
+
/* v8 ignore next -- @preserve */
|
|
125
126
|
if (mappedPath) {
|
|
126
127
|
return mappedPath.slice(1);
|
|
127
|
-
/* c8 ignore next 3 */
|
|
128
128
|
}
|
|
129
|
+
/* v8 ignore next -- @preserve */
|
|
129
130
|
return layerRegexPath.toString();
|
|
130
131
|
};
|
|
131
132
|
const mapKeysToPath = (layerRegexPath, keys) => {
|
|
@@ -197,6 +198,7 @@ export function toOpenAPISpec(app, info = {
|
|
|
197
198
|
}
|
|
198
199
|
// Convert Express path params to OpenAPI path params
|
|
199
200
|
const openApiPath = path.replace(/:([^/]+)/g, '{$1}');
|
|
201
|
+
/* v8 ignore next -- @preserve */
|
|
200
202
|
if (!paths[openApiPath]) {
|
|
201
203
|
paths[openApiPath] = {};
|
|
202
204
|
}
|
|
@@ -214,8 +216,9 @@ export function toOpenAPISpec(app, info = {
|
|
|
214
216
|
// Add query parameters
|
|
215
217
|
if (query && typeof query === 'object' && 'properties' in query) {
|
|
216
218
|
const querySchema = query;
|
|
217
|
-
/*
|
|
219
|
+
/* v8 ignore next -- @preserve */
|
|
218
220
|
const properties = querySchema.properties || {};
|
|
221
|
+
/* v8 ignore next -- @preserve */
|
|
219
222
|
parameters.push(...Object.keys(properties).map((name) => ({
|
|
220
223
|
name,
|
|
221
224
|
in: 'query',
|
package/dist/streams.js
CHANGED
|
@@ -17,6 +17,7 @@ export function writeChunk(writable, chunk, state) {
|
|
|
17
17
|
const flush = () => {
|
|
18
18
|
while (state.queue.length) {
|
|
19
19
|
const next = state.queue.shift();
|
|
20
|
+
/* v8 ignore next -- @preserve */
|
|
20
21
|
if (!writable.write(next)) {
|
|
21
22
|
state.draining = true;
|
|
22
23
|
writable.once('drain', flush);
|
package/dist/validate.js
CHANGED
|
@@ -27,12 +27,12 @@ export async function validateSchema(data, schema) {
|
|
|
27
27
|
catch (error) {
|
|
28
28
|
return {
|
|
29
29
|
isValid: false,
|
|
30
|
+
/* v8 ignore next -- @preserve */
|
|
30
31
|
errors: (error instanceof ValidationError
|
|
31
|
-
?
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
])
|
|
32
|
+
? error.issues.map((issue) => [
|
|
33
|
+
String(issue.path ?? '/'),
|
|
34
|
+
issue.message,
|
|
35
|
+
])
|
|
36
36
|
: []).reduce((result, item) => {
|
|
37
37
|
const [key, value] = item;
|
|
38
38
|
if (Array.isArray(result[key])) {
|
|
@@ -66,9 +66,9 @@ export function validateStandardSchema(schemas, extraMetadata) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
next();
|
|
69
|
-
/* c8 ignore next 3 */
|
|
70
69
|
}
|
|
71
70
|
catch (error) {
|
|
71
|
+
/* v8 ignore next -- @preserve */
|
|
72
72
|
next(error);
|
|
73
73
|
}
|
|
74
74
|
};
|
|
@@ -93,15 +93,16 @@ export function validateStandardSchema(schemas, extraMetadata) {
|
|
|
93
93
|
return middleware;
|
|
94
94
|
}
|
|
95
95
|
function toJsonSchema(standardSchema) {
|
|
96
|
+
/* v8 ignore next -- @preserve */
|
|
96
97
|
if ('toJsonSchema' in standardSchema &&
|
|
97
98
|
typeof standardSchema.toJsonSchema === 'function') {
|
|
98
99
|
return standardSchema.toJsonSchema();
|
|
99
|
-
/* c8 ignore next 3 */
|
|
100
100
|
}
|
|
101
|
+
/* v8 ignore next -- @preserve */
|
|
101
102
|
return {};
|
|
102
103
|
}
|
|
103
104
|
function transformAjvErrors(errors) {
|
|
104
|
-
/*
|
|
105
|
+
/* v8 ignore next -- @preserve */
|
|
105
106
|
if (errors == null) {
|
|
106
107
|
return {};
|
|
107
108
|
}
|
|
@@ -13,11 +13,11 @@ export function defineViteConfig({ base = '', reactCompiler = false, ssr = false
|
|
|
13
13
|
`${process.cwd().split('/').pop()}.local`,
|
|
14
14
|
`${process.cwd().split('/').pop()}.test`,
|
|
15
15
|
];
|
|
16
|
-
/*
|
|
16
|
+
/* v8 ignore next -- @preserve */
|
|
17
17
|
const globalSetup = existsSync(path.resolve('./src/setupTests.global.ts'))
|
|
18
18
|
? [path.resolve('./src/setupTests.global.ts')]
|
|
19
19
|
: undefined;
|
|
20
|
-
/*
|
|
20
|
+
/* v8 ignore next -- @preserve */
|
|
21
21
|
const setupFiles = existsSync(path.resolve('./src/setupTests.ts'))
|
|
22
22
|
? [path.resolve('./src/setupTests.ts')]
|
|
23
23
|
: undefined;
|
|
@@ -77,6 +77,7 @@ export function defineViteConfig({ base = '', reactCompiler = false, ssr = false
|
|
|
77
77
|
noExternal: ['react-helmet-async'],
|
|
78
78
|
}
|
|
79
79
|
: undefined,
|
|
80
|
+
// @ts-expect-error i dont know
|
|
80
81
|
test: {
|
|
81
82
|
setupFiles,
|
|
82
83
|
globalSetup,
|
package/dist/vite/ssr.js
CHANGED
|
@@ -16,7 +16,6 @@ import { createFetchRequest } from '../webapi.js';
|
|
|
16
16
|
export async function createSsrMiddleware({ root = process.cwd(), getInitialData, useDevServer = config.VITE_USE_DEV_SERVER, } = {}) {
|
|
17
17
|
const resolve = (p) => path.resolve(root, p);
|
|
18
18
|
let vite;
|
|
19
|
-
let render;
|
|
20
19
|
let assetLinks = { preload: '', css: '', js: '' };
|
|
21
20
|
const middlewares = [];
|
|
22
21
|
if (useDevServer) {
|
|
@@ -25,18 +24,22 @@ export async function createSsrMiddleware({ root = process.cwd(), getInitialData
|
|
|
25
24
|
server: { middlewareMode: true },
|
|
26
25
|
appType: 'custom',
|
|
27
26
|
}));
|
|
28
|
-
render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render;
|
|
29
27
|
middlewares.push(vite.middlewares);
|
|
30
28
|
}
|
|
31
29
|
else {
|
|
32
|
-
const result = await import(path.join(root, 'dist/server/entry-server.js'));
|
|
33
|
-
render = result.render;
|
|
34
30
|
assetLinks = await getAssetLinks(resolve('dist/client/.vite/manifest.json'), 'src/entry-client.tsx');
|
|
35
31
|
middlewares.push(express.static(resolve('dist/client'), { index: false }));
|
|
36
32
|
}
|
|
37
33
|
middlewares.push(route(async (req, res) => {
|
|
38
34
|
try {
|
|
39
35
|
const nonce = useCspNonce();
|
|
36
|
+
let render;
|
|
37
|
+
if (useDevServer) {
|
|
38
|
+
render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
render = (await import(path.join(root, 'dist/server/entry-server.js'))).render;
|
|
42
|
+
}
|
|
40
43
|
const rendered = await render(createFetchRequest(req, res), {
|
|
41
44
|
useDevServer,
|
|
42
45
|
getInitialData: getInitialData || (async () => ({})),
|
|
@@ -76,15 +79,17 @@ export async function createSsrMiddleware({ root = process.cwd(), getInitialData
|
|
|
76
79
|
return [200, html, { 'Content-type': 'text/html' }];
|
|
77
80
|
}
|
|
78
81
|
catch (error) {
|
|
82
|
+
/* v8 ignore next -- @preserve */
|
|
79
83
|
if (error instanceof Response &&
|
|
80
84
|
error.status >= 300 &&
|
|
81
85
|
error.status <= 399) {
|
|
82
86
|
return res.redirect(error.status, error.headers.get('Location') || '');
|
|
83
|
-
/* c8 ignore next 9 */
|
|
84
87
|
}
|
|
88
|
+
/* v8 ignore next -- @preserve */
|
|
85
89
|
if (useDevServer) {
|
|
86
90
|
vite.ssrFixStacktrace(error);
|
|
87
91
|
}
|
|
92
|
+
/* v8 ignore next -- @preserve */
|
|
88
93
|
throw error;
|
|
89
94
|
}
|
|
90
95
|
}));
|
|
@@ -104,24 +109,24 @@ export function replaceSsrPlaceholders(template, values) {
|
|
|
104
109
|
}
|
|
105
110
|
async function getAssetLinks(manifestPath, input, publicPath = '/') {
|
|
106
111
|
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf-8'));
|
|
107
|
-
/*
|
|
112
|
+
/* v8 ignore next -- @preserve */
|
|
108
113
|
if (!manifest[input]) {
|
|
109
114
|
throw Error('Entry not found in the manifest.json file');
|
|
110
115
|
}
|
|
111
116
|
const chunk = manifest[input];
|
|
112
|
-
/*
|
|
117
|
+
/* v8 ignore next -- @preserve */
|
|
113
118
|
const cssHtml = (chunk.css || [])
|
|
114
119
|
.map((href) => `<link rel="stylesheet" href="${publicPath}${href}" />`)
|
|
115
120
|
.join(' ');
|
|
116
121
|
// Generate HTML for preload links
|
|
117
122
|
const preloadList = [input];
|
|
118
|
-
/*
|
|
123
|
+
/* v8 ignore next -- @preserve */
|
|
119
124
|
if (Array.isArray(chunk.imports)) {
|
|
120
125
|
preloadList.push(...chunk.imports);
|
|
121
126
|
}
|
|
122
127
|
const preloadHtml = preloadList
|
|
123
128
|
.map((pre) => {
|
|
124
|
-
/*
|
|
129
|
+
/* v8 ignore next -- @preserve */
|
|
125
130
|
if (!manifest[pre]?.file) {
|
|
126
131
|
return '';
|
|
127
132
|
}
|
package/dist/vitest/matchers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {} from 'vitest';
|
|
2
2
|
import failOnConsole from 'vitest-fail-on-console';
|
|
3
3
|
export { failOnConsole };
|
|
4
|
-
/*
|
|
4
|
+
/* v8 ignore next -- @preserve */
|
|
5
5
|
export function extendMatchers(expect) {
|
|
6
6
|
expect.extend({
|
|
7
7
|
async toMatchResponse(received, expectedStatusCode, expectedBody) {
|
|
@@ -77,4 +77,3 @@ export function extendMatchers(expect) {
|
|
|
77
77
|
},
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
|
-
/* c8 ignore stop */
|
|
@@ -9,7 +9,7 @@ export async function createTestClient(server, options) {
|
|
|
9
9
|
server = createServer(server);
|
|
10
10
|
}
|
|
11
11
|
let address = server.address();
|
|
12
|
-
/*
|
|
12
|
+
/* v8 ignore next 5 -- @preserve */
|
|
13
13
|
if (typeof address === 'string') {
|
|
14
14
|
throw new Error(`Can't use a server listening on a UNIX domain socket: ${address}`);
|
|
15
15
|
}
|
package/dist/webapi.js
CHANGED
|
@@ -3,7 +3,7 @@ export function createFetchHeaders(requestHeaders) {
|
|
|
3
3
|
const headers = new Headers();
|
|
4
4
|
for (const [key, values] of Object.entries(requestHeaders)) {
|
|
5
5
|
if (values) {
|
|
6
|
-
/*
|
|
6
|
+
/* v8 ignore next -- @preserve */
|
|
7
7
|
if (Array.isArray(values)) {
|
|
8
8
|
for (const value of values) {
|
|
9
9
|
headers.append(key, value);
|
|
@@ -18,7 +18,7 @@ export function createFetchHeaders(requestHeaders) {
|
|
|
18
18
|
}
|
|
19
19
|
export function createFetchRequest(req, res) {
|
|
20
20
|
const origin = `${req.protocol}://${req.get('host')}`;
|
|
21
|
-
/*
|
|
21
|
+
/* v8 ignore next -- @preserve */
|
|
22
22
|
const url = new URL(req.originalUrl || req.url, origin);
|
|
23
23
|
let controller = new AbortController();
|
|
24
24
|
const init = {
|
|
@@ -26,10 +26,11 @@ export function createFetchRequest(req, res) {
|
|
|
26
26
|
headers: createFetchHeaders(req.headers),
|
|
27
27
|
signal: controller.signal,
|
|
28
28
|
};
|
|
29
|
-
/*
|
|
29
|
+
/* v8 ignore next -- @preserve */
|
|
30
30
|
res.on('finish', () => {
|
|
31
31
|
controller = null;
|
|
32
32
|
});
|
|
33
|
+
/* v8 ignore next -- @preserve */
|
|
33
34
|
res.on('close', () => {
|
|
34
35
|
controller?.abort();
|
|
35
36
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,43 +60,42 @@
|
|
|
60
60
|
],
|
|
61
61
|
"license": "ISC",
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@vitejs/plugin-react": "^5.0
|
|
64
|
-
"arktype": "^2.1.
|
|
63
|
+
"@vitejs/plugin-react": "^5.1.0",
|
|
64
|
+
"arktype": "^2.1.25",
|
|
65
65
|
"faktory-worker": "^4.7.1",
|
|
66
66
|
"react": "^19.2.0",
|
|
67
67
|
"react-dom": "^19.2.0",
|
|
68
|
-
"vite": "^7.1.
|
|
68
|
+
"vite": "^7.1.12"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"@devmoods/config": "0.2.0",
|
|
72
72
|
"@devmoods/fetch": "^4.2.0",
|
|
73
73
|
"@devmoods/observability": "0.1.1",
|
|
74
|
-
"@devmoods/postgres": "0.2.
|
|
75
|
-
"@hey-api/openapi-ts": "^0.86.
|
|
76
|
-
"@sentry/node": "^10.
|
|
77
|
-
"@sentry/node-native": "^10.
|
|
78
|
-
"@sentry/profiling-node": "^10.
|
|
74
|
+
"@devmoods/postgres": "0.2.2",
|
|
75
|
+
"@hey-api/openapi-ts": "^0.86.11",
|
|
76
|
+
"@sentry/node": "^10.22.0",
|
|
77
|
+
"@sentry/node-native": "^10.22.0",
|
|
78
|
+
"@sentry/profiling-node": "^10.22.0",
|
|
79
79
|
"@standard-schema/spec": "^1.0.0",
|
|
80
80
|
"@types/bcrypt": "^6.0.0",
|
|
81
|
-
"@types/cookie-parser": "^1.4.
|
|
82
|
-
"@types/express": "^5.0.
|
|
83
|
-
"@types/
|
|
84
|
-
"@types/
|
|
85
|
-
"@types/nodemailer": "^7.0.2",
|
|
81
|
+
"@types/cookie-parser": "^1.4.10",
|
|
82
|
+
"@types/express": "^5.0.5",
|
|
83
|
+
"@types/koa-compose": "^3.2.9",
|
|
84
|
+
"@types/nodemailer": "^7.0.3",
|
|
86
85
|
"@types/qs": "^6.14.0",
|
|
87
86
|
"ajv": "^8.17.1",
|
|
88
87
|
"argon2": "^0.44.0",
|
|
89
88
|
"bcrypt": "^6.0.0",
|
|
90
89
|
"chalk": "^5.6.2",
|
|
91
|
-
"commander": "^14.0.
|
|
90
|
+
"commander": "^14.0.2",
|
|
92
91
|
"cookie-parser": "^1.4.7",
|
|
93
92
|
"dotenv": "^17.2.3",
|
|
94
93
|
"express": "^4.21.2",
|
|
95
|
-
"google-auth-library": "^10.
|
|
94
|
+
"google-auth-library": "^10.5.0",
|
|
96
95
|
"helmet": "^8.1.0",
|
|
97
|
-
"
|
|
98
|
-
"nodemailer": "^7.0.
|
|
99
|
-
"redis": "^5.
|
|
96
|
+
"jose": "^6.1.0",
|
|
97
|
+
"nodemailer": "^7.0.10",
|
|
98
|
+
"redis": "^5.9.0",
|
|
100
99
|
"sonda": "^0.9.0",
|
|
101
100
|
"tough-cookie": "^6.0.0",
|
|
102
101
|
"uuid": "^13.0.0",
|
|
@@ -112,5 +111,5 @@
|
|
|
112
111
|
"engines": {
|
|
113
112
|
"node": ">=20 <25"
|
|
114
113
|
},
|
|
115
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "eb2f12e0a86234754ebf2977be7f4a14ccf8bed3"
|
|
116
115
|
}
|