@veloxts/router 0.7.0 → 0.7.2

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,23 @@
1
1
  # @veloxts/router
2
2
 
3
+ ## 0.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - chore(auth,core,create,cli,client,orm,mcp,router,validation,web): simplify code for clarity and maintainability
8
+ - Updated dependencies
9
+ - @veloxts/core@0.7.2
10
+ - @veloxts/validation@0.7.2
11
+
12
+ ## 0.7.1
13
+
14
+ ### Patch Changes
15
+
16
+ - security audit, bumps dependency packages
17
+ - Updated dependencies
18
+ - @veloxts/core@0.7.1
19
+ - @veloxts/validation@0.7.1
20
+
3
21
  ## 0.7.0
4
22
 
5
23
  ### Minor Changes
@@ -9,7 +9,9 @@
9
9
  import fs from 'node:fs/promises';
10
10
  import path from 'node:path';
11
11
  import { pathToFileURL } from 'node:url';
12
+ import { createLogger } from '@veloxts/core';
12
13
  import { isProcedureCollection } from '../procedure/builder.js';
14
+ const log = createLogger('router');
13
15
  import { directoryNotFound, fileLoadError, invalidExport, noProceduresFound, permissionDenied, } from './errors.js';
14
16
  import { DiscoveryErrorCode, } from './types.js';
15
17
  // ============================================================================
@@ -113,7 +115,7 @@ export async function discoverProceduresVerbose(searchPath, options = {}) {
113
115
  });
114
116
  // Log warning if mode is 'warn'
115
117
  if (onInvalidExport === 'warn') {
116
- console.warn(`[Discovery Warning] ${file}: ${result.message}`);
118
+ log.warn(`[Discovery Warning] ${file}: ${result.message}`);
117
119
  }
118
120
  }
119
121
  }
@@ -201,6 +201,42 @@ function hasProperties(schema) {
201
201
  // ============================================================================
202
202
  // Response Generation
203
203
  // ============================================================================
204
+ /**
205
+ * Creates a standard error response schema with the given example code
206
+ *
207
+ * @param exampleCode - Example error code (e.g., 'VALIDATION_ERROR', 'NOT_FOUND')
208
+ * @param extraProperties - Additional properties on the error object (e.g., details)
209
+ */
210
+ function createErrorSchema(exampleCode, extraProperties) {
211
+ return {
212
+ type: 'object',
213
+ properties: {
214
+ error: {
215
+ type: 'object',
216
+ properties: {
217
+ code: { type: 'string', example: exampleCode },
218
+ message: { type: 'string' },
219
+ ...extraProperties,
220
+ },
221
+ required: ['code', 'message'],
222
+ },
223
+ },
224
+ required: ['error'],
225
+ };
226
+ }
227
+ /**
228
+ * Creates a standard error response with the given description and example code
229
+ */
230
+ function createErrorResponse(description, exampleCode) {
231
+ return {
232
+ description,
233
+ content: {
234
+ 'application/json': {
235
+ schema: createErrorSchema(exampleCode),
236
+ },
237
+ },
238
+ };
239
+ }
204
240
  /**
205
241
  * Builds response definitions for an operation
206
242
  */
@@ -211,7 +247,6 @@ function buildResponses(method, outputSchema, hasAuth) {
211
247
  const successDescription = getSuccessDescription(method);
212
248
  // Success response
213
249
  if (successCode === '204') {
214
- // No content
215
250
  responses['204'] = { description: successDescription };
216
251
  }
217
252
  else {
@@ -224,120 +259,26 @@ function buildResponses(method, outputSchema, hasAuth) {
224
259
  }),
225
260
  };
226
261
  }
227
- // Error responses
262
+ // Validation error
228
263
  responses['400'] = {
229
264
  description: 'Bad Request - Validation error',
230
265
  content: {
231
266
  'application/json': {
232
- schema: {
233
- type: 'object',
234
- properties: {
235
- error: {
236
- type: 'object',
237
- properties: {
238
- code: { type: 'string', example: 'VALIDATION_ERROR' },
239
- message: { type: 'string' },
240
- details: { type: 'object' },
241
- },
242
- required: ['code', 'message'],
243
- },
244
- },
245
- required: ['error'],
246
- },
267
+ schema: createErrorSchema('VALIDATION_ERROR', { details: { type: 'object' } }),
247
268
  },
248
269
  },
249
270
  };
250
271
  // Auth-related responses
251
272
  if (hasAuth) {
252
- responses['401'] = {
253
- description: 'Unauthorized - Authentication required',
254
- content: {
255
- 'application/json': {
256
- schema: {
257
- type: 'object',
258
- properties: {
259
- error: {
260
- type: 'object',
261
- properties: {
262
- code: { type: 'string', example: 'UNAUTHORIZED' },
263
- message: { type: 'string' },
264
- },
265
- required: ['code', 'message'],
266
- },
267
- },
268
- required: ['error'],
269
- },
270
- },
271
- },
272
- };
273
- responses['403'] = {
274
- description: 'Forbidden - Insufficient permissions',
275
- content: {
276
- 'application/json': {
277
- schema: {
278
- type: 'object',
279
- properties: {
280
- error: {
281
- type: 'object',
282
- properties: {
283
- code: { type: 'string', example: 'FORBIDDEN' },
284
- message: { type: 'string' },
285
- },
286
- required: ['code', 'message'],
287
- },
288
- },
289
- required: ['error'],
290
- },
291
- },
292
- },
293
- };
273
+ responses['401'] = createErrorResponse('Unauthorized - Authentication required', 'UNAUTHORIZED');
274
+ responses['403'] = createErrorResponse('Forbidden - Insufficient permissions', 'FORBIDDEN');
294
275
  }
295
276
  // Not found for single-resource operations
296
277
  if (['GET', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
297
- responses['404'] = {
298
- description: 'Not Found - Resource does not exist',
299
- content: {
300
- 'application/json': {
301
- schema: {
302
- type: 'object',
303
- properties: {
304
- error: {
305
- type: 'object',
306
- properties: {
307
- code: { type: 'string', example: 'NOT_FOUND' },
308
- message: { type: 'string' },
309
- },
310
- required: ['code', 'message'],
311
- },
312
- },
313
- required: ['error'],
314
- },
315
- },
316
- },
317
- };
278
+ responses['404'] = createErrorResponse('Not Found - Resource does not exist', 'NOT_FOUND');
318
279
  }
319
280
  // Internal server error
320
- responses['500'] = {
321
- description: 'Internal Server Error',
322
- content: {
323
- 'application/json': {
324
- schema: {
325
- type: 'object',
326
- properties: {
327
- error: {
328
- type: 'object',
329
- properties: {
330
- code: { type: 'string', example: 'INTERNAL_ERROR' },
331
- message: { type: 'string' },
332
- },
333
- required: ['code', 'message'],
334
- },
335
- },
336
- required: ['error'],
337
- },
338
- },
339
- },
340
- };
281
+ responses['500'] = createErrorResponse('Internal Server Error', 'INTERNAL_ERROR');
341
282
  return responses;
342
283
  }
343
284
  /**
@@ -97,7 +97,7 @@ export function parsePathParameters(path, schemas) {
97
97
  * @returns True if path contains parameters
98
98
  */
99
99
  export function hasPathParameters(path) {
100
- return PATH_PARAM_REGEX.test(path);
100
+ return path.includes(':');
101
101
  }
102
102
  /**
103
103
  * Extracts query parameters from a JSON Schema
@@ -5,7 +5,9 @@
5
5
  *
6
6
  * @module @veloxts/router/openapi/schema-converter
7
7
  */
8
+ import { createLogger } from '@veloxts/core';
8
9
  import { z } from 'zod';
10
+ const log = createLogger('router');
9
11
  /**
10
12
  * Maps our target names to Zod 4's native `z.toJSONSchema()` target names.
11
13
  *
@@ -65,7 +67,7 @@ export function zodSchemaToJsonSchema(schema, options = {}) {
65
67
  }
66
68
  catch (error) {
67
69
  // Log error but don't fail - return a generic schema
68
- console.warn('[VeloxTS] Failed to convert Zod schema to JSON Schema:', error);
70
+ log.warn('Failed to convert Zod schema to JSON Schema:', error);
69
71
  return { type: 'object' };
70
72
  }
71
73
  }
@@ -199,26 +199,6 @@ function createBuilder(state) {
199
199
  mutation(handler) {
200
200
  return compileProcedure('mutation', handler, state);
201
201
  },
202
- /**
203
- * Sets the output type based on a resource schema
204
- *
205
- * Accepts either a plain `ResourceSchema` or a tagged schema
206
- * (e.g., `UserSchema.authenticated`) for declarative auto-projection.
207
- *
208
- * @example
209
- * ```typescript
210
- * // Tagged schema — auto-projects to authenticated fields
211
- * procedure()
212
- * .guard(authenticated)
213
- * .resource(UserSchema.authenticated)
214
- * .query(handler);
215
- *
216
- * // Plain schema — defaults to public, or derives from guardNarrow
217
- * procedure()
218
- * .resource(UserSchema)
219
- * .query(handler);
220
- * ```
221
- */
222
202
  /**
223
203
  * Sets the output type based on a resource schema
224
204
  *
@@ -248,9 +228,7 @@ function compileProcedure(type, handler, state) {
248
228
  const typedMiddlewares = state.middlewares;
249
229
  // Pre-compile the middleware chain executor if middlewares exist
250
230
  // This avoids rebuilding the chain on every request
251
- const precompiledExecutor = typedMiddlewares.length > 0
252
- ? createPrecompiledMiddlewareExecutor(typedMiddlewares, handler)
253
- : undefined;
231
+ const precompiledExecutor = typedMiddlewares.length > 0 ? createMiddlewareExecutor(typedMiddlewares, handler) : undefined;
254
232
  // Create the final procedure object
255
233
  return {
256
234
  type,
@@ -272,18 +250,6 @@ function compileProcedure(type, handler, state) {
272
250
  _resourceLevel: state.resourceLevel,
273
251
  };
274
252
  }
275
- /**
276
- * Creates a pre-compiled middleware chain executor
277
- *
278
- * PERFORMANCE: This function builds the middleware chain once during procedure
279
- * compilation, creating a single reusable function that executes the entire chain.
280
- * This eliminates the need to rebuild closures on every request.
281
- *
282
- * @internal
283
- */
284
- function createPrecompiledMiddlewareExecutor(middlewares, handler) {
285
- return createMiddlewareExecutor(middlewares, handler);
286
- }
287
253
  /**
288
254
  * Defines a collection of procedures under a namespace
289
255
  *
@@ -477,7 +443,7 @@ export async function executeProcedure(procedure, rawInput, ctx) {
477
443
  }
478
444
  else {
479
445
  // Fallback: Build middleware chain dynamically (should not normally happen)
480
- result = await executeMiddlewareChainFallback(procedure.middlewares, input, ctxWithLevel, async () => procedure.handler({ input, ctx: ctxWithLevel }));
446
+ result = await executeMiddlewareChain(procedure.middlewares, input, ctxWithLevel, async () => procedure.handler({ input, ctx: ctxWithLevel }));
481
447
  }
482
448
  // Step 4: Auto-project if resource schema is set
483
449
  if (procedure._resourceSchema) {
@@ -508,17 +474,6 @@ export async function executeProcedure(procedure, rawInput, ctx) {
508
474
  }
509
475
  return result;
510
476
  }
511
- /**
512
- * Fallback middleware chain executor for edge cases
513
- *
514
- * This function is only used when _precompiledExecutor is not available,
515
- * which should be rare in normal operation.
516
- *
517
- * @internal
518
- */
519
- async function executeMiddlewareChainFallback(middlewares, input, ctx, handler) {
520
- return executeMiddlewareChain(middlewares, input, ctx, handler);
521
- }
522
477
  // ============================================================================
523
478
  // Type Utilities
524
479
  // ============================================================================
@@ -7,7 +7,8 @@
7
7
  *
8
8
  * @module rest/adapter
9
9
  */
10
- import { ConfigurationError } from '@veloxts/core';
10
+ import { ConfigurationError, createLogger } from '@veloxts/core';
11
+ const log = createLogger('router');
11
12
  import { executeProcedure } from '../procedure/builder.js';
12
13
  import { buildMultiLevelNestedPath, buildNestedRestPath, buildRestPath, calculateNestingDepth, parseNamingConvention, } from './naming.js';
13
14
  /** Default nesting depth threshold for warnings */
@@ -35,7 +36,7 @@ export function generateRestRoutes(collection, options = {}) {
35
36
  if (nestingWarnings) {
36
37
  const depth = calculateNestingDepth(procedure.parentResource, procedure.parentResources);
37
38
  if (depth >= NESTING_DEPTH_WARNING_THRESHOLD) {
38
- console.warn(`⚠️ Resource '${collection.namespace}/${name}' has ${depth} levels of nesting. ` +
39
+ log.warn(`Resource '${collection.namespace}/${name}' has ${depth} levels of nesting. ` +
39
40
  `Consider using shortcuts: true or restructuring your API.`);
40
41
  }
41
42
  }
@@ -221,10 +222,8 @@ function gatherInput(request, route) {
221
222
  (route.procedure.parentResources !== undefined && route.procedure.parentResources.length > 0);
222
223
  switch (route.method) {
223
224
  case 'GET':
224
- // GET: params (for :id and all parent params) + query (for filters/pagination)
225
- return { ...params, ...query };
226
225
  case 'DELETE':
227
- // DELETE: params (for :id and all parent params) + query (for options), no body per REST conventions
226
+ // GET/DELETE: params (for :id and all parent params) + query (for filters/pagination/options)
228
227
  return { ...params, ...query };
229
228
  case 'PUT':
230
229
  case 'PATCH':
@@ -319,24 +318,7 @@ export function registerRestRoutes(server, collections, options = {}) {
319
318
  // When used in legacy mode, we prepend the prefix manually.
320
319
  const fullPath = _prefixHandledByFastify ? route.path : `${prefix}${route.path}`;
321
320
  const handler = createRouteHandler(route);
322
- // Register route based on method
323
- switch (route.method) {
324
- case 'GET':
325
- server.get(fullPath, handler);
326
- break;
327
- case 'POST':
328
- server.post(fullPath, handler);
329
- break;
330
- case 'PUT':
331
- server.put(fullPath, handler);
332
- break;
333
- case 'PATCH':
334
- server.patch(fullPath, handler);
335
- break;
336
- case 'DELETE':
337
- server.delete(fullPath, handler);
338
- break;
339
- }
321
+ server.route({ method: route.method, url: fullPath, handler });
340
322
  }
341
323
  }
342
324
  }
@@ -86,38 +86,22 @@ function buildTRPCProcedure(t, procedure) {
86
86
  if (procedure.outputSchema) {
87
87
  builder = builder.output(procedure.outputSchema);
88
88
  }
89
- // Select handler based on whether input is expected
90
- const handler = procedure.inputSchema
91
- ? createHandler(procedure)
92
- : createNoInputHandler(procedure);
89
+ // Create handler that works with or without input
90
+ const handler = createProcedureHandler(procedure);
93
91
  // Finalize as query or mutation
94
92
  return procedure.type === 'query' ? builder.query(handler) : builder.mutation(handler);
95
93
  }
96
94
  /**
97
- * Create a handler function for a procedure with input
95
+ * Create a tRPC handler function from a compiled procedure
98
96
  *
99
- * @internal
100
- */
101
- function createHandler(procedure) {
102
- return async (opts) => {
103
- const { input, ctx } = opts;
104
- // Execute middleware chain if any
105
- if (procedure.middlewares.length > 0) {
106
- return executeWithMiddleware(procedure, input, ctx);
107
- }
108
- // Direct handler execution
109
- return procedure.handler({ input, ctx });
110
- };
111
- }
112
- /**
113
- * Create a handler function for a procedure without input
97
+ * Works for both procedures with and without input schemas.
114
98
  *
115
99
  * @internal
116
100
  */
117
- function createNoInputHandler(procedure) {
101
+ function createProcedureHandler(procedure) {
118
102
  return async (opts) => {
119
103
  const { ctx } = opts;
120
- const input = undefined;
104
+ const input = opts.input ?? undefined;
121
105
  // Execute middleware chain if any
122
106
  if (procedure.middlewares.length > 0) {
123
107
  return executeWithMiddleware(procedure, input, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/router",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Procedure definitions with tRPC and REST routing for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,14 +37,14 @@
37
37
  }
38
38
  },
39
39
  "dependencies": {
40
- "@trpc/server": "11.9.0",
40
+ "@trpc/server": "11.10.0",
41
41
  "fastify": "5.7.4",
42
- "@veloxts/core": "0.7.0",
43
- "@veloxts/validation": "0.7.0"
42
+ "@veloxts/validation": "0.7.2",
43
+ "@veloxts/core": "0.7.2"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@vitest/coverage-v8": "4.0.18",
47
- "esbuild": "0.27.2",
47
+ "esbuild": "0.27.3",
48
48
  "typescript": "5.9.3",
49
49
  "vite": "7.3.1",
50
50
  "vitest": "4.0.18",