@veloxts/core 0.7.1 → 0.7.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @veloxts/core
2
2
 
3
+ ## 0.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(cli): auto-populate Zod schemas from Prisma model fields
8
+
9
+ ## 0.7.2
10
+
11
+ ### Patch Changes
12
+
13
+ - fix(core): type error handler generic as Error to fix IDE null warning
14
+ - chore(core): mark `velox` as deprecated in favor of `veloxApp`
15
+ - simplify code for clarity and maintainability
16
+
3
17
  ## 0.7.1
4
18
 
5
19
  ### Patch Changes
package/dist/app.d.ts CHANGED
@@ -79,20 +79,12 @@ export declare class VeloxApp {
79
79
  */
80
80
  get address(): string | null;
81
81
  /**
82
- * Async initialization (called by factory function)
83
- *
84
- * @internal - This method is public for factory access but should not be called directly.
85
- * Use createVeloxApp() instead.
86
- */
87
- initialize(): Promise<void>;
88
- /**
89
- * Sets up request context decorator
90
- *
91
- * Adds `request.context` property to all requests via onRequest hook
82
+ * Async initialization hook for factory function.
83
+ * Empty because Fastify's ready() must be called in start() after plugin registration.
92
84
  *
93
85
  * @internal
94
86
  */
95
- private _setupContext;
87
+ initialize(): Promise<void>;
96
88
  /**
97
89
  * Sets up global error handling
98
90
  *
@@ -101,12 +93,6 @@ export declare class VeloxApp {
101
93
  * @internal
102
94
  */
103
95
  private _setupErrorHandling;
104
- /**
105
- * Sets up graceful shutdown handlers for process signals
106
- *
107
- * @internal
108
- */
109
- private _setupGracefulShutdown;
110
96
  /**
111
97
  * Registers a plugin with the application
112
98
  *
@@ -293,5 +279,6 @@ export declare function veloxApp(config?: VeloxAppConfig): Promise<VeloxApp>;
293
279
  *
294
280
  * const app = await velox({ port: 3030 });
295
281
  * ```
282
+ * @deprecated Use veloxApp() instead.
296
283
  */
297
284
  export declare const velox: typeof veloxApp;
package/dist/app.js CHANGED
@@ -48,25 +48,18 @@ export class VeloxApp {
48
48
  * @internal
49
49
  */
50
50
  constructor(config) {
51
- // Merge user config with defaults and validate
52
51
  const merged = mergeConfig(config);
53
- // Validate and freeze configuration
54
52
  this._config = validateConfig(merged);
55
- // Create Fastify instance
56
- const fastifyOptions = {
53
+ this._server = fastify({
57
54
  logger: this._config.logger,
58
55
  ...this._config.fastify,
59
- };
60
- this._server = fastify(fastifyOptions);
61
- // Initialize lifecycle manager
56
+ });
62
57
  this._lifecycle = new LifecycleManager();
63
- // Set up context decorator
64
- this._setupContext();
65
- // Set up error handling
58
+ setupContextHook(this._server);
66
59
  this._setupErrorHandling();
67
- // Set up graceful shutdown
68
- this._setupGracefulShutdown();
69
- // Register request logger if enabled via environment
60
+ this._lifecycle.setupSignalHandlers(async () => {
61
+ await this.stop();
62
+ });
70
63
  if (process.env.VELOX_REQUEST_LOGGING === 'true') {
71
64
  this._server.register(requestLogger);
72
65
  }
@@ -110,24 +103,13 @@ export class VeloxApp {
110
103
  return this._address;
111
104
  }
112
105
  /**
113
- * Async initialization (called by factory function)
114
- *
115
- * @internal - This method is public for factory access but should not be called directly.
116
- * Use createVeloxApp() instead.
117
- */
118
- async initialize() {
119
- // Keep empty - ready() must be called in start() after plugins are registered
120
- // This is a Fastify constraint: plugins must be registered before ready()
121
- }
122
- /**
123
- * Sets up request context decorator
124
- *
125
- * Adds `request.context` property to all requests via onRequest hook
106
+ * Async initialization hook for factory function.
107
+ * Empty because Fastify's ready() must be called in start() after plugin registration.
126
108
  *
127
109
  * @internal
128
110
  */
129
- _setupContext() {
130
- setupContextHook(this._server);
111
+ async initialize() {
112
+ // Intentionally empty - ready() is called in start()
131
113
  }
132
114
  /**
133
115
  * Sets up global error handling
@@ -139,8 +121,7 @@ export class VeloxApp {
139
121
  _setupErrorHandling() {
140
122
  this._server.setErrorHandler(async (error, request, reply) => {
141
123
  try {
142
- // Handle ZodError (validation errors) - return 400
143
- if (error instanceof Error && error.name === 'ZodError' && 'issues' in error) {
124
+ if (error.name === 'ZodError' && 'issues' in error) {
144
125
  const zodError = error;
145
126
  return reply.status(400).send({
146
127
  error: 'ValidationError',
@@ -152,9 +133,7 @@ export class VeloxApp {
152
133
  })),
153
134
  });
154
135
  }
155
- // Handle Prisma unique constraint errors - return 409 Conflict
156
- if (error instanceof Error &&
157
- error.name === 'PrismaClientKnownRequestError' &&
136
+ if (error.name === 'PrismaClientKnownRequestError' &&
158
137
  'code' in error &&
159
138
  error.code === 'P2002') {
160
139
  const prismaError = error;
@@ -165,22 +144,21 @@ export class VeloxApp {
165
144
  statusCode: 409,
166
145
  });
167
146
  }
168
- // Only log server errors (5xx), not client errors (4xx)
169
- const statusCode = isVeloxError(error)
170
- ? error.statusCode
171
- : typeof error === 'object' && error !== null && 'statusCode' in error
172
- ? error.statusCode
173
- : 500;
147
+ let statusCode = 500;
148
+ if (isVeloxError(error)) {
149
+ statusCode = error.statusCode;
150
+ }
151
+ else if (typeof error === 'object' && error !== null && 'statusCode' in error) {
152
+ statusCode = error.statusCode;
153
+ }
174
154
  if (statusCode >= 500) {
175
155
  request.log.error(error);
176
156
  }
177
- // Handle VeloxError instances (fast path - most common case)
178
157
  if (isVeloxError(error)) {
179
158
  return reply.status(error.statusCode).send(error.toJSON());
180
159
  }
181
- // Handle other errors
182
- const message = error instanceof Error ? error.message : 'Internal Server Error';
183
- const name = error instanceof Error ? error.name : 'Error';
160
+ const message = error.message ?? 'Internal Server Error';
161
+ const name = error.name ?? 'Error';
184
162
  return reply.status(statusCode).send({
185
163
  error: name,
186
164
  message,
@@ -188,7 +166,6 @@ export class VeloxApp {
188
166
  });
189
167
  }
190
168
  catch (handlerError) {
191
- // Last resort error handling - prevents unhandled rejections
192
169
  log.error('Critical error in error handler:', handlerError);
193
170
  if (!reply.sent) {
194
171
  return reply.status(500).send({
@@ -200,16 +177,6 @@ export class VeloxApp {
200
177
  }
201
178
  });
202
179
  }
203
- /**
204
- * Sets up graceful shutdown handlers for process signals
205
- *
206
- * @internal
207
- */
208
- _setupGracefulShutdown() {
209
- this._lifecycle.setupSignalHandlers(async () => {
210
- await this.stop();
211
- });
212
- }
213
180
  /**
214
181
  * Registers a plugin with the application
215
182
  *
@@ -239,17 +206,13 @@ export class VeloxApp {
239
206
  * ```
240
207
  */
241
208
  async register(plugin, options) {
242
- // Handle VeloxPlugin objects (with name, version, register)
243
209
  if (isVeloxPlugin(plugin)) {
244
- // Validate plugin metadata
245
210
  validatePluginMetadata(plugin);
246
- // Wrap plugin with fastify-plugin for proper encapsulation
247
211
  const wrappedPlugin = fp(plugin.register, {
248
212
  name: plugin.name,
249
213
  dependencies: plugin.dependencies,
250
214
  fastify: '5.x',
251
215
  });
252
- // Register with Fastify
253
216
  try {
254
217
  await this._server.register(wrappedPlugin, options ?? {});
255
218
  }
@@ -258,7 +221,6 @@ export class VeloxApp {
258
221
  }
259
222
  return;
260
223
  }
261
- // Handle FastifyPluginAsync functions (standard Fastify plugins)
262
224
  if (isFastifyPlugin(plugin)) {
263
225
  try {
264
226
  await this._server.register(plugin, options ?? {});
@@ -268,7 +230,6 @@ export class VeloxApp {
268
230
  }
269
231
  return;
270
232
  }
271
- // Invalid plugin type
272
233
  throw new VeloxError('Invalid plugin: must be a VeloxPlugin object or FastifyPluginAsync function', 500, 'INVALID_PLUGIN_TYPE');
273
234
  }
274
235
  /**
@@ -356,16 +317,13 @@ export class VeloxApp {
356
317
  }
357
318
  const startTime = performance.now();
358
319
  try {
359
- // Ensure Fastify is ready before listening (must be after plugin registration)
360
320
  await this._server.ready();
361
- // Start listening
362
321
  const address = await this._server.listen({
363
322
  port: this._config.port,
364
323
  host: this._config.host,
365
324
  });
366
325
  this._isRunning = true;
367
326
  this._address = address;
368
- // Print startup banner unless silent
369
327
  if (!options.silent) {
370
328
  printBanner(this._server, {
371
329
  address,
@@ -399,11 +357,8 @@ export class VeloxApp {
399
357
  throw new VeloxError('Server is not running', 500, 'SERVER_NOT_RUNNING');
400
358
  }
401
359
  try {
402
- // Execute shutdown handlers
403
360
  await this._lifecycle.executeShutdownHandlers();
404
- // Clean up signal handlers to prevent memory leaks in tests
405
361
  this._lifecycle.cleanupSignalHandlers();
406
- // Close server
407
362
  await this._server.close();
408
363
  this._isRunning = false;
409
364
  this._address = null;
@@ -485,5 +440,6 @@ export async function veloxApp(config = {}) {
485
440
  *
486
441
  * const app = await velox({ port: 3030 });
487
442
  * ```
443
+ * @deprecated Use veloxApp() instead.
488
444
  */
489
445
  export const velox = veloxApp;
package/dist/context.js CHANGED
@@ -38,17 +38,13 @@ export function createContext(request, reply) {
38
38
  * ```
39
39
  */
40
40
  export function isContext(value) {
41
- // Early return for non-objects
42
41
  if (typeof value !== 'object' || value === null) {
43
42
  return false;
44
43
  }
45
- // Check properties exist using 'in' operator
46
44
  if (!('request' in value) || !('reply' in value)) {
47
45
  return false;
48
46
  }
49
- // After 'in' checks, safely access properties
50
47
  const ctx = value;
51
- // Verify request and reply are non-null objects
52
48
  return (typeof ctx.request === 'object' &&
53
49
  ctx.request !== null &&
54
50
  typeof ctx.reply === 'object' &&
@@ -65,12 +61,7 @@ export function isContext(value) {
65
61
  * @internal
66
62
  */
67
63
  export function setupContextHook(server) {
68
- // Create context for each request via direct assignment
69
- // TypeScript's declaration merging provides type safety
70
64
  server.addHook('onRequest', async (request, reply) => {
71
- // Direct assignment is ~100-400ns faster than Object.defineProperty
72
- // We use a mutable type assertion here because this is the framework's
73
- // initialization code - the readonly constraint is for user code safety
74
65
  request.context = createContext(request, reply);
75
66
  });
76
67
  }
@@ -33,6 +33,17 @@ export type ErrorCode = keyof typeof ERROR_CATALOG;
33
33
  * Variables for template interpolation
34
34
  */
35
35
  export type InterpolationVars = Record<string, string | number | boolean | undefined>;
36
+ /**
37
+ * JSON representation of a VeloxFailure for API responses
38
+ */
39
+ interface VeloxFailureJson {
40
+ error: string;
41
+ message: string;
42
+ statusCode: number;
43
+ code: string;
44
+ fix?: string;
45
+ docs?: string;
46
+ }
36
47
  /**
37
48
  * Fluent error builder that provides catalog-driven errors
38
49
  * with optional customization.
@@ -111,14 +122,7 @@ export declare class VeloxFailure extends Error {
111
122
  /**
112
123
  * Convert to JSON for API responses
113
124
  */
114
- toJSON(): {
115
- error: string;
116
- message: string;
117
- statusCode: number;
118
- code: string;
119
- fix?: string;
120
- docs?: string;
121
- };
125
+ toJSON(): VeloxFailureJson;
122
126
  /**
123
127
  * Interpolate variables into a template string
124
128
  */
@@ -161,3 +165,4 @@ export declare function fail(code: string, vars?: InterpolationVars): VeloxFailu
161
165
  * @returns true if error is a VeloxFailure
162
166
  */
163
167
  export declare function isVeloxFailure(error: unknown): error is VeloxFailure;
168
+ export {};
@@ -54,7 +54,6 @@ export class VeloxFailure extends Error {
54
54
  this.code = code;
55
55
  this.statusCode = entry.statusCode;
56
56
  this.entry = entry;
57
- // Maintains proper stack trace
58
57
  if (Error.captureStackTrace) {
59
58
  Error.captureStackTrace(this, VeloxFailure);
60
59
  }
@@ -142,11 +141,9 @@ export class VeloxFailure extends Error {
142
141
  statusCode: this.statusCode,
143
142
  code: this.code,
144
143
  };
145
- // Include fix in development
146
144
  if (process.env.NODE_ENV !== 'production' && this.suggestion) {
147
145
  json.fix = this.suggestion;
148
146
  }
149
- // Always include docs URL
150
147
  if (this.docsUrl) {
151
148
  json.docs = this.docsUrl;
152
149
  }
@@ -262,15 +262,12 @@ export function formatErrorForApi(error, catalogCode) {
262
262
  statusCode: error.statusCode ?? entry?.statusCode ?? 500,
263
263
  code: catalogCode ?? error.code,
264
264
  };
265
- // Include field errors for validation
266
265
  if (error.fields) {
267
266
  response.fields = error.fields;
268
267
  }
269
- // Include fix suggestion in development
270
268
  if (process.env.NODE_ENV !== 'production' && entry?.fix) {
271
269
  response.fix = entry.fix.suggestion;
272
270
  }
273
- // Include docs link
274
271
  if (entry?.docsUrl) {
275
272
  response.docs = entry.docsUrl;
276
273
  }
@@ -9,9 +9,6 @@
9
9
  *
10
10
  * @module errors
11
11
  */
12
- // Re-export catalog
13
12
  export { ERROR_CATALOG, ERROR_DOMAINS, getDocsUrl, getErrorEntry, getErrorsByDomain, isKnownErrorCode, } from './catalog.js';
14
- // Re-export fail() - the elegant error creation API
15
13
  export { fail, isVeloxFailure, VeloxFailure, } from './fail.js';
16
- // Re-export formatter
17
14
  export { extractErrorLocation, formatError, formatErrorForApi, formatErrorOneLine, logDeprecation, logError, logWarning, } from './formatter.js';
package/dist/errors.js CHANGED
@@ -3,12 +3,10 @@
3
3
  * Provides base error classes with HTTP status codes and discriminated unions
4
4
  * @module errors
5
5
  */
6
- // Import catalog for use in error classes
7
6
  import { ERROR_CATALOG } from './errors/catalog.js';
8
7
  import { formatError as _formatError } from './errors/formatter.js';
9
8
  import { createLogger } from './utils/logger.js';
10
9
  const log = createLogger('core');
11
- // Re-export the enhanced error catalog, formatter, and fail()
12
10
  export { ERROR_CATALOG, ERROR_DOMAINS, extractErrorLocation, fail, formatError, formatErrorForApi, formatErrorOneLine, getDocsUrl, getErrorEntry, getErrorsByDomain, isKnownErrorCode, isVeloxFailure, logDeprecation, logError, logWarning, VeloxFailure, } from './errors/index.js';
13
11
  /**
14
12
  * Type guard for validation error responses
@@ -81,7 +79,6 @@ export class VeloxError extends Error {
81
79
  this.name = 'VeloxError';
82
80
  this.statusCode = statusCode;
83
81
  this.code = code;
84
- // Look up catalog entry for enhanced error info
85
82
  if (code != null && String(code).startsWith('VELOX-')) {
86
83
  const entry = ERROR_CATALOG[code];
87
84
  if (entry) {
@@ -89,7 +86,6 @@ export class VeloxError extends Error {
89
86
  this.docsUrl = entry.docsUrl;
90
87
  }
91
88
  }
92
- // Maintains proper stack trace for where error was thrown (V8 only)
93
89
  if (Error.captureStackTrace) {
94
90
  Error.captureStackTrace(this, VeloxError);
95
91
  }
@@ -106,11 +102,9 @@ export class VeloxError extends Error {
106
102
  statusCode: this.statusCode,
107
103
  code: this.code,
108
104
  };
109
- // Include fix suggestion in development only
110
105
  if (process.env.NODE_ENV !== 'production' && this.fix) {
111
106
  response.fix = this.fix;
112
107
  }
113
- // Always include docs URL if available
114
108
  if (this.docsUrl) {
115
109
  response.docs = this.docsUrl;
116
110
  }
@@ -298,7 +292,7 @@ export class NotFoundError extends VeloxError {
298
292
  * ```
299
293
  */
300
294
  export function isVeloxError(error) {
301
- return error != null && error instanceof VeloxError;
295
+ return error instanceof VeloxError;
302
296
  }
303
297
  /**
304
298
  * Type guard to check if an error is a ValidationError
package/dist/index.js CHANGED
@@ -15,19 +15,13 @@
15
15
  * @module @veloxts/core
16
16
  */
17
17
  import { createRequire } from 'node:module';
18
- // Read version from package.json dynamically
19
18
  const require = createRequire(import.meta.url);
20
19
  const packageJson = require('../package.json');
21
20
  /** VeloxTS framework version */
22
21
  export const VELOX_VERSION = packageJson.version ?? '0.0.0-unknown';
23
22
  export { VeloxApp, velox, veloxApp } from './app.js';
24
23
  export { createContext, isContext, setupContextHook, setupTestContext } from './context.js';
25
- export { assertNever, ConfigurationError,
26
- // Elegant error creation API
27
- fail, isConfigurationError, isNotFoundError, isNotFoundErrorResponse, isValidationError, isValidationErrorResponse, isVeloxError, isVeloxFailure,
28
- // Developer experience utilities
29
- logDeprecation, logWarning, NotFoundError, ValidationError, VeloxError, VeloxFailure, } from './errors.js';
30
- // Plugin system
24
+ export { assertNever, ConfigurationError, fail, isConfigurationError, isNotFoundError, isNotFoundErrorResponse, isValidationError, isValidationErrorResponse, isVeloxError, isVeloxFailure, logDeprecation, logWarning, NotFoundError, ValidationError, VeloxError, VeloxFailure, } from './errors.js';
31
25
  export { definePlugin, isFastifyPlugin, isVeloxPlugin, validatePluginMetadata } from './plugin.js';
32
26
  export { isValidHost, isValidPort } from './utils/config.js';
33
27
  export { registerStatic } from './plugins/static.js';
package/dist/plugin.js CHANGED
@@ -78,7 +78,6 @@ export function validatePluginMetadata(plugin) {
78
78
  if (!plugin.register || typeof plugin.register !== 'function') {
79
79
  throw new VeloxError(`Plugin "${plugin.name}" must have a register function`, 500, 'INVALID_PLUGIN_METADATA');
80
80
  }
81
- // Validate dependencies array if provided
82
81
  if (plugin.dependencies !== undefined) {
83
82
  if (!Array.isArray(plugin.dependencies)) {
84
83
  throw new VeloxError(`Plugin "${plugin.name}" dependencies must be an array`, 500, 'INVALID_PLUGIN_METADATA');
@@ -110,7 +109,6 @@ export function isVeloxPlugin(value) {
110
109
  if (typeof value !== 'object' || value === null) {
111
110
  return false;
112
111
  }
113
- // Use 'in' operator for safe property access without type assertions
114
112
  return ('name' in value &&
115
113
  'version' in value &&
116
114
  'register' in value &&
@@ -93,17 +93,13 @@ function formatDuration(ms) {
93
93
  * @param server - Fastify instance
94
94
  */
95
95
  async function requestLoggerPlugin(server) {
96
- // Skip if request logging not enabled via environment
97
96
  if (process.env.VELOX_REQUEST_LOGGING !== 'true') {
98
97
  return;
99
98
  }
100
- // Track request start time
101
99
  server.addHook('onRequest', async (request) => {
102
100
  request._veloxStartTime = performance.now();
103
101
  });
104
- // Log on response with timing
105
102
  server.addHook('onResponse', async (request, reply) => {
106
- // Wrap in try-catch to ensure logging never breaks request handling
107
103
  try {
108
104
  const startTime = request._veloxStartTime;
109
105
  const duration = startTime ? performance.now() - startTime : 0;
@@ -115,7 +111,7 @@ async function requestLoggerPlugin(server) {
115
111
  console.log(`${COLORS.dim}${timestamp}${COLORS.reset} ${color}${method}${COLORS.reset} ${url} ${color}${status}${COLORS.reset} ${COLORS.dim}${formatDuration(duration)}${COLORS.reset}`);
116
112
  }
117
113
  catch {
118
- // Silently fail - logging should never affect application behavior
114
+ // Logging failures must not affect request handling
119
115
  }
120
116
  });
121
117
  }
@@ -83,9 +83,7 @@ function buildCacheControl(options) {
83
83
  */
84
84
  export async function registerStatic(server, path, options = {}) {
85
85
  const { spa = false, prefix = '/', cache = {}, exclude = [], index = 'index.html' } = options;
86
- // Resolve to absolute path
87
86
  const absoluteRoot = resolve(process.cwd(), path);
88
- // Dynamically import @fastify/static (optional peer dependency)
89
87
  let fastifyStatic;
90
88
  try {
91
89
  const module = await import('@fastify/static');
@@ -94,9 +92,7 @@ export async function registerStatic(server, path, options = {}) {
94
92
  catch {
95
93
  throw new Error('To serve static files, please install @fastify/static:\n\n pnpm add @fastify/static');
96
94
  }
97
- // Build cache control header
98
95
  const cacheControl = buildCacheControl(cache);
99
- // Register static plugin
100
96
  await server.register(fastifyStatic, {
101
97
  root: absoluteRoot,
102
98
  prefix,
@@ -106,10 +102,8 @@ export async function registerStatic(server, path, options = {}) {
106
102
  res.setHeader('Cache-Control', cacheControl);
107
103
  },
108
104
  });
109
- // SPA fallback: serve index.html for non-file routes
110
105
  if (spa) {
111
106
  server.setNotFoundHandler(async (request, reply) => {
112
- // Skip excluded paths (API routes, etc.)
113
107
  for (const excludePath of exclude) {
114
108
  if (request.url.startsWith(excludePath)) {
115
109
  return reply.status(404).send({
@@ -119,7 +113,6 @@ export async function registerStatic(server, path, options = {}) {
119
113
  });
120
114
  }
121
115
  }
122
- // Skip requests for files (have extensions)
123
116
  if (/\.\w+$/.test(request.url)) {
124
117
  return reply.status(404).send({
125
118
  error: 'NotFound',
@@ -127,7 +120,6 @@ export async function registerStatic(server, path, options = {}) {
127
120
  statusCode: 404,
128
121
  });
129
122
  }
130
- // Serve index.html for SPA routes
131
123
  return reply.sendFile(index, absoluteRoot);
132
124
  });
133
125
  }
@@ -17,11 +17,9 @@ export function printBanner(server, options) {
17
17
  const { address, env, startTime } = options;
18
18
  const elapsed = Math.round(performance.now() - startTime);
19
19
  if (env === 'production') {
20
- // Production: single line, minimal, machine-parseable
21
20
  console.log(` VeloxTS v${VELOX_VERSION} | ${env} | ${address}`);
22
21
  return;
23
22
  }
24
- // Development: detailed banner with route information
25
23
  const routes = collectRoutes(server);
26
24
  console.log('');
27
25
  console.log(` ${pc.bold('VeloxTS')} v${VELOX_VERSION}`);
@@ -62,11 +60,7 @@ function formatMethod(method) {
62
60
  */
63
61
  function collectRoutes(server) {
64
62
  const routes = [];
65
- // Fastify exposes routes via printRoutes or we can iterate
66
- // Using the internal routes map after ready()
67
63
  const routesList = server.printRoutes({ commonPrefix: false });
68
- // Parse the route tree output
69
- // Format: "└── /api (GET, HEAD)\n └── /users (GET, POST, HEAD)"
70
64
  const lines = routesList.split('\n');
71
65
  for (const line of lines) {
72
66
  const match = line.match(/([/][^\s(]*)\s*\(([^)]+)\)/);
@@ -38,7 +38,7 @@ export function isValidPort(port) {
38
38
  * @returns true if host is valid (non-empty string), narrowing to ValidHost
39
39
  */
40
40
  export function isValidHost(host) {
41
- return typeof host === 'string' && host.length > 0;
41
+ return host.length > 0;
42
42
  }
43
43
  // ============================================================================
44
44
  // Configuration Functions
@@ -88,10 +88,9 @@ export function validateConfig(config) {
88
88
  if (!isValidHost(config.host)) {
89
89
  throw new Error('Host must be a non-empty string.');
90
90
  }
91
- // Return frozen config with branded types
92
91
  return Object.freeze({
93
- port: config.port, // Already narrowed to ValidPort by isValidPort
94
- host: config.host, // Already narrowed to ValidHost by isValidHost
92
+ port: config.port,
93
+ host: config.host,
95
94
  logger: config.logger,
96
95
  fastify: config.fastify,
97
96
  });
@@ -34,12 +34,10 @@ export class LifecycleManager {
34
34
  * ```
35
35
  */
36
36
  addShutdownHandler(handler) {
37
- // Prevent memory leaks from unbounded growth
38
37
  if (this.shutdownHandlers.size >= LifecycleManager.MAX_SHUTDOWN_HANDLERS) {
39
38
  throw new Error(`Maximum number of shutdown handlers (${LifecycleManager.MAX_SHUTDOWN_HANDLERS}) exceeded. ` +
40
39
  'This may indicate a memory leak.');
41
40
  }
42
- // Set automatically prevents duplicates
43
41
  this.shutdownHandlers.add(handler);
44
42
  }
45
43
  /**
@@ -87,7 +85,7 @@ export class LifecycleManager {
87
85
  */
88
86
  setupSignalHandlers(onShutdown) {
89
87
  const signals = ['SIGINT', 'SIGTERM'];
90
- signals.forEach((signal) => {
88
+ for (const signal of signals) {
91
89
  const handler = async () => {
92
90
  log.info(`\nReceived ${signal}, initiating graceful shutdown...`);
93
91
  try {
@@ -100,10 +98,9 @@ export class LifecycleManager {
100
98
  process.exit(1);
101
99
  }
102
100
  };
103
- // Store handler reference so it can be removed later
104
101
  this.signalHandlers.set(signal, handler);
105
102
  process.once(signal, handler);
106
- });
103
+ }
107
104
  }
108
105
  /**
109
106
  * Removes all signal handlers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/core",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Fastify wrapper and plugin system for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",