@devmoods/express-extras 0.72.1 → 0.73.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.js +5 -16
- package/dist/config.js +1 -1
- package/dist/errors.d.ts +0 -2
- package/dist/errors.js +0 -11
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/listen.d.ts +1 -0
- package/dist/listen.js +1 -0
- package/dist/logger.d.ts +3 -9
- package/dist/logger.js +14 -26
- package/dist/vite/ssr.js +14 -4
- package/package.json +9 -12
- package/dist/sentry.d.ts +0 -5
- package/dist/sentry.js +0 -38
package/dist/auth.js
CHANGED
|
@@ -5,7 +5,7 @@ import { InvalidToken, PermissionDenied } from './auth/errors.js';
|
|
|
5
5
|
import { createAccessToken, decodeAccessToken, extractAccessTokenFromRequest, setAccessTokenCookie, tokenSchema, } from './auth/token.js';
|
|
6
6
|
import {} from './auth/types.js';
|
|
7
7
|
import { setRequestContext, useRequestContext } from './context.js';
|
|
8
|
-
import { Forbidden,
|
|
8
|
+
import { Forbidden, Unauthorized } from './errors.js';
|
|
9
9
|
import { getLogger } from './logger.js';
|
|
10
10
|
import { createRouter } from './router.js';
|
|
11
11
|
import { jsonSchema } from './validate.js';
|
|
@@ -77,33 +77,22 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
|
|
|
77
77
|
const user = useCurrentUser();
|
|
78
78
|
return filterUser(user);
|
|
79
79
|
});
|
|
80
|
-
router.
|
|
80
|
+
router.post('/logout', {}, async (req, res) => {
|
|
81
81
|
await logout(req, res);
|
|
82
82
|
res.redirect(loginUrl);
|
|
83
83
|
});
|
|
84
|
-
/**
|
|
85
|
-
* Check that a reset token is valid.
|
|
86
|
-
*/
|
|
87
84
|
router.get('/reset/:token', {
|
|
88
85
|
params: jsonSchema({
|
|
89
86
|
type: 'object',
|
|
90
87
|
properties: { token: tokenSchema },
|
|
91
88
|
required: ['token'],
|
|
92
89
|
}),
|
|
93
|
-
}, async (
|
|
94
|
-
|
|
95
|
-
const value = await tokenStorage.get(token);
|
|
96
|
-
if (!value) {
|
|
97
|
-
logger.info('invalid reset token', { token });
|
|
98
|
-
throw NotFound();
|
|
99
|
-
}
|
|
100
|
-
return 200;
|
|
101
|
-
});
|
|
102
|
-
router.get('/token/refresh', {}, middleware(false), async (req, res) => {
|
|
90
|
+
}, async () => 200);
|
|
91
|
+
router.post('/token/refresh', {}, middleware(false), async (req, res) => {
|
|
103
92
|
const user = useCurrentUser();
|
|
104
93
|
if (!user) {
|
|
105
94
|
setAccessTokenCookie(res, null);
|
|
106
|
-
|
|
95
|
+
return { token: null, user: null };
|
|
107
96
|
}
|
|
108
97
|
const { token } = await providerContext.login(req, res, user, true);
|
|
109
98
|
logger.info('refreshed token');
|
package/dist/config.js
CHANGED
|
@@ -14,7 +14,7 @@ const app = define({
|
|
|
14
14
|
SENTRY_DSN: t.string(),
|
|
15
15
|
SENTRY_TRACES_SAMPLE_RATE: t.number(0.3),
|
|
16
16
|
SENTRY_PROFILES_SAMPLE_RATE: t.number(0.3),
|
|
17
|
-
SENTRY_EVENT_LOOP_THRESHOLD: t.number(
|
|
17
|
+
SENTRY_EVENT_LOOP_THRESHOLD: t.number(750),
|
|
18
18
|
LOG_LEVEL: t.string(),
|
|
19
19
|
LOG_SQL: t.boolean(),
|
|
20
20
|
NODE_ENV: t.string(),
|
package/dist/errors.d.ts
CHANGED
|
@@ -35,8 +35,6 @@ export interface ErrorHandlerOptions {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Generic error handler for JSON-based apps.
|
|
38
|
-
*
|
|
39
|
-
* Composed together with the Sentry error handler.
|
|
40
38
|
*/
|
|
41
39
|
export declare function errorHandler({ onError }?: ErrorHandlerOptions): ErrorHandler<any, any, void>;
|
|
42
40
|
export {};
|
package/dist/errors.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { STATUS_CODES } from 'node:http';
|
|
2
2
|
import { DoesNotExistError, IntegrityError } from '@devmoods/postgres';
|
|
3
|
-
import * as Sentry from '@sentry/node';
|
|
4
3
|
import {} from 'express';
|
|
5
4
|
import { InvalidToken } from './auth/errors.js';
|
|
6
5
|
import { errors } from './composeMiddleware.js';
|
|
@@ -66,8 +65,6 @@ function _errorHandler({ onError }) {
|
|
|
66
65
|
}
|
|
67
66
|
/**
|
|
68
67
|
* Generic error handler for JSON-based apps.
|
|
69
|
-
*
|
|
70
|
-
* Composed together with the Sentry error handler.
|
|
71
68
|
*/
|
|
72
69
|
export function errorHandler({ onError } = {}) {
|
|
73
70
|
const logger = getLogger();
|
|
@@ -82,14 +79,6 @@ export function errorHandler({ onError } = {}) {
|
|
|
82
79
|
}
|
|
83
80
|
};
|
|
84
81
|
return errors([
|
|
85
|
-
Sentry.expressErrorHandler({
|
|
86
|
-
shouldHandleError(error) {
|
|
87
|
-
if (error instanceof ApiError && error.status < 500) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
return process.env.NODE_ENV === 'production';
|
|
91
|
-
},
|
|
92
|
-
}),
|
|
93
82
|
_errorHandler({
|
|
94
83
|
onError: onError || _defaultErrorHandler,
|
|
95
84
|
}),
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/listen.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface ExpressServerOptions {
|
|
|
14
14
|
onStart?(): Promise<void> | void;
|
|
15
15
|
onShutdown?(): Promise<void> | void;
|
|
16
16
|
beforeShutdown?(): Promise<void> | void;
|
|
17
|
+
afterShutdown?(): Promise<void> | void;
|
|
17
18
|
healthCheck?(type?: 'live' | 'ready'): Promise<unknown>;
|
|
18
19
|
gracePeriodMs?: number;
|
|
19
20
|
}
|
package/dist/listen.js
CHANGED
package/dist/logger.d.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { type Writable } from 'node:stream';
|
|
2
|
+
import { type LoggerAdapter } from '@devmoods/observability';
|
|
2
3
|
export type LoggerContext = Record<string, any>;
|
|
3
|
-
export interface LoggerAdapter {
|
|
4
|
-
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
5
|
-
write(chunk: Record<string, any>): boolean;
|
|
6
|
-
}
|
|
7
4
|
export declare class JsonStreamAdapter implements LoggerAdapter {
|
|
8
5
|
#private;
|
|
9
6
|
constructor(stream: Writable);
|
|
10
7
|
captureError(error: Error, attributes?: Record<string, any>): void;
|
|
11
8
|
write(log: Record<string, any>): boolean;
|
|
12
9
|
}
|
|
13
|
-
export declare class SentryLoggerAdapter implements LoggerAdapter {
|
|
14
|
-
constructor();
|
|
15
|
-
captureError(error: Error | null, attributes?: Record<string, any>): void;
|
|
16
|
-
write(log: Record<string, any>): boolean;
|
|
17
|
-
}
|
|
18
10
|
export interface LoggerOptions {
|
|
19
11
|
/**
|
|
20
12
|
* Name of the logger
|
|
@@ -36,6 +28,8 @@ export interface LoggerOptions {
|
|
|
36
28
|
export declare function setGlobalLoggerContext(getContext: () => LoggerContext): void;
|
|
37
29
|
export declare function clearGlobalLoggerContext(): void;
|
|
38
30
|
export type Logger = ReturnType<typeof createLogger>;
|
|
31
|
+
export declare function addDefaultLoggerOutput(output: LoggerAdapter): void;
|
|
32
|
+
export declare function clearDefaultLoggerOutputs(): void;
|
|
39
33
|
export declare function createLogger({ name, getContext, level, outputs }: LoggerOptions): {
|
|
40
34
|
debug(message: string, context?: LoggerContext): void;
|
|
41
35
|
info(message: string, context?: LoggerContext): void;
|
package/dist/logger.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as os from 'node:os';
|
|
2
2
|
import {} from 'node:stream';
|
|
3
3
|
import { config } from '@devmoods/config';
|
|
4
|
-
import
|
|
4
|
+
import {} from '@devmoods/observability';
|
|
5
5
|
export class JsonStreamAdapter {
|
|
6
6
|
#stream;
|
|
7
7
|
constructor(stream) {
|
|
@@ -14,30 +14,6 @@ export class JsonStreamAdapter {
|
|
|
14
14
|
return this.#stream.write(chunk);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
export class SentryLoggerAdapter {
|
|
18
|
-
constructor() { }
|
|
19
|
-
captureError(error, attributes) {
|
|
20
|
-
/* v8 ignore next -- @preserve */
|
|
21
|
-
if (error == null) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
/* v8 ignore next -- @preserve */
|
|
25
|
-
if (Sentry.isInitialized()) {
|
|
26
|
-
Sentry.captureException(error, {
|
|
27
|
-
tags: attributes,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
write(log) {
|
|
32
|
-
const { message, ...attributes } = log;
|
|
33
|
-
/* v8 ignore next -- @preserve */
|
|
34
|
-
if (Sentry.isInitialized()) {
|
|
35
|
-
const fn = log.level;
|
|
36
|
-
Sentry.logger[fn](message, attributes);
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
17
|
const LEVELS = {
|
|
42
18
|
TRACE: 10,
|
|
43
19
|
DEBUG: 20,
|
|
@@ -73,7 +49,19 @@ function baseContext() {
|
|
|
73
49
|
}
|
|
74
50
|
return globalContext;
|
|
75
51
|
}
|
|
76
|
-
|
|
52
|
+
const defaultLoggerOutputs = [
|
|
53
|
+
new JsonStreamAdapter(process.stdout),
|
|
54
|
+
];
|
|
55
|
+
export function addDefaultLoggerOutput(output) {
|
|
56
|
+
if (defaultLoggerOutputs.includes(output)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
defaultLoggerOutputs.push(output);
|
|
60
|
+
}
|
|
61
|
+
export function clearDefaultLoggerOutputs() {
|
|
62
|
+
defaultLoggerOutputs.length = 0;
|
|
63
|
+
}
|
|
64
|
+
export function createLogger({ name, getContext, level = config.LOG_LEVEL, outputs = defaultLoggerOutputs, }) {
|
|
77
65
|
if (_loggers.has(name)) {
|
|
78
66
|
throw new Error(`A logger named ${name} already exists`);
|
|
79
67
|
}
|
package/dist/vite/ssr.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
1
9
|
import * as fs from 'node:fs/promises';
|
|
2
10
|
import * as path from 'node:path';
|
|
3
11
|
import { config } from '@devmoods/config';
|
|
@@ -38,7 +46,7 @@ export async function createSsrMiddleware({ root = process.cwd(), getInitialData
|
|
|
38
46
|
render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render;
|
|
39
47
|
}
|
|
40
48
|
else {
|
|
41
|
-
render = (await import(path.join(root, 'dist/server/entry-server.js'))).render;
|
|
49
|
+
render = (await import(__rewriteRelativeImportExtension(path.join(root, 'dist/server/entry-server.js')))).render;
|
|
42
50
|
}
|
|
43
51
|
const rendered = await render(createFetchRequest(req, res), {
|
|
44
52
|
useDevServer,
|
|
@@ -117,15 +125,17 @@ async function getAssetLinks(manifestPath, input, publicPath = '/') {
|
|
|
117
125
|
// Collect all transitively imported chunk keys so we can gather their CSS too
|
|
118
126
|
const visited = new Set();
|
|
119
127
|
const collectImports = (key) => {
|
|
128
|
+
/* c8 ignore start */
|
|
120
129
|
if (visited.has(key)) {
|
|
121
130
|
return;
|
|
122
131
|
}
|
|
132
|
+
/* c8 ignore end */
|
|
123
133
|
visited.add(key);
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
134
|
+
const manifestValue = manifest[key];
|
|
135
|
+
if (!manifestValue) {
|
|
126
136
|
return;
|
|
127
137
|
}
|
|
128
|
-
for (const importKey of
|
|
138
|
+
for (const importKey of manifestValue.imports ?? []) {
|
|
129
139
|
collectImports(importKey);
|
|
130
140
|
}
|
|
131
141
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,15 +68,12 @@
|
|
|
68
68
|
"vite": "^8.0.8"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@devmoods/config": "0.2.
|
|
71
|
+
"@devmoods/config": "0.2.3",
|
|
72
72
|
"@devmoods/fetch": "^4.3.0",
|
|
73
|
-
"@devmoods/observability": "0.
|
|
74
|
-
"@devmoods/postgres": "0.
|
|
75
|
-
"@hey-api/openapi-ts": "^0.
|
|
76
|
-
"@rolldown/plugin-babel": "^0.2.
|
|
77
|
-
"@sentry/node": "^10.47.0",
|
|
78
|
-
"@sentry/node-native": "^10.47.0",
|
|
79
|
-
"@sentry/profiling-node": "^10.47.0",
|
|
73
|
+
"@devmoods/observability": "0.2.0",
|
|
74
|
+
"@devmoods/postgres": "0.5.0",
|
|
75
|
+
"@hey-api/openapi-ts": "^0.96.0",
|
|
76
|
+
"@rolldown/plugin-babel": "^0.2.3",
|
|
80
77
|
"@standard-schema/spec": "^1.1.0",
|
|
81
78
|
"@types/bcrypt": "^6.0.0",
|
|
82
79
|
"@types/cookie-parser": "^1.4.10",
|
|
@@ -91,7 +88,7 @@
|
|
|
91
88
|
"chalk": "^5.6.2",
|
|
92
89
|
"commander": "^14.0.3",
|
|
93
90
|
"cookie-parser": "^1.4.7",
|
|
94
|
-
"dotenv": "^17.4.
|
|
91
|
+
"dotenv": "^17.4.2",
|
|
95
92
|
"express": "^4.21.2",
|
|
96
93
|
"google-auth-library": "^10.6.2",
|
|
97
94
|
"helmet": "^8.1.0",
|
|
@@ -104,7 +101,7 @@
|
|
|
104
101
|
"vitest-fail-on-console": "^0.10.1"
|
|
105
102
|
},
|
|
106
103
|
"peerDependencies": {
|
|
107
|
-
"@vitejs/plugin-react": ">=
|
|
104
|
+
"@vitejs/plugin-react": ">=6.0.1",
|
|
108
105
|
"arktype": ">=2.0.0",
|
|
109
106
|
"express": ">=4.21.2 <5.0.0",
|
|
110
107
|
"faktory-worker": ">=4.5.1",
|
|
@@ -113,5 +110,5 @@
|
|
|
113
110
|
"engines": {
|
|
114
111
|
"node": ">=20 <26"
|
|
115
112
|
},
|
|
116
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "ea4f51756aca98bd050cf98128bf4d4b3da1bf38"
|
|
117
114
|
}
|
package/dist/sentry.d.ts
DELETED
package/dist/sentry.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { config } from '@devmoods/config';
|
|
2
|
-
/**
|
|
3
|
-
* Safely imports native Sentry integrations if available.
|
|
4
|
-
* Returns null if native modules are not available (e.g., on unsupported Node.js versions).
|
|
5
|
-
*/
|
|
6
|
-
async function getNativeIntegrations() {
|
|
7
|
-
const integrations = [];
|
|
8
|
-
/* v8 ignore next -- @preserve */
|
|
9
|
-
try {
|
|
10
|
-
const { nodeProfilingIntegration } = await import('@sentry/profiling-node');
|
|
11
|
-
integrations.push(nodeProfilingIntegration());
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
// Native profiling integration not available
|
|
15
|
-
}
|
|
16
|
-
/* v8 ignore next -- @preserve */
|
|
17
|
-
try {
|
|
18
|
-
const { eventLoopBlockIntegration } = await import('@sentry/node-native');
|
|
19
|
-
integrations.push(eventLoopBlockIntegration({
|
|
20
|
-
threshold: config.SENTRY_EVENT_LOOP_THRESHOLD,
|
|
21
|
-
}));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
// Native event loop integration not available
|
|
25
|
-
}
|
|
26
|
-
return integrations;
|
|
27
|
-
}
|
|
28
|
-
export async function sentryOptions(options = {}) {
|
|
29
|
-
const nativeIntegrations = await getNativeIntegrations();
|
|
30
|
-
return {
|
|
31
|
-
dsn: config.SENTRY_DSN,
|
|
32
|
-
tracesSampleRate: config.SENTRY_TRACES_SAMPLE_RATE,
|
|
33
|
-
profilesSampleRate: config.SENTRY_PROFILES_SAMPLE_RATE,
|
|
34
|
-
integrations: nativeIntegrations,
|
|
35
|
-
enableLogs: true,
|
|
36
|
-
...options,
|
|
37
|
-
};
|
|
38
|
-
}
|