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.
- package/README.md +33 -2
- package/dist/cli/index.js +63 -64
- package/dist/cli/index.js.map +1 -1
- package/dist/cli.js +166 -129
- 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 +1 -1
- package/src/cli/index.ts +78 -78
- 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/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
|