vector-framework 0.9.8 → 0.9.9

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.
@@ -1,13 +1,32 @@
1
- import { existsSync, promises as fs } from 'node:fs';
2
- import { join, relative, resolve, sep } from 'node:path';
3
- import type { GeneratedRoute } from '../types';
1
+ import { existsSync, promises as fs } from "node:fs";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import type { GeneratedRoute } from "../types";
4
4
 
5
5
  export class RouteScanner {
6
6
  private routesDir: string;
7
-
8
- constructor(routesDir = './routes') {
7
+ private excludePatterns: string[];
8
+ private static readonly DEFAULT_EXCLUDE_PATTERNS = [
9
+ "*.test.ts",
10
+ "*.test.js",
11
+ "*.test.tsx",
12
+ "*.test.jsx",
13
+ "*.spec.ts",
14
+ "*.spec.js",
15
+ "*.spec.tsx",
16
+ "*.spec.jsx",
17
+ "*.tests.ts",
18
+ "*.tests.js",
19
+ "**/__tests__/**",
20
+ "*.interface.ts",
21
+ "*.type.ts",
22
+ "*.d.ts",
23
+ ];
24
+
25
+ constructor(routesDir = "./routes", excludePatterns?: string[]) {
9
26
  // Always resolve from the current working directory (user's project)
10
27
  this.routesDir = resolve(process.cwd(), routesDir);
28
+ this.excludePatterns =
29
+ excludePatterns || RouteScanner.DEFAULT_EXCLUDE_PATTERNS;
11
30
  }
12
31
 
13
32
  async scan(): Promise<GeneratedRoute[]> {
@@ -21,7 +40,7 @@ export class RouteScanner {
21
40
  try {
22
41
  await this.scanDirectory(this.routesDir, routes);
23
42
  } catch (error) {
24
- if ((error as any).code === 'ENOENT') {
43
+ if ((error as any).code === "ENOENT") {
25
44
  console.warn(` ✗ Routes directory not accessible: ${this.routesDir}`);
26
45
  return [];
27
46
  }
@@ -31,7 +50,34 @@ export class RouteScanner {
31
50
  return routes;
32
51
  }
33
52
 
34
- private async scanDirectory(dir: string, routes: GeneratedRoute[], basePath = ''): Promise<void> {
53
+ private isExcluded(filePath: string): boolean {
54
+ const relativePath = relative(this.routesDir, filePath);
55
+
56
+ for (const pattern of this.excludePatterns) {
57
+ // Convert glob pattern to regex
58
+ const regexPattern = pattern
59
+ .replace(/\./g, "\\.") // Escape dots
60
+ .replace(/\*/g, "[^/]*") // * matches anything except /
61
+ .replace(/\*\*/g, ".*") // ** matches anything including /
62
+ .replace(/\?/g, "."); // ? matches single character
63
+
64
+ const regex = new RegExp(`^${regexPattern}$`);
65
+
66
+ // Check both the full relative path and just the filename
67
+ const filename = relativePath.split(sep).pop() || "";
68
+ if (regex.test(relativePath) || regex.test(filename)) {
69
+ return true;
70
+ }
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ private async scanDirectory(
77
+ dir: string,
78
+ routes: GeneratedRoute[],
79
+ basePath = ""
80
+ ): Promise<void> {
35
81
  const entries = await fs.readdir(dir);
36
82
 
37
83
  for (const entry of entries) {
@@ -41,26 +87,32 @@ export class RouteScanner {
41
87
  if (stats.isDirectory()) {
42
88
  const newBasePath = basePath ? `${basePath}/${entry}` : entry;
43
89
  await this.scanDirectory(fullPath, routes, newBasePath);
44
- } else if (entry.endsWith('.ts') || entry.endsWith('.js')) {
90
+ } else if (entry.endsWith(".ts") || entry.endsWith(".js")) {
91
+ // Skip excluded files (test files, etc.)
92
+ if (this.isExcluded(fullPath)) {
93
+ continue;
94
+ }
45
95
  const routePath = relative(this.routesDir, fullPath)
46
- .replace(/\.(ts|js)$/, '')
96
+ .replace(/\.(ts|js)$/, "")
47
97
  .split(sep)
48
- .join('/');
98
+ .join("/");
49
99
 
50
100
  try {
51
101
  // Convert Windows paths to URLs for import
52
102
  const importPath =
53
- process.platform === 'win32' ? `file:///${fullPath.replace(/\\/g, '/')}` : fullPath;
103
+ process.platform === "win32"
104
+ ? `file:///${fullPath.replace(/\\/g, "/")}`
105
+ : fullPath;
54
106
 
55
107
  const module = await import(importPath);
56
108
 
57
- if (module.default && typeof module.default === 'function') {
109
+ if (module.default && typeof module.default === "function") {
58
110
  routes.push({
59
- name: 'default',
111
+ name: "default",
60
112
  path: fullPath,
61
- method: 'GET',
113
+ method: "GET",
62
114
  options: {
63
- method: 'GET',
115
+ method: "GET",
64
116
  path: `/${routePath}`,
65
117
  expose: true,
66
118
  },
@@ -68,10 +120,16 @@ export class RouteScanner {
68
120
  }
69
121
 
70
122
  for (const [name, value] of Object.entries(module)) {
71
- if (name === 'default') continue;
123
+ if (name === "default") continue;
72
124
 
73
125
  // Check for new RouteDefinition format
74
- if (value && typeof value === 'object' && 'entry' in value && 'options' in value && 'handler' in value) {
126
+ if (
127
+ value &&
128
+ typeof value === "object" &&
129
+ "entry" in value &&
130
+ "options" in value &&
131
+ "handler" in value
132
+ ) {
75
133
  const routeDef = value as any;
76
134
  routes.push({
77
135
  name,
@@ -103,7 +161,7 @@ export class RouteScanner {
103
161
  }
104
162
 
105
163
  enableWatch(callback: () => void) {
106
- if (typeof Bun !== 'undefined' && Bun.env.NODE_ENV === 'development') {
164
+ if (typeof Bun !== "undefined" && Bun.env.NODE_ENV === "development") {
107
165
  console.log(`Watching for route changes in ${this.routesDir}`);
108
166
 
109
167
  setInterval(async () => {
@@ -45,7 +45,13 @@ export class MiddlewareManager<
45
45
  let currentResponse = response;
46
46
 
47
47
  for (const handler of this.finallyHandlers) {
48
- currentResponse = await handler(currentResponse, request);
48
+ try {
49
+ currentResponse = await handler(currentResponse, request);
50
+ } catch (error) {
51
+ // Log but don't throw - we don't want to break the response chain
52
+ console.error('After middleware error:', error);
53
+ // Continue with the current response
54
+ }
49
55
  }
50
56
 
51
57
  return currentResponse;
@@ -85,6 +85,7 @@ export interface VectorConfig<TTypes extends VectorTypes = DefaultVectorTypes> {
85
85
  before?: BeforeMiddlewareHandler<TTypes>[];
86
86
  finally?: AfterMiddlewareHandler<TTypes>[];
87
87
  routesDir?: string;
88
+ routeExcludePatterns?: string[];
88
89
  autoDiscover?: boolean;
89
90
  idleTimeout?: number;
90
91
  }
@@ -97,6 +98,7 @@ export interface VectorConfigSchema<TTypes extends VectorTypes = DefaultVectorTy
97
98
  reusePort?: boolean;
98
99
  development?: boolean;
99
100
  routesDir?: string;
101
+ routeExcludePatterns?: string[];
100
102
  idleTimeout?: number;
101
103
 
102
104
  // Middleware functions