@devmoods/express-extras 0.68.0 → 0.70.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.
@@ -56,8 +56,9 @@ export function EmailAndPasswordAuthProvider(options) {
56
56
  }
57
57
  context.setCurrentUser(user);
58
58
  await options.onPasswordChanged?.(user, await hashPassword(password), {
59
- /* c8 ignore next 2 */
59
+ /* v8 ignore next -- @preserve */
60
60
  ip: req.ip ?? '',
61
+ /* v8 ignore next -- @preserve */
61
62
  userAgent: req.headers['user-agent'] ?? '',
62
63
  });
63
64
  logger.info('user changed password');
@@ -10,10 +10,11 @@ export function PasswordlessAuthProvider(options) {
10
10
  return await options.getUserByEmail(email);
11
11
  }
12
12
  catch (error) {
13
+ /* v8 ignore next -- @preserve */
13
14
  if (error instanceof DoesNotExistError) {
14
15
  return null;
15
- /* c8 ignore next 3 */
16
16
  }
17
+ /* v8 ignore next -- @preserve */
17
18
  throw error;
18
19
  }
19
20
  };
@@ -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
@@ -29,8 +29,9 @@ export function createAuth({ getUserById, filterUser, tokenStorage, providers, l
29
29
  setCurrentUser(user);
30
30
  if (!isRefresh) {
31
31
  await onUserLoggedIn?.(user, {
32
- /* c8 ignore next 2 */
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
- /* c8 ignore start */
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
@@ -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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
177
+ /* v8 ignore next -- @preserve */
176
178
  next(error);
177
179
  }
178
180
  };
179
181
  }
180
- function protect(isAuthorized = () => true) {
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 5 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
@@ -36,6 +36,7 @@ export class CLI {
36
36
  }
37
37
  catch (error) {
38
38
  this.redLine(error.message);
39
+ /* v8 ignore next -- @preserve */
39
40
  if (this.options.printStackTraces) {
40
41
  this.writeLine('\nStack trace:\n', stderr);
41
42
  this.redLine(error.stack);
@@ -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
- /* c8 ignore start */
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
- /* c8 ignore start */
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
@@ -30,6 +30,7 @@ export function useRequestContext(key) {
30
30
  */
31
31
  export function setRequestContext(key, value) {
32
32
  const store = requestContext.getStore();
33
+ /* v8 ignore next -- @preserve */
33
34
  if (store) {
34
35
  store.set(key, value);
35
36
  }
package/dist/csrf.js CHANGED
@@ -11,7 +11,7 @@ const defaultCookieName = () => {
11
11
  };
12
12
  export const getCsrfSecret = () => {
13
13
  const secret = config.CSRF_SECRET;
14
- /* c8 ignore next 3 */
14
+ /* v8 ignore next -- @preserve */
15
15
  if (!secret) {
16
16
  throw new Error('CSRF_SECRET is not set');
17
17
  }
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
- /* c8 ignore start */
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
- /* c8 ignore next 1 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore start */
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
- /* c8 ignore start */
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
- /* c8 ignore next 1 */
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;
@@ -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
- /* c8 ignore next 1 */
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
- /* c8 ignore next 5 */
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 if (typeof result !== 'undefined') {
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
  };
@@ -48,6 +48,7 @@ getKey = (req) => req.ip || 'unknown', } = {}) {
48
48
  });
49
49
  cursor = result.cursor;
50
50
  const keys = result.keys;
51
+ /* v8 ignore next -- @preserve */
51
52
  if (keys.length > 0) {
52
53
  // Use UNLINK instead of DEL - asynchronous deletion
53
54
  await redis.unlink(keys);
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 1 */
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
- ? /* c8 ignore next 5*/
32
- error.issues.map((issue) => [
33
- String(issue.path ?? '/'),
34
- issue.message,
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 4 */
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
- /* c8 ignore next 4 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
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
- /* c8 ignore next 3 */
129
+ /* v8 ignore next -- @preserve */
125
130
  if (!manifest[pre]?.file) {
126
131
  return '';
127
132
  }
@@ -1,7 +1,7 @@
1
1
  import {} from 'vitest';
2
2
  import failOnConsole from 'vitest-fail-on-console';
3
3
  export { failOnConsole };
4
- /* c8 ignore start */
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
- /* c8 ignore next 5 */
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
- /* c8 ignore next 4 */
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
- /* c8 ignore next 1 */
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
- /* c8 ignore next 6 */
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.68.0",
3
+ "version": "0.70.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,43 @@
60
60
  ],
61
61
  "license": "ISC",
62
62
  "devDependencies": {
63
- "@vitejs/plugin-react": "^5.0.4",
64
- "arktype": "^2.1.23",
63
+ "@vitejs/plugin-react": "^5.1.1",
64
+ "arktype": "^2.1.26",
65
65
  "faktory-worker": "^4.7.1",
66
66
  "react": "^19.2.0",
67
67
  "react-dom": "^19.2.0",
68
- "vite": "^7.1.11"
68
+ "vite": "^7.2.2"
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.1",
75
- "@hey-api/openapi-ts": "^0.86.1",
76
- "@sentry/node": "^10.21.0",
77
- "@sentry/node-native": "^10.21.0",
78
- "@sentry/profiling-node": "^10.21.0",
74
+ "@devmoods/postgres": "0.2.2",
75
+ "@hey-api/openapi-ts": "^0.87.2",
76
+ "@sentry/node": "^10.25.0",
77
+ "@sentry/node-native": "^10.25.0",
78
+ "@sentry/profiling-node": "^10.25.0",
79
79
  "@standard-schema/spec": "^1.0.0",
80
80
  "@types/bcrypt": "^6.0.0",
81
- "@types/cookie-parser": "^1.4.9",
82
- "@types/express": "^5.0.3",
83
- "@types/koa-compose": "^3.2.8",
84
- "@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",
85
85
  "@types/qs": "^6.14.0",
86
86
  "ajv": "^8.17.1",
87
87
  "argon2": "^0.44.0",
88
88
  "bcrypt": "^6.0.0",
89
89
  "chalk": "^5.6.2",
90
- "commander": "^14.0.1",
90
+ "commander": "^14.0.2",
91
91
  "cookie-parser": "^1.4.7",
92
92
  "dotenv": "^17.2.3",
93
93
  "express": "^4.21.2",
94
- "google-auth-library": "^10.4.1",
94
+ "google-auth-library": "^10.5.0",
95
95
  "helmet": "^8.1.0",
96
- "jose": "^6.1.0",
97
- "nodemailer": "^7.0.9",
98
- "redis": "^5.8.3",
99
- "sonda": "^0.9.0",
96
+ "jose": "^6.1.1",
97
+ "nodemailer": "^7.0.10",
98
+ "redis": "^5.9.0",
99
+ "sonda": "^0.10.0",
100
100
  "tough-cookie": "^6.0.0",
101
101
  "uuid": "^13.0.0",
102
102
  "vitest-fail-on-console": "^0.10.1"
@@ -111,5 +111,5 @@
111
111
  "engines": {
112
112
  "node": ">=20 <25"
113
113
  },
114
- "gitHead": "721a709c087904a5cc1332d28a62857a82295fea"
114
+ "gitHead": "a3791c0224fe3ee0ab017dc62c3c8b9d85364c15"
115
115
  }