vector-framework 0.9.8 → 1.0.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/README.md +33 -2
- package/dist/cli/index.js +86 -88
- package/dist/cli/index.js.map +1 -1
- package/dist/cli.js +192 -161
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +3 -6
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +1 -2
- package/dist/core/server.js.map +1 -1
- package/dist/core/vector.d.ts.map +1 -1
- package/dist/core/vector.js +8 -4
- package/dist/core/vector.js.map +1 -1
- package/dist/dev/route-scanner.d.ts +5 -2
- package/dist/dev/route-scanner.d.ts.map +1 -1
- package/dist/dev/route-scanner.js +63 -16
- package/dist/dev/route-scanner.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/dist/middleware/manager.d.ts.map +1 -1
- package/dist/middleware/manager.js +8 -1
- package/dist/middleware/manager.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/cli/index.ts +98 -109
- package/src/core/config-loader.ts +4 -10
- package/src/core/server.ts +1 -2
- package/src/core/vector.ts +9 -4
- package/src/dev/route-scanner.ts +76 -18
- package/src/middleware/manager.ts +7 -1
- package/src/types/index.ts +2 -0
package/src/cli/index.ts
CHANGED
|
@@ -49,9 +49,6 @@ const command = positionals[0] || "dev";
|
|
|
49
49
|
|
|
50
50
|
async function runDev() {
|
|
51
51
|
const isDev = command === "dev";
|
|
52
|
-
console.log(
|
|
53
|
-
`\n→ Starting Vector ${isDev ? "development" : "production"} server\n`
|
|
54
|
-
);
|
|
55
52
|
|
|
56
53
|
let server: any = null;
|
|
57
54
|
let vector: any = null;
|
|
@@ -66,83 +63,62 @@ async function runDev() {
|
|
|
66
63
|
|
|
67
64
|
// Create the actual server start promise
|
|
68
65
|
const serverStartPromise = (async (): Promise<{ server: any; vector: any; config: any }> => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
66
|
+
// Load configuration using ConfigLoader
|
|
67
|
+
const configLoader = new ConfigLoader(values.config as string | undefined);
|
|
68
|
+
const config = await configLoader.load();
|
|
69
|
+
|
|
70
|
+
// Merge CLI options with loaded config
|
|
71
|
+
// Only use CLI values if config doesn't have them
|
|
72
|
+
config.port = config.port ?? Number.parseInt(values.port as string);
|
|
73
|
+
config.hostname = config.hostname ?? (values.host as string);
|
|
74
|
+
config.routesDir = config.routesDir ?? (values.routes as string);
|
|
75
|
+
config.development = config.development ?? isDev;
|
|
76
|
+
config.autoDiscover = true; // Always auto-discover routes
|
|
77
|
+
|
|
78
|
+
// Apply CLI CORS option if not explicitly set in config
|
|
79
|
+
// Only apply default CORS if config.cors is undefined (not set)
|
|
80
|
+
if (config.cors === undefined && values.cors) {
|
|
81
|
+
config.cors = {
|
|
82
|
+
origin: "*",
|
|
83
|
+
credentials: true,
|
|
84
|
+
allowHeaders: "Content-Type, Authorization",
|
|
85
|
+
allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
86
|
+
exposeHeaders: "Authorization",
|
|
87
|
+
maxAge: 86400,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
94
90
|
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
// Get Vector instance and configure handlers
|
|
92
|
+
vector = getVectorInstance();
|
|
97
93
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Load and set cache handler if configured
|
|
105
|
-
const cacheHandler = await configLoader.loadCacheHandler();
|
|
106
|
-
if (cacheHandler) {
|
|
107
|
-
vector.setCacheHandler(cacheHandler);
|
|
108
|
-
}
|
|
94
|
+
// Load and set auth handler if configured
|
|
95
|
+
const authHandler = await configLoader.loadAuthHandler();
|
|
96
|
+
if (authHandler) {
|
|
97
|
+
vector.setProtectedHandler(authHandler);
|
|
98
|
+
}
|
|
109
99
|
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
// Load and set cache handler if configured
|
|
101
|
+
const cacheHandler = await configLoader.loadCacheHandler();
|
|
102
|
+
if (cacheHandler) {
|
|
103
|
+
vector.setCacheHandler(cacheHandler);
|
|
104
|
+
}
|
|
112
105
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
throw new Error("Server started but is not responding correctly");
|
|
116
|
-
}
|
|
106
|
+
// Start the server
|
|
107
|
+
server = await vector.startServer(config);
|
|
117
108
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const green = "\x1b[32m";
|
|
122
|
-
|
|
123
|
-
console.log(
|
|
124
|
-
` ${gray}Config${reset} ${
|
|
125
|
-
configSource === "user" ? "User config loaded" : "Using defaults"
|
|
126
|
-
}`
|
|
127
|
-
);
|
|
128
|
-
console.log(` ${gray}Routes${reset} ${config.routesDir}`);
|
|
129
|
-
if (isDev && values.watch) {
|
|
130
|
-
console.log(` ${gray}Watching${reset} All project files`);
|
|
131
|
-
}
|
|
132
|
-
console.log(
|
|
133
|
-
` ${gray}CORS${reset} ${config.cors ? "Enabled" : "Disabled"}`
|
|
134
|
-
);
|
|
135
|
-
console.log(
|
|
136
|
-
` ${gray}Mode${reset} ${config.development ? "Development" : "Production"}\n`
|
|
137
|
-
);
|
|
138
|
-
console.log(
|
|
139
|
-
` ${green}Ready${reset} → ${cyan}http://${config.hostname}:${config.port}${reset}\n`
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
return { server, vector, config };
|
|
143
|
-
} catch (error) {
|
|
144
|
-
throw error;
|
|
109
|
+
// Verify the server is actually running
|
|
110
|
+
if (!server || !server.port) {
|
|
111
|
+
throw new Error("Server started but is not responding correctly");
|
|
145
112
|
}
|
|
113
|
+
|
|
114
|
+
const cyan = "\x1b[36m";
|
|
115
|
+
const reset = "\x1b[0m";
|
|
116
|
+
|
|
117
|
+
console.log(
|
|
118
|
+
`\nListening on ${cyan}http://${config.hostname}:${config.port}${reset}\n`
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return { server, vector, config };
|
|
146
122
|
})();
|
|
147
123
|
|
|
148
124
|
// Race between server startup and timeout
|
|
@@ -206,9 +182,13 @@ async function runDev() {
|
|
|
206
182
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
207
183
|
|
|
208
184
|
// Clear module cache to ensure fresh imports
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
185
|
+
// Note: Bun uses ESM and doesn't have require.cache
|
|
186
|
+
// The Loader API will handle module reloading automatically
|
|
187
|
+
if (typeof require !== 'undefined' && require.cache) {
|
|
188
|
+
for (const key in require.cache) {
|
|
189
|
+
if (!key.includes("node_modules")) {
|
|
190
|
+
delete require.cache[key];
|
|
191
|
+
}
|
|
212
192
|
}
|
|
213
193
|
}
|
|
214
194
|
|
|
@@ -221,84 +201,93 @@ async function runDev() {
|
|
|
221
201
|
console.error("\n[Reload Error]", error.message || error);
|
|
222
202
|
// Don't exit the process on reload failures, just continue watching
|
|
223
203
|
} finally {
|
|
224
|
-
// Reset flag after
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}, 2000); // 2 second cooldown
|
|
204
|
+
// Reset flag immediately after reload completes
|
|
205
|
+
// The lastReloadTime check provides additional protection
|
|
206
|
+
isReloading = false;
|
|
228
207
|
}
|
|
229
208
|
}, 500); // Increased debounce to 500ms
|
|
230
209
|
}
|
|
231
210
|
});
|
|
232
|
-
} catch
|
|
233
|
-
|
|
211
|
+
} catch {
|
|
212
|
+
const yellow = "\x1b[33m";
|
|
213
|
+
const reset = "\x1b[0m";
|
|
214
|
+
console.warn(`${yellow}Warning: File watching not available${reset}`);
|
|
234
215
|
}
|
|
235
216
|
}
|
|
236
217
|
} catch (error: any) {
|
|
237
218
|
const red = "\x1b[31m";
|
|
238
219
|
const reset = "\x1b[0m";
|
|
239
220
|
|
|
240
|
-
console.error(`\n${red}
|
|
241
|
-
|
|
242
|
-
// Always show the error message and stack trace
|
|
243
|
-
if (error.message) {
|
|
244
|
-
console.error(`Message: ${error.message}`);
|
|
245
|
-
}
|
|
221
|
+
console.error(`\n${red}Error: ${error.message || error}${reset}\n`);
|
|
246
222
|
|
|
247
|
-
if (error.stack) {
|
|
248
|
-
console.error(`\nStack trace:`);
|
|
223
|
+
if (error.stack && process.env.NODE_ENV === "development") {
|
|
249
224
|
console.error(error.stack);
|
|
250
|
-
} else if (!error.message) {
|
|
251
|
-
// If no message or stack, show the raw error
|
|
252
|
-
console.error(`Raw error:`, error);
|
|
253
225
|
}
|
|
254
226
|
|
|
255
|
-
// Ensure we exit with error code
|
|
256
227
|
process.exit(1);
|
|
257
228
|
}
|
|
258
229
|
}
|
|
259
230
|
|
|
260
231
|
async function runBuild() {
|
|
261
|
-
console.log("\n→ Building Vector application\n");
|
|
262
|
-
|
|
263
232
|
try {
|
|
264
233
|
const { RouteScanner } = await import("../dev/route-scanner");
|
|
265
234
|
const { RouteGenerator } = await import("../dev/route-generator");
|
|
266
235
|
|
|
236
|
+
// Step 1: Scan and generate routes
|
|
267
237
|
const scanner = new RouteScanner(values.routes as string);
|
|
268
238
|
const generator = new RouteGenerator();
|
|
269
239
|
|
|
270
240
|
const routes = await scanner.scan();
|
|
271
241
|
await generator.generate(routes);
|
|
272
242
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Use spawn based on runtime
|
|
243
|
+
// Step 2: Build the application with Bun
|
|
276
244
|
if (typeof Bun !== "undefined") {
|
|
245
|
+
// Build the CLI as an executable
|
|
277
246
|
const buildProcess = Bun.spawn([
|
|
278
247
|
"bun",
|
|
279
248
|
"build",
|
|
280
|
-
"src/index.ts",
|
|
281
|
-
"--
|
|
282
|
-
"
|
|
249
|
+
"src/cli/index.ts",
|
|
250
|
+
"--target",
|
|
251
|
+
"bun",
|
|
252
|
+
"--outfile",
|
|
253
|
+
"dist/server.js",
|
|
283
254
|
"--minify",
|
|
284
255
|
]);
|
|
285
|
-
|
|
256
|
+
|
|
257
|
+
const exitCode = await buildProcess.exited;
|
|
258
|
+
if (exitCode !== 0) {
|
|
259
|
+
throw new Error(`Build failed with exit code ${exitCode}`);
|
|
260
|
+
}
|
|
286
261
|
} else {
|
|
287
262
|
// For Node.js, use child_process
|
|
288
263
|
const { spawnSync } = await import("child_process");
|
|
289
|
-
spawnSync(
|
|
264
|
+
const result = spawnSync(
|
|
290
265
|
"bun",
|
|
291
|
-
[
|
|
266
|
+
[
|
|
267
|
+
"build",
|
|
268
|
+
"src/cli/index.ts",
|
|
269
|
+
"--target",
|
|
270
|
+
"bun",
|
|
271
|
+
"--outfile",
|
|
272
|
+
"dist/server.js",
|
|
273
|
+
"--minify",
|
|
274
|
+
],
|
|
292
275
|
{
|
|
293
276
|
stdio: "inherit",
|
|
294
277
|
shell: true,
|
|
295
278
|
}
|
|
296
279
|
);
|
|
280
|
+
|
|
281
|
+
if (result.status !== 0) {
|
|
282
|
+
throw new Error(`Build failed with exit code ${result.status}`);
|
|
283
|
+
}
|
|
297
284
|
}
|
|
298
285
|
|
|
299
|
-
console.log("\
|
|
300
|
-
} catch (error) {
|
|
301
|
-
|
|
286
|
+
console.log("\nBuild complete: dist/server.js\n");
|
|
287
|
+
} catch (error: any) {
|
|
288
|
+
const red = "\x1b[31m";
|
|
289
|
+
const reset = "\x1b[0m";
|
|
290
|
+
console.error(`\n${red}Error: ${error.message || error}${reset}\n`);
|
|
302
291
|
process.exit(1);
|
|
303
292
|
}
|
|
304
293
|
}
|
|
@@ -30,27 +30,21 @@ export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
30
30
|
// Check if config file exists before attempting to load
|
|
31
31
|
if (existsSync(this.configPath)) {
|
|
32
32
|
try {
|
|
33
|
-
console.log(`→ Loading config from: ${this.configPath}`);
|
|
34
|
-
|
|
35
33
|
// Use explicit file:// URL to ensure correct resolution
|
|
36
34
|
const userConfigPath = toFileUrl(this.configPath);
|
|
37
35
|
const userConfig = await import(userConfigPath);
|
|
38
36
|
this.config = userConfig.default || userConfig;
|
|
39
37
|
this.configSource = "user";
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
} catch (error: any) {
|
|
39
|
+
const red = "\x1b[31m";
|
|
40
|
+
const reset = "\x1b[0m";
|
|
43
41
|
console.error(
|
|
44
|
-
|
|
45
|
-
error
|
|
42
|
+
`${red}Error loading config: ${error.message || error}${reset}`
|
|
46
43
|
);
|
|
47
|
-
console.log(" → Using default configuration");
|
|
48
44
|
this.config = {};
|
|
49
45
|
}
|
|
50
46
|
} else {
|
|
51
47
|
// Config file doesn't exist, use defaults
|
|
52
|
-
console.log(` → No config file found at: ${this.configPath}`);
|
|
53
|
-
console.log(" → Using default configuration");
|
|
54
48
|
this.config = {};
|
|
55
49
|
}
|
|
56
50
|
|
package/src/core/server.ts
CHANGED
|
@@ -87,8 +87,7 @@ export class VectorServer<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
87
87
|
throw new Error(`Failed to start server on ${hostname}:${port} - server object is invalid`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// Server logs are handled by CLI
|
|
91
|
-
console.log(`→ Vector server running at http://${hostname}:${port}`);
|
|
90
|
+
// Server logs are handled by CLI
|
|
92
91
|
|
|
93
92
|
return this.server;
|
|
94
93
|
} catch (error: any) {
|
package/src/core/vector.ts
CHANGED
|
@@ -86,6 +86,11 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
86
86
|
// Clear previous middleware to avoid accumulation across multiple starts
|
|
87
87
|
this.middlewareManager.clear();
|
|
88
88
|
|
|
89
|
+
// Only clear routes if we're doing auto-discovery
|
|
90
|
+
if (this.config.autoDiscover !== false) {
|
|
91
|
+
this.router.clearRoutes();
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
if (config?.before) {
|
|
90
95
|
this.middlewareManager.addBefore(...config.before);
|
|
91
96
|
}
|
|
@@ -106,10 +111,11 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
106
111
|
|
|
107
112
|
private async discoverRoutes() {
|
|
108
113
|
const routesDir = this.config.routesDir || "./routes";
|
|
114
|
+
const excludePatterns = this.config.routeExcludePatterns;
|
|
109
115
|
|
|
110
116
|
// Always create a new RouteScanner with the current config's routesDir
|
|
111
117
|
// to ensure we're using the correct path from the user's config
|
|
112
|
-
this.routeScanner = new RouteScanner(routesDir);
|
|
118
|
+
this.routeScanner = new RouteScanner(routesDir, excludePatterns);
|
|
113
119
|
|
|
114
120
|
if (!this.routeGenerator) {
|
|
115
121
|
this.routeGenerator = new RouteGenerator();
|
|
@@ -208,9 +214,8 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
208
214
|
this.server.stop();
|
|
209
215
|
this.server = null;
|
|
210
216
|
}
|
|
211
|
-
// Don't reset managers - they
|
|
212
|
-
//
|
|
213
|
-
this.router.clearRoutes();
|
|
217
|
+
// Don't reset managers or routes - they persist for the singleton
|
|
218
|
+
// Routes will be cleared on next startServer() call
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
getServer(): VectorServer<TTypes> | null {
|
package/src/dev/route-scanner.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
-
import { existsSync, promises as fs } from
|
|
2
|
-
import { join, relative, resolve, sep } from
|
|
3
|
-
import type { GeneratedRoute } from
|
|
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
|
-
|
|
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 ===
|
|
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
|
|
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(
|
|
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 ===
|
|
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 ===
|
|
109
|
+
if (module.default && typeof module.default === "function") {
|
|
58
110
|
routes.push({
|
|
59
|
-
name:
|
|
111
|
+
name: "default",
|
|
60
112
|
path: fullPath,
|
|
61
|
-
method:
|
|
113
|
+
method: "GET",
|
|
62
114
|
options: {
|
|
63
|
-
method:
|
|
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 ===
|
|
123
|
+
if (name === "default") continue;
|
|
72
124
|
|
|
73
125
|
// Check for new RouteDefinition format
|
|
74
|
-
if (
|
|
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 !==
|
|
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
|
-
|
|
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;
|
package/src/types/index.ts
CHANGED
|
@@ -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
|