@devmoods/express-extras 0.72.5 → 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 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, NotFound, Unauthorized } from './errors.js';
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,29 +77,18 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
77
77
  const user = useCurrentUser();
78
78
  return filterUser(user);
79
79
  });
80
- router.get('/logout', {}, async (req, res) => {
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 (req) => {
94
- const { token } = req.params;
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);
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
@@ -19,7 +19,6 @@ export * from './rate-limit.js';
19
19
  export * from './redis.js';
20
20
  export * from './router.js';
21
21
  export * from './routes.js';
22
- export * from './sentry.js';
23
22
  export * from './slack.js';
24
23
  export * from './utils.js';
25
24
  export * from './validate.js';
package/dist/index.js CHANGED
@@ -20,7 +20,6 @@ export * from './rate-limit.js';
20
20
  export * from './redis.js';
21
21
  export * from './router.js';
22
22
  export * from './routes.js';
23
- export * from './sentry.js';
24
23
  export * from './slack.js';
25
24
  export * from './utils.js';
26
25
  export * from './validate.js';
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
@@ -126,6 +126,7 @@ export function createExpressServer(app, options = {}) {
126
126
  duration: timer.getDuration(),
127
127
  gracefully,
128
128
  });
129
+ await options.afterShutdown?.();
129
130
  process.exit(0);
130
131
  }
131
132
  catch (error) {
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 * as Sentry from '@sentry/node';
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
- export function createLogger({ name, getContext, level = config.LOG_LEVEL, outputs = [new JsonStreamAdapter(process.stdout), new SentryLoggerAdapter()], }) {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/express-extras",
3
- "version": "0.72.5",
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",
@@ -70,13 +70,10 @@
70
70
  "dependencies": {
71
71
  "@devmoods/config": "0.2.3",
72
72
  "@devmoods/fetch": "^4.3.0",
73
- "@devmoods/observability": "0.1.4",
74
- "@devmoods/postgres": "0.4.2",
75
- "@hey-api/openapi-ts": "^0.95.0",
76
- "@rolldown/plugin-babel": "^0.2.2",
77
- "@sentry/node": "^10.48.0",
78
- "@sentry/node-native": "^10.48.0",
79
- "@sentry/profiling-node": "^10.48.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.1",
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",
@@ -113,5 +110,5 @@
113
110
  "engines": {
114
111
  "node": ">=20 <26"
115
112
  },
116
- "gitHead": "e29bcf8284033887f836a8ca7486b2b80e00852e"
113
+ "gitHead": "ea4f51756aca98bd050cf98128bf4d4b3da1bf38"
117
114
  }
package/dist/sentry.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import type * as Sentry from '@sentry/node';
2
- export type { Sentry };
3
- export interface SentryOptions extends Sentry.NodeOptions {
4
- }
5
- export declare function sentryOptions(options?: SentryOptions): Promise<SentryOptions>;
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
- profileSessionSampleRate: config.SENTRY_PROFILES_SAMPLE_RATE,
34
- integrations: nativeIntegrations,
35
- enableLogs: true,
36
- ...options,
37
- };
38
- }