vector-framework 0.9.7 → 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 +516 -346
- package/dist/cli/index.js +63 -64
- package/dist/cli/index.js.map +1 -1
- package/dist/cli.js +480 -424
- package/dist/core/router.d.ts +1 -0
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +73 -25
- package/dist/core/router.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/http.d.ts +2 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +3 -4
- package/dist/http.js.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.mjs +7 -7
- 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 +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/cli/index.ts +78 -78
- package/src/core/router.ts +85 -27
- package/src/core/vector.ts +9 -4
- package/src/dev/route-scanner.ts +76 -18
- package/src/http.ts +8 -13
- package/src/middleware/manager.ts +7 -1
- package/src/types/index.ts +5 -0
package/src/core/router.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RouteEntry } from 'itty-router';
|
|
2
|
+
import { withCookies } from 'itty-router';
|
|
2
3
|
import type { AuthManager } from '../auth/protected';
|
|
3
4
|
import type { CacheManager } from '../cache/manager';
|
|
4
5
|
import { APIError, createResponse } from '../http';
|
|
@@ -113,30 +114,63 @@ export class VectorRouter<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
113
114
|
);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
private
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
private prepareRequest(
|
|
118
|
+
request: VectorRequest<TTypes>,
|
|
119
|
+
options?: {
|
|
120
|
+
params?: Record<string, string>;
|
|
121
|
+
route?: string;
|
|
122
|
+
metadata?: any;
|
|
123
|
+
}
|
|
124
|
+
): void {
|
|
125
|
+
// Initialize context if not present
|
|
126
|
+
if (!request.context) {
|
|
127
|
+
request.context = {} as any;
|
|
128
|
+
}
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
// Set params and route if provided
|
|
131
|
+
if (options?.params !== undefined) {
|
|
132
|
+
request.params = options.params;
|
|
133
|
+
}
|
|
134
|
+
if (options?.route !== undefined) {
|
|
135
|
+
request.route = options.route;
|
|
136
|
+
}
|
|
137
|
+
if (options?.metadata !== undefined) {
|
|
138
|
+
request.metadata = options.metadata;
|
|
139
|
+
}
|
|
125
140
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
// Parse query parameters from URL if not already parsed
|
|
142
|
+
if (!request.query && request.url) {
|
|
143
|
+
const url = new URL(request.url);
|
|
144
|
+
const query: Record<string, string | string[]> = {};
|
|
145
|
+
for (const [key, value] of url.searchParams) {
|
|
146
|
+
if (key in query) {
|
|
147
|
+
if (Array.isArray(query[key])) {
|
|
148
|
+
(query[key] as string[]).push(value);
|
|
149
|
+
} else {
|
|
150
|
+
query[key] = [query[key] as string, value];
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
query[key] = value;
|
|
132
154
|
}
|
|
133
|
-
vectorRequest.query = query;
|
|
134
155
|
}
|
|
156
|
+
request.query = query;
|
|
157
|
+
}
|
|
135
158
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
// Parse cookies if not already parsed
|
|
160
|
+
if (!request.cookies) {
|
|
161
|
+
withCookies(request as any);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private wrapHandler(options: RouteOptions<TTypes>, handler: RouteHandler<TTypes>) {
|
|
166
|
+
return async (request: any) => {
|
|
167
|
+
// Ensure request has required properties
|
|
168
|
+
const vectorRequest = request as VectorRequest<TTypes>;
|
|
169
|
+
|
|
170
|
+
// Prepare the request with common logic
|
|
171
|
+
this.prepareRequest(vectorRequest, {
|
|
172
|
+
metadata: options.metadata
|
|
173
|
+
});
|
|
140
174
|
|
|
141
175
|
request = vectorRequest;
|
|
142
176
|
try {
|
|
@@ -182,22 +216,45 @@ export class VectorRouter<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
182
216
|
let result;
|
|
183
217
|
const cacheOptions = options.cache;
|
|
184
218
|
|
|
219
|
+
// Create cache factory that handles Response objects
|
|
220
|
+
const cacheFactory = async () => {
|
|
221
|
+
const res = await handler(request);
|
|
222
|
+
// If Response, extract data for caching
|
|
223
|
+
if (res instanceof Response) {
|
|
224
|
+
return {
|
|
225
|
+
_isResponse: true,
|
|
226
|
+
body: await res.text(),
|
|
227
|
+
status: res.status,
|
|
228
|
+
headers: Object.fromEntries(res.headers.entries())
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return res;
|
|
232
|
+
};
|
|
233
|
+
|
|
185
234
|
if (cacheOptions && typeof cacheOptions === 'number' && cacheOptions > 0) {
|
|
186
235
|
const cacheKey = this.cacheManager.generateKey(request as any, {
|
|
187
236
|
authUser: request.authUser,
|
|
188
237
|
});
|
|
189
|
-
result = await this.cacheManager.get(cacheKey,
|
|
238
|
+
result = await this.cacheManager.get(cacheKey, cacheFactory, cacheOptions);
|
|
190
239
|
} else if (cacheOptions && typeof cacheOptions === 'object' && cacheOptions.ttl) {
|
|
191
240
|
const cacheKey =
|
|
192
241
|
cacheOptions.key ||
|
|
193
242
|
this.cacheManager.generateKey(request as any, {
|
|
194
243
|
authUser: request.authUser,
|
|
195
244
|
});
|
|
196
|
-
result = await this.cacheManager.get(cacheKey,
|
|
245
|
+
result = await this.cacheManager.get(cacheKey, cacheFactory, cacheOptions.ttl);
|
|
197
246
|
} else {
|
|
198
247
|
result = await handler(request);
|
|
199
248
|
}
|
|
200
249
|
|
|
250
|
+
// Reconstruct Response if it was cached
|
|
251
|
+
if (result && typeof result === 'object' && result._isResponse === true) {
|
|
252
|
+
result = new Response(result.body, {
|
|
253
|
+
status: result.status,
|
|
254
|
+
headers: result.headers
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
201
258
|
let response: Response;
|
|
202
259
|
if (options.rawResponse || result instanceof Response) {
|
|
203
260
|
response = result instanceof Response ? result : new Response(result);
|
|
@@ -235,16 +292,17 @@ export class VectorRouter<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
235
292
|
const url = new URL(request.url);
|
|
236
293
|
const pathname = url.pathname;
|
|
237
294
|
|
|
238
|
-
for (const [method, regex, handlers] of this.routes) {
|
|
295
|
+
for (const [method, regex, handlers, path] of this.routes) {
|
|
239
296
|
if (request.method === 'OPTIONS' || request.method === method) {
|
|
240
297
|
const match = pathname.match(regex);
|
|
241
298
|
if (match) {
|
|
242
299
|
const req = request as any as VectorRequest<TTypes>;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
300
|
+
|
|
301
|
+
// Prepare the request with common logic
|
|
302
|
+
this.prepareRequest(req, {
|
|
303
|
+
params: match.groups || {},
|
|
304
|
+
route: path || pathname
|
|
305
|
+
});
|
|
248
306
|
|
|
249
307
|
for (const handler of handlers) {
|
|
250
308
|
const response = await handler(req as 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 () => {
|
package/src/http.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cors,
|
|
3
|
-
type IRequest,
|
|
4
|
-
type RouteEntry,
|
|
5
|
-
withContent,
|
|
6
|
-
withCookies,
|
|
7
|
-
} from "itty-router";
|
|
1
|
+
import { cors, type IRequest, type RouteEntry, withContent } from "itty-router";
|
|
8
2
|
import { CONTENT_TYPES, HTTP_STATUS } from "./constants";
|
|
9
3
|
import type {
|
|
4
|
+
CacheOptions,
|
|
10
5
|
DefaultVectorTypes,
|
|
11
6
|
GetAuthType,
|
|
12
7
|
VectorRequest,
|
|
13
8
|
VectorTypes,
|
|
14
9
|
} from "./types";
|
|
10
|
+
import { getVectorInstance } from "./core/vector";
|
|
15
11
|
|
|
16
12
|
export interface ProtectedRequest<
|
|
17
13
|
TTypes extends VectorTypes = DefaultVectorTypes
|
|
@@ -33,7 +29,9 @@ interface ExtendedApiOptions extends ApiOptions {
|
|
|
33
29
|
path: string;
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
export interface RouteDefinition<
|
|
32
|
+
export interface RouteDefinition<
|
|
33
|
+
TTypes extends VectorTypes = DefaultVectorTypes
|
|
34
|
+
> {
|
|
37
35
|
entry: RouteEntry;
|
|
38
36
|
options: ExtendedApiOptions;
|
|
39
37
|
handler: (req: VectorRequest<TTypes>) => Promise<unknown>;
|
|
@@ -64,7 +62,7 @@ export function route<TTypes extends VectorTypes = DefaultVectorTypes>(
|
|
|
64
62
|
return {
|
|
65
63
|
entry,
|
|
66
64
|
options,
|
|
67
|
-
handler: fn
|
|
65
|
+
handler: fn,
|
|
68
66
|
};
|
|
69
67
|
}
|
|
70
68
|
|
|
@@ -263,7 +261,6 @@ export const protectedRoute = async <
|
|
|
263
261
|
responseContentType?: string
|
|
264
262
|
) => {
|
|
265
263
|
// Get the Vector instance to access the protected handler
|
|
266
|
-
const { getVectorInstance } = await import("./core/vector");
|
|
267
264
|
const vector = getVectorInstance();
|
|
268
265
|
|
|
269
266
|
const protectedHandler = vector.getProtectedHandler();
|
|
@@ -290,7 +287,7 @@ export interface ApiOptions {
|
|
|
290
287
|
expose?: boolean;
|
|
291
288
|
rawRequest?: boolean;
|
|
292
289
|
rawResponse?: boolean;
|
|
293
|
-
cache?: number | null;
|
|
290
|
+
cache?: CacheOptions | number | null;
|
|
294
291
|
responseContentType?: string;
|
|
295
292
|
}
|
|
296
293
|
|
|
@@ -325,8 +322,6 @@ export function api<TTypes extends VectorTypes = DefaultVectorTypes>(
|
|
|
325
322
|
await withContent(request);
|
|
326
323
|
}
|
|
327
324
|
|
|
328
|
-
withCookies(request);
|
|
329
|
-
|
|
330
325
|
// Cache handling is now done in the router
|
|
331
326
|
const result = await fn(request as any as VectorRequest<TTypes>);
|
|
332
327
|
|
|
@@ -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
|
@@ -51,6 +51,9 @@ export interface VectorRequest<TTypes extends VectorTypes = DefaultVectorTypes>
|
|
|
51
51
|
metadata?: GetMetadataType<TTypes>;
|
|
52
52
|
content?: any;
|
|
53
53
|
params?: Record<string, string>;
|
|
54
|
+
query: { [key: string]: string | string[] | undefined };
|
|
55
|
+
headers: Headers;
|
|
56
|
+
cookies?: Record<string, string>;
|
|
54
57
|
startTime?: number;
|
|
55
58
|
[key: string]: any;
|
|
56
59
|
}
|
|
@@ -82,6 +85,7 @@ export interface VectorConfig<TTypes extends VectorTypes = DefaultVectorTypes> {
|
|
|
82
85
|
before?: BeforeMiddlewareHandler<TTypes>[];
|
|
83
86
|
finally?: AfterMiddlewareHandler<TTypes>[];
|
|
84
87
|
routesDir?: string;
|
|
88
|
+
routeExcludePatterns?: string[];
|
|
85
89
|
autoDiscover?: boolean;
|
|
86
90
|
idleTimeout?: number;
|
|
87
91
|
}
|
|
@@ -94,6 +98,7 @@ export interface VectorConfigSchema<TTypes extends VectorTypes = DefaultVectorTy
|
|
|
94
98
|
reusePort?: boolean;
|
|
95
99
|
development?: boolean;
|
|
96
100
|
routesDir?: string;
|
|
101
|
+
routeExcludePatterns?: string[];
|
|
97
102
|
idleTimeout?: number;
|
|
98
103
|
|
|
99
104
|
// Middleware functions
|