vector-framework 0.9.0 → 0.9.1

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/dist/cli.js ADDED
@@ -0,0 +1,1523 @@
1
+ #!/usr/bin/env node
2
+ // @bun
3
+ import { createRequire } from "node:module";
4
+ var __defProp = Object.defineProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, {
8
+ get: all[name],
9
+ enumerable: true,
10
+ configurable: true,
11
+ set: (newValue) => all[name] = () => newValue
12
+ });
13
+ };
14
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
15
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
16
+
17
+ // src/auth/protected.ts
18
+ class AuthManager {
19
+ protectedHandler = null;
20
+ setProtectedHandler(handler) {
21
+ this.protectedHandler = handler;
22
+ }
23
+ async authenticate(request) {
24
+ if (!this.protectedHandler) {
25
+ throw new Error("Protected handler not configured. Use vector.protected() to set authentication handler.");
26
+ }
27
+ try {
28
+ const authUser = await this.protectedHandler(request);
29
+ request.authUser = authUser;
30
+ return authUser;
31
+ } catch (error) {
32
+ throw new Error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`);
33
+ }
34
+ }
35
+ isAuthenticated(request) {
36
+ return !!request.authUser;
37
+ }
38
+ getUser(request) {
39
+ return request.authUser || null;
40
+ }
41
+ }
42
+
43
+ // src/constants/index.ts
44
+ var HTTP_STATUS, DEFAULT_CONFIG, CONTENT_TYPES;
45
+ var init_constants = __esm(() => {
46
+ HTTP_STATUS = {
47
+ OK: 200,
48
+ CREATED: 201,
49
+ ACCEPTED: 202,
50
+ NON_AUTHORITATIVE_INFORMATION: 203,
51
+ NO_CONTENT: 204,
52
+ RESET_CONTENT: 205,
53
+ PARTIAL_CONTENT: 206,
54
+ MULTI_STATUS: 207,
55
+ ALREADY_REPORTED: 208,
56
+ IM_USED: 226,
57
+ MULTIPLE_CHOICES: 300,
58
+ MOVED_PERMANENTLY: 301,
59
+ FOUND: 302,
60
+ SEE_OTHER: 303,
61
+ NOT_MODIFIED: 304,
62
+ USE_PROXY: 305,
63
+ TEMPORARY_REDIRECT: 307,
64
+ PERMANENT_REDIRECT: 308,
65
+ BAD_REQUEST: 400,
66
+ UNAUTHORIZED: 401,
67
+ PAYMENT_REQUIRED: 402,
68
+ FORBIDDEN: 403,
69
+ NOT_FOUND: 404,
70
+ METHOD_NOT_ALLOWED: 405,
71
+ NOT_ACCEPTABLE: 406,
72
+ PROXY_AUTHENTICATION_REQUIRED: 407,
73
+ REQUEST_TIMEOUT: 408,
74
+ CONFLICT: 409,
75
+ GONE: 410,
76
+ LENGTH_REQUIRED: 411,
77
+ PRECONDITION_FAILED: 412,
78
+ PAYLOAD_TOO_LARGE: 413,
79
+ URI_TOO_LONG: 414,
80
+ UNSUPPORTED_MEDIA_TYPE: 415,
81
+ RANGE_NOT_SATISFIABLE: 416,
82
+ EXPECTATION_FAILED: 417,
83
+ IM_A_TEAPOT: 418,
84
+ MISDIRECTED_REQUEST: 421,
85
+ UNPROCESSABLE_ENTITY: 422,
86
+ LOCKED: 423,
87
+ FAILED_DEPENDENCY: 424,
88
+ TOO_EARLY: 425,
89
+ UPGRADE_REQUIRED: 426,
90
+ PRECONDITION_REQUIRED: 428,
91
+ TOO_MANY_REQUESTS: 429,
92
+ REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
93
+ UNAVAILABLE_FOR_LEGAL_REASONS: 451,
94
+ INTERNAL_SERVER_ERROR: 500,
95
+ NOT_IMPLEMENTED: 501,
96
+ BAD_GATEWAY: 502,
97
+ SERVICE_UNAVAILABLE: 503,
98
+ GATEWAY_TIMEOUT: 504,
99
+ HTTP_VERSION_NOT_SUPPORTED: 505,
100
+ VARIANT_ALSO_NEGOTIATES: 506,
101
+ INSUFFICIENT_STORAGE: 507,
102
+ LOOP_DETECTED: 508,
103
+ NOT_EXTENDED: 510,
104
+ NETWORK_AUTHENTICATION_REQUIRED: 511
105
+ };
106
+ DEFAULT_CONFIG = {
107
+ PORT: 3000,
108
+ HOSTNAME: "localhost",
109
+ ROUTES_DIR: "./routes",
110
+ CACHE_TTL: 0,
111
+ CORS_MAX_AGE: 86400
112
+ };
113
+ CONTENT_TYPES = {
114
+ JSON: "application/json",
115
+ TEXT: "text/plain",
116
+ HTML: "text/html",
117
+ FORM_URLENCODED: "application/x-www-form-urlencoded",
118
+ MULTIPART: "multipart/form-data"
119
+ };
120
+ });
121
+
122
+ // src/cache/manager.ts
123
+ class CacheManager {
124
+ cacheHandler = null;
125
+ memoryCache = new Map;
126
+ cleanupInterval = null;
127
+ setCacheHandler(handler) {
128
+ this.cacheHandler = handler;
129
+ }
130
+ async get(key, factory, ttl = DEFAULT_CONFIG.CACHE_TTL) {
131
+ if (ttl <= 0) {
132
+ return factory();
133
+ }
134
+ if (this.cacheHandler) {
135
+ return this.cacheHandler(key, factory, ttl);
136
+ }
137
+ return this.getFromMemoryCache(key, factory, ttl);
138
+ }
139
+ async getFromMemoryCache(key, factory, ttl) {
140
+ const now = Date.now();
141
+ const cached = this.memoryCache.get(key);
142
+ if (this.isCacheValid(cached, now)) {
143
+ return cached.value;
144
+ }
145
+ const value = await factory();
146
+ this.setInMemoryCache(key, value, ttl);
147
+ return value;
148
+ }
149
+ isCacheValid(entry, now) {
150
+ return entry !== undefined && entry.expires > now;
151
+ }
152
+ setInMemoryCache(key, value, ttl) {
153
+ const expires = Date.now() + ttl * 1000;
154
+ this.memoryCache.set(key, { value, expires });
155
+ this.scheduleCleanup();
156
+ }
157
+ scheduleCleanup() {
158
+ if (this.cleanupInterval)
159
+ return;
160
+ this.cleanupInterval = setInterval(() => {
161
+ this.cleanupExpired();
162
+ }, 60000);
163
+ }
164
+ cleanupExpired() {
165
+ const now = Date.now();
166
+ for (const [key, entry] of this.memoryCache.entries()) {
167
+ if (entry.expires <= now) {
168
+ this.memoryCache.delete(key);
169
+ }
170
+ }
171
+ if (this.memoryCache.size === 0 && this.cleanupInterval) {
172
+ clearInterval(this.cleanupInterval);
173
+ this.cleanupInterval = null;
174
+ }
175
+ }
176
+ clear() {
177
+ this.memoryCache.clear();
178
+ if (this.cleanupInterval) {
179
+ clearInterval(this.cleanupInterval);
180
+ this.cleanupInterval = null;
181
+ }
182
+ }
183
+ async set(key, value, ttl = DEFAULT_CONFIG.CACHE_TTL) {
184
+ if (ttl <= 0) {
185
+ return;
186
+ }
187
+ if (this.cacheHandler) {
188
+ await this.cacheHandler(key, async () => value, ttl);
189
+ return;
190
+ }
191
+ this.setInMemoryCache(key, value, ttl);
192
+ }
193
+ delete(key) {
194
+ return this.memoryCache.delete(key);
195
+ }
196
+ has(key) {
197
+ const entry = this.memoryCache.get(key);
198
+ if (!entry)
199
+ return false;
200
+ if (entry.expires <= Date.now()) {
201
+ this.memoryCache.delete(key);
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ generateKey(request, options) {
207
+ const url = new URL(request.url);
208
+ const parts = [
209
+ request.method,
210
+ url.pathname,
211
+ url.search,
212
+ options?.authUser?.id || "anonymous"
213
+ ];
214
+ return parts.join(":");
215
+ }
216
+ }
217
+ var init_manager = __esm(() => {
218
+ init_constants();
219
+ });
220
+
221
+ // src/dev/route-generator.ts
222
+ import { promises as fs } from "node:fs";
223
+ import { dirname, relative } from "node:path";
224
+
225
+ class RouteGenerator {
226
+ outputPath;
227
+ constructor(outputPath = "./.vector/routes.generated.ts") {
228
+ this.outputPath = outputPath;
229
+ }
230
+ async generate(routes) {
231
+ const outputDir = dirname(this.outputPath);
232
+ await fs.mkdir(outputDir, { recursive: true });
233
+ const imports = [];
234
+ const groupedByFile = new Map;
235
+ for (const route of routes) {
236
+ if (!groupedByFile.has(route.path)) {
237
+ groupedByFile.set(route.path, []);
238
+ }
239
+ groupedByFile.get(route.path).push(route);
240
+ }
241
+ let importIndex = 0;
242
+ const routeEntries = [];
243
+ for (const [filePath, fileRoutes] of groupedByFile) {
244
+ const relativePath = relative(dirname(this.outputPath), filePath).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
245
+ const importName = `route_${importIndex++}`;
246
+ const namedImports = fileRoutes.filter((r) => r.name !== "default").map((r) => r.name);
247
+ if (fileRoutes.some((r) => r.name === "default")) {
248
+ if (namedImports.length > 0) {
249
+ imports.push(`import ${importName}, { ${namedImports.join(", ")} } from '${relativePath}';`);
250
+ } else {
251
+ imports.push(`import ${importName} from '${relativePath}';`);
252
+ }
253
+ } else if (namedImports.length > 0) {
254
+ imports.push(`import { ${namedImports.join(", ")} } from '${relativePath}';`);
255
+ }
256
+ for (const route of fileRoutes) {
257
+ const routeVar = route.name === "default" ? importName : route.name;
258
+ routeEntries.push(` ${routeVar},`);
259
+ }
260
+ }
261
+ const content = `// This file is auto-generated. Do not edit manually.
262
+ // Generated at: ${new Date().toISOString()}
263
+
264
+ ${imports.join(`
265
+ `)}
266
+
267
+ export const routes = [
268
+ ${routeEntries.join(`
269
+ `)}
270
+ ];
271
+
272
+ export default routes;
273
+ `;
274
+ await fs.writeFile(this.outputPath, content, "utf-8");
275
+ console.log(`Generated routes file: ${this.outputPath}`);
276
+ }
277
+ async generateDynamic(routes) {
278
+ const routeEntries = [];
279
+ for (const route of routes) {
280
+ const routeObj = JSON.stringify({
281
+ method: route.method,
282
+ path: route.options.path,
283
+ options: route.options
284
+ });
285
+ routeEntries.push(` await import('${route.path}').then(m => ({
286
+ ...${routeObj},
287
+ handler: m.${route.name === "default" ? "default" : route.name}
288
+ }))`);
289
+ }
290
+ return `export const loadRoutes = async () => {
291
+ return Promise.all([
292
+ ${routeEntries.join(`,
293
+ `)}
294
+ ]);
295
+ };`;
296
+ }
297
+ }
298
+ var init_route_generator = () => {};
299
+
300
+ // src/dev/route-scanner.ts
301
+ import { existsSync, promises as fs2 } from "node:fs";
302
+ import { join, relative as relative2, resolve, sep } from "node:path";
303
+
304
+ class RouteScanner {
305
+ routesDir;
306
+ constructor(routesDir = "./routes") {
307
+ this.routesDir = resolve(process.cwd(), routesDir);
308
+ }
309
+ async scan() {
310
+ const routes = [];
311
+ if (!existsSync(this.routesDir)) {
312
+ console.log(` → Routes directory not found: ${this.routesDir}`);
313
+ console.log(" → No routes will be auto-discovered");
314
+ return [];
315
+ }
316
+ try {
317
+ console.log(` → Scanning routes from: ${this.routesDir}`);
318
+ await this.scanDirectory(this.routesDir, routes);
319
+ if (routes.length > 0) {
320
+ console.log(` ✓ Found ${routes.length} route${routes.length === 1 ? "" : "s"}`);
321
+ }
322
+ } catch (error) {
323
+ if (error.code === "ENOENT") {
324
+ console.warn(` ✗ Routes directory not accessible: ${this.routesDir}`);
325
+ return [];
326
+ }
327
+ throw error;
328
+ }
329
+ return routes;
330
+ }
331
+ async scanDirectory(dir, routes, basePath = "") {
332
+ const entries = await fs2.readdir(dir);
333
+ for (const entry of entries) {
334
+ const fullPath = join(dir, entry);
335
+ const stats = await fs2.stat(fullPath);
336
+ if (stats.isDirectory()) {
337
+ const newBasePath = basePath ? `${basePath}/${entry}` : entry;
338
+ await this.scanDirectory(fullPath, routes, newBasePath);
339
+ } else if (entry.endsWith(".ts") || entry.endsWith(".js")) {
340
+ const routePath = relative2(this.routesDir, fullPath).replace(/\.(ts|js)$/, "").split(sep).join("/");
341
+ try {
342
+ const importPath = process.platform === "win32" ? `file:///${fullPath.replace(/\\/g, "/")}` : fullPath;
343
+ const module = await import(importPath);
344
+ if (module.default && typeof module.default === "function") {
345
+ routes.push({
346
+ name: "default",
347
+ path: fullPath,
348
+ method: "GET",
349
+ options: {
350
+ method: "GET",
351
+ path: `/${routePath}`,
352
+ expose: true
353
+ }
354
+ });
355
+ }
356
+ for (const [name, value] of Object.entries(module)) {
357
+ if (name === "default")
358
+ continue;
359
+ if (value && typeof value === "object" && "entry" in value && "options" in value && "handler" in value) {
360
+ const routeDef = value;
361
+ routes.push({
362
+ name,
363
+ path: fullPath,
364
+ method: routeDef.options.method,
365
+ options: routeDef.options
366
+ });
367
+ } else if (Array.isArray(value) && value.length >= 4) {
368
+ const [method, , , path] = value;
369
+ routes.push({
370
+ name,
371
+ path: fullPath,
372
+ method,
373
+ options: {
374
+ method,
375
+ path,
376
+ expose: true
377
+ }
378
+ });
379
+ }
380
+ }
381
+ } catch (error) {
382
+ console.error(`Failed to load route from ${fullPath}:`, error);
383
+ }
384
+ }
385
+ }
386
+ }
387
+ enableWatch(callback) {
388
+ if (typeof Bun !== "undefined" && Bun.env.NODE_ENV === "development") {
389
+ console.log(`Watching for route changes in ${this.routesDir}`);
390
+ setInterval(async () => {
391
+ await callback();
392
+ }, 1000);
393
+ }
394
+ }
395
+ }
396
+ var init_route_scanner = () => {};
397
+
398
+ // src/middleware/manager.ts
399
+ class MiddlewareManager {
400
+ beforeHandlers = [];
401
+ finallyHandlers = [];
402
+ addBefore(...handlers) {
403
+ this.beforeHandlers.push(...handlers);
404
+ }
405
+ addFinally(...handlers) {
406
+ this.finallyHandlers.push(...handlers);
407
+ }
408
+ async executeBefore(request) {
409
+ let currentRequest = request;
410
+ for (const handler of this.beforeHandlers) {
411
+ const result = await handler(currentRequest);
412
+ if (result instanceof Response) {
413
+ return result;
414
+ }
415
+ currentRequest = result;
416
+ }
417
+ return currentRequest;
418
+ }
419
+ async executeFinally(response, request) {
420
+ let currentResponse = response;
421
+ for (const handler of this.finallyHandlers) {
422
+ currentResponse = await handler(currentResponse, request);
423
+ }
424
+ return currentResponse;
425
+ }
426
+ clone() {
427
+ const manager = new MiddlewareManager;
428
+ manager.beforeHandlers = [...this.beforeHandlers];
429
+ manager.finallyHandlers = [...this.finallyHandlers];
430
+ return manager;
431
+ }
432
+ clear() {
433
+ this.beforeHandlers = [];
434
+ this.finallyHandlers = [];
435
+ }
436
+ }
437
+
438
+ // src/utils/path.ts
439
+ function toFileUrl(path) {
440
+ return process.platform === "win32" ? `file:///${path.replace(/\\/g, "/")}` : path;
441
+ }
442
+
443
+ // node_modules/itty-router/index.mjs
444
+ var r = (e = "text/plain; charset=utf-8", t) => (r2, o = {}) => {
445
+ if (r2 === undefined || r2 instanceof Response)
446
+ return r2;
447
+ const a = new Response(t?.(r2) ?? r2, o.url ? undefined : o);
448
+ return a.headers.set("content-type", e), a;
449
+ }, o, p, f, u, h, g, y = (e = {}) => {
450
+ const { origin: t = "*", credentials: r2 = false, allowMethods: o2 = "*", allowHeaders: a, exposeHeaders: s, maxAge: c } = e, n = (e2) => {
451
+ const o3 = e2?.headers.get("origin");
452
+ return t === true ? o3 : t instanceof RegExp ? t.test(o3) ? o3 : undefined : Array.isArray(t) ? t.includes(o3) ? o3 : undefined : t instanceof Function ? t(o3) : t == "*" && r2 ? o3 : t;
453
+ }, l = (e2, t2) => {
454
+ for (const [r3, o3] of Object.entries(t2))
455
+ o3 && e2.headers.append(r3, o3);
456
+ return e2;
457
+ };
458
+ return { corsify: (e2, t2) => e2?.headers?.get("access-control-allow-origin") || e2.status == 101 ? e2 : l(e2.clone(), { "access-control-allow-origin": n(t2), "access-control-allow-credentials": r2 }), preflight: (e2) => {
459
+ if (e2.method == "OPTIONS") {
460
+ const t2 = new Response(null, { status: 204 });
461
+ return l(t2, { "access-control-allow-origin": n(e2), "access-control-allow-methods": o2?.join?.(",") ?? o2, "access-control-expose-headers": s?.join?.(",") ?? s, "access-control-allow-headers": a?.join?.(",") ?? a ?? e2.headers.get("access-control-request-headers"), "access-control-max-age": c, "access-control-allow-credentials": r2 });
462
+ }
463
+ } };
464
+ };
465
+ var init_itty_router = __esm(() => {
466
+ o = r("application/json; charset=utf-8", JSON.stringify);
467
+ p = r("text/plain; charset=utf-8", String);
468
+ f = r("text/html");
469
+ u = r("image/jpeg");
470
+ h = r("image/png");
471
+ g = r("image/webp");
472
+ });
473
+
474
+ // src/http.ts
475
+ function stringifyData(data) {
476
+ return JSON.stringify(data ?? null, (_key, value) => typeof value === "bigint" ? value.toString() : value);
477
+ }
478
+ function createErrorResponse(code, message, contentType) {
479
+ const errorBody = {
480
+ error: true,
481
+ message,
482
+ statusCode: code,
483
+ timestamp: new Date().toISOString()
484
+ };
485
+ return createResponse(code, errorBody, contentType);
486
+ }
487
+ function createResponse(statusCode, data, contentType = CONTENT_TYPES.JSON) {
488
+ const body = contentType === CONTENT_TYPES.JSON ? stringifyData(data) : data;
489
+ return new Response(body, {
490
+ status: statusCode,
491
+ headers: { "content-type": contentType }
492
+ });
493
+ }
494
+ var preflight, corsify, APIError;
495
+ var init_http = __esm(() => {
496
+ init_itty_router();
497
+ init_constants();
498
+ ({ preflight, corsify } = y({
499
+ origin: "*",
500
+ credentials: true,
501
+ allowHeaders: "Content-Type, Authorization",
502
+ allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
503
+ exposeHeaders: "Authorization",
504
+ maxAge: 86400
505
+ }));
506
+ APIError = {
507
+ badRequest: (msg = "Bad Request", contentType) => createErrorResponse(HTTP_STATUS.BAD_REQUEST, msg, contentType),
508
+ unauthorized: (msg = "Unauthorized", contentType) => createErrorResponse(HTTP_STATUS.UNAUTHORIZED, msg, contentType),
509
+ paymentRequired: (msg = "Payment Required", contentType) => createErrorResponse(402, msg, contentType),
510
+ forbidden: (msg = "Forbidden", contentType) => createErrorResponse(HTTP_STATUS.FORBIDDEN, msg, contentType),
511
+ notFound: (msg = "Not Found", contentType) => createErrorResponse(HTTP_STATUS.NOT_FOUND, msg, contentType),
512
+ methodNotAllowed: (msg = "Method Not Allowed", contentType) => createErrorResponse(405, msg, contentType),
513
+ notAcceptable: (msg = "Not Acceptable", contentType) => createErrorResponse(406, msg, contentType),
514
+ requestTimeout: (msg = "Request Timeout", contentType) => createErrorResponse(408, msg, contentType),
515
+ conflict: (msg = "Conflict", contentType) => createErrorResponse(HTTP_STATUS.CONFLICT, msg, contentType),
516
+ gone: (msg = "Gone", contentType) => createErrorResponse(410, msg, contentType),
517
+ lengthRequired: (msg = "Length Required", contentType) => createErrorResponse(411, msg, contentType),
518
+ preconditionFailed: (msg = "Precondition Failed", contentType) => createErrorResponse(412, msg, contentType),
519
+ payloadTooLarge: (msg = "Payload Too Large", contentType) => createErrorResponse(413, msg, contentType),
520
+ uriTooLong: (msg = "URI Too Long", contentType) => createErrorResponse(414, msg, contentType),
521
+ unsupportedMediaType: (msg = "Unsupported Media Type", contentType) => createErrorResponse(415, msg, contentType),
522
+ rangeNotSatisfiable: (msg = "Range Not Satisfiable", contentType) => createErrorResponse(416, msg, contentType),
523
+ expectationFailed: (msg = "Expectation Failed", contentType) => createErrorResponse(417, msg, contentType),
524
+ imATeapot: (msg = "I'm a teapot", contentType) => createErrorResponse(418, msg, contentType),
525
+ misdirectedRequest: (msg = "Misdirected Request", contentType) => createErrorResponse(421, msg, contentType),
526
+ unprocessableEntity: (msg = "Unprocessable Entity", contentType) => createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
527
+ locked: (msg = "Locked", contentType) => createErrorResponse(423, msg, contentType),
528
+ failedDependency: (msg = "Failed Dependency", contentType) => createErrorResponse(424, msg, contentType),
529
+ tooEarly: (msg = "Too Early", contentType) => createErrorResponse(425, msg, contentType),
530
+ upgradeRequired: (msg = "Upgrade Required", contentType) => createErrorResponse(426, msg, contentType),
531
+ preconditionRequired: (msg = "Precondition Required", contentType) => createErrorResponse(428, msg, contentType),
532
+ tooManyRequests: (msg = "Too Many Requests", contentType) => createErrorResponse(429, msg, contentType),
533
+ requestHeaderFieldsTooLarge: (msg = "Request Header Fields Too Large", contentType) => createErrorResponse(431, msg, contentType),
534
+ unavailableForLegalReasons: (msg = "Unavailable For Legal Reasons", contentType) => createErrorResponse(451, msg, contentType),
535
+ internalServerError: (msg = "Internal Server Error", contentType) => createErrorResponse(HTTP_STATUS.INTERNAL_SERVER_ERROR, msg, contentType),
536
+ notImplemented: (msg = "Not Implemented", contentType) => createErrorResponse(501, msg, contentType),
537
+ badGateway: (msg = "Bad Gateway", contentType) => createErrorResponse(502, msg, contentType),
538
+ serviceUnavailable: (msg = "Service Unavailable", contentType) => createErrorResponse(503, msg, contentType),
539
+ gatewayTimeout: (msg = "Gateway Timeout", contentType) => createErrorResponse(504, msg, contentType),
540
+ httpVersionNotSupported: (msg = "HTTP Version Not Supported", contentType) => createErrorResponse(505, msg, contentType),
541
+ variantAlsoNegotiates: (msg = "Variant Also Negotiates", contentType) => createErrorResponse(506, msg, contentType),
542
+ insufficientStorage: (msg = "Insufficient Storage", contentType) => createErrorResponse(507, msg, contentType),
543
+ loopDetected: (msg = "Loop Detected", contentType) => createErrorResponse(508, msg, contentType),
544
+ notExtended: (msg = "Not Extended", contentType) => createErrorResponse(510, msg, contentType),
545
+ networkAuthenticationRequired: (msg = "Network Authentication Required", contentType) => createErrorResponse(511, msg, contentType),
546
+ invalidArgument: (msg = "Invalid Argument", contentType) => createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
547
+ rateLimitExceeded: (msg = "Rate Limit Exceeded", contentType) => createErrorResponse(429, msg, contentType),
548
+ maintenance: (msg = "Service Under Maintenance", contentType) => createErrorResponse(503, msg, contentType),
549
+ custom: (statusCode, msg, contentType) => createErrorResponse(statusCode, msg, contentType)
550
+ };
551
+ });
552
+
553
+ // src/core/router.ts
554
+ class VectorRouter {
555
+ middlewareManager;
556
+ authManager;
557
+ cacheManager;
558
+ routes = [];
559
+ constructor(middlewareManager, authManager, cacheManager) {
560
+ this.middlewareManager = middlewareManager;
561
+ this.authManager = authManager;
562
+ this.cacheManager = cacheManager;
563
+ }
564
+ getRouteSpecificity(path) {
565
+ const STATIC_SEGMENT_WEIGHT = 1000;
566
+ const PARAM_SEGMENT_WEIGHT = 10;
567
+ const WILDCARD_WEIGHT = 1;
568
+ const EXACT_MATCH_BONUS = 1e4;
569
+ let score = 0;
570
+ const segments = path.split("/").filter(Boolean);
571
+ for (const segment of segments) {
572
+ if (this.isStaticSegment(segment)) {
573
+ score += STATIC_SEGMENT_WEIGHT;
574
+ } else if (this.isParamSegment(segment)) {
575
+ score += PARAM_SEGMENT_WEIGHT;
576
+ } else if (this.isWildcardSegment(segment)) {
577
+ score += WILDCARD_WEIGHT;
578
+ }
579
+ }
580
+ score += path.length;
581
+ if (this.isExactPath(path)) {
582
+ score += EXACT_MATCH_BONUS;
583
+ }
584
+ return score;
585
+ }
586
+ isStaticSegment(segment) {
587
+ return !segment.startsWith(":") && !segment.includes("*");
588
+ }
589
+ isParamSegment(segment) {
590
+ return segment.startsWith(":");
591
+ }
592
+ isWildcardSegment(segment) {
593
+ return segment.includes("*");
594
+ }
595
+ isExactPath(path) {
596
+ return !path.includes(":") && !path.includes("*");
597
+ }
598
+ sortRoutes() {
599
+ this.routes.sort((a, b) => {
600
+ const pathA = this.extractPath(a);
601
+ const pathB = this.extractPath(b);
602
+ const scoreA = this.getRouteSpecificity(pathA);
603
+ const scoreB = this.getRouteSpecificity(pathB);
604
+ return scoreB - scoreA;
605
+ });
606
+ }
607
+ extractPath(route) {
608
+ const PATH_INDEX = 3;
609
+ return route[PATH_INDEX] || "";
610
+ }
611
+ route(options, handler) {
612
+ const wrappedHandler = this.wrapHandler(options, handler);
613
+ const routeEntry = [
614
+ options.method.toUpperCase(),
615
+ this.createRouteRegex(options.path),
616
+ [wrappedHandler],
617
+ options.path
618
+ ];
619
+ this.routes.push(routeEntry);
620
+ this.sortRoutes();
621
+ return routeEntry;
622
+ }
623
+ createRouteRegex(path) {
624
+ return RegExp(`^${path.replace(/\/+(\/|$)/g, "$1").replace(/(\/?\.?):(\w+)\+/g, "($1(?<$2>*))").replace(/(\/?\.?):(\w+)/g, "($1(?<$2>[^$1/]+?))").replace(/\./g, "\\.").replace(/(\/?)\*/g, "($1.*)?")}/*$`);
625
+ }
626
+ wrapHandler(options, handler) {
627
+ return async (request) => {
628
+ const vectorRequest = request;
629
+ if (!vectorRequest.context) {
630
+ vectorRequest.context = {};
631
+ }
632
+ if (!vectorRequest.query && vectorRequest.url) {
633
+ const url = new URL(vectorRequest.url);
634
+ const query = {};
635
+ for (let [k, v] of url.searchParams) {
636
+ query[k] = query[k] ? [].concat(query[k], v) : v;
637
+ }
638
+ vectorRequest.query = query;
639
+ }
640
+ if (options.metadata) {
641
+ vectorRequest.metadata = options.metadata;
642
+ }
643
+ request = vectorRequest;
644
+ try {
645
+ if (options.expose === false) {
646
+ return APIError.forbidden("Forbidden");
647
+ }
648
+ const beforeResult = await this.middlewareManager.executeBefore(request);
649
+ if (beforeResult instanceof Response) {
650
+ return beforeResult;
651
+ }
652
+ request = beforeResult;
653
+ if (options.auth) {
654
+ try {
655
+ await this.authManager.authenticate(request);
656
+ } catch (error) {
657
+ return APIError.unauthorized(error instanceof Error ? error.message : "Authentication failed", options.responseContentType);
658
+ }
659
+ }
660
+ if (!options.rawRequest && request.method !== "GET" && request.method !== "HEAD") {
661
+ try {
662
+ const contentType = request.headers.get("content-type");
663
+ if (contentType?.includes("application/json")) {
664
+ request.content = await request.json();
665
+ } else if (contentType?.includes("application/x-www-form-urlencoded")) {
666
+ request.content = Object.fromEntries(await request.formData());
667
+ } else if (contentType?.includes("multipart/form-data")) {
668
+ request.content = await request.formData();
669
+ } else {
670
+ request.content = await request.text();
671
+ }
672
+ } catch {
673
+ request.content = null;
674
+ }
675
+ }
676
+ let result;
677
+ const cacheOptions = options.cache;
678
+ if (cacheOptions && typeof cacheOptions === "number" && cacheOptions > 0) {
679
+ const cacheKey = this.cacheManager.generateKey(request, {
680
+ authUser: request.authUser
681
+ });
682
+ result = await this.cacheManager.get(cacheKey, () => handler(request), cacheOptions);
683
+ } else if (cacheOptions && typeof cacheOptions === "object" && cacheOptions.ttl) {
684
+ const cacheKey = cacheOptions.key || this.cacheManager.generateKey(request, {
685
+ authUser: request.authUser
686
+ });
687
+ result = await this.cacheManager.get(cacheKey, () => handler(request), cacheOptions.ttl);
688
+ } else {
689
+ result = await handler(request);
690
+ }
691
+ let response;
692
+ if (options.rawResponse || result instanceof Response) {
693
+ response = result instanceof Response ? result : new Response(result);
694
+ } else {
695
+ response = createResponse(200, result, options.responseContentType);
696
+ }
697
+ response = await this.middlewareManager.executeFinally(response, request);
698
+ return response;
699
+ } catch (error) {
700
+ if (error instanceof Response) {
701
+ return error;
702
+ }
703
+ console.error("Route handler error:", error);
704
+ return APIError.internalServerError(error instanceof Error ? error.message : String(error), options.responseContentType);
705
+ }
706
+ };
707
+ }
708
+ addRoute(routeEntry) {
709
+ this.routes.push(routeEntry);
710
+ this.sortRoutes();
711
+ }
712
+ getRoutes() {
713
+ return this.routes;
714
+ }
715
+ async handle(request) {
716
+ const url = new URL(request.url);
717
+ const pathname = url.pathname;
718
+ for (const [method, regex, handlers] of this.routes) {
719
+ if (request.method === "OPTIONS" || request.method === method) {
720
+ const match = pathname.match(regex);
721
+ if (match) {
722
+ const req = request;
723
+ if (!req.context) {
724
+ req.context = {};
725
+ }
726
+ req.params = match.groups || {};
727
+ for (const handler of handlers) {
728
+ const response = await handler(req);
729
+ if (response)
730
+ return response;
731
+ }
732
+ }
733
+ }
734
+ }
735
+ return APIError.notFound("Route not found");
736
+ }
737
+ clearRoutes() {
738
+ this.routes = [];
739
+ }
740
+ }
741
+ var init_router = __esm(() => {
742
+ init_http();
743
+ });
744
+
745
+ // src/core/server.ts
746
+ class VectorServer {
747
+ server = null;
748
+ router;
749
+ config;
750
+ corsHandler;
751
+ constructor(router, config) {
752
+ this.router = router;
753
+ this.config = config;
754
+ if (config.cors) {
755
+ const { preflight: preflight2, corsify: corsify2 } = y(this.normalizeCorsOptions(config.cors));
756
+ this.corsHandler = { preflight: preflight2, corsify: corsify2 };
757
+ }
758
+ }
759
+ normalizeCorsOptions(options) {
760
+ return {
761
+ origin: options.origin || "*",
762
+ credentials: options.credentials !== false,
763
+ allowHeaders: Array.isArray(options.allowHeaders) ? options.allowHeaders.join(", ") : options.allowHeaders || "Content-Type, Authorization",
764
+ allowMethods: Array.isArray(options.allowMethods) ? options.allowMethods.join(", ") : options.allowMethods || "GET, POST, PUT, PATCH, DELETE, OPTIONS",
765
+ exposeHeaders: Array.isArray(options.exposeHeaders) ? options.exposeHeaders.join(", ") : options.exposeHeaders || "Authorization",
766
+ maxAge: options.maxAge || 86400
767
+ };
768
+ }
769
+ async start() {
770
+ const port = this.config.port || 3000;
771
+ const hostname = this.config.hostname || "localhost";
772
+ const fetch = async (request) => {
773
+ try {
774
+ if (this.corsHandler && request.method === "OPTIONS") {
775
+ return this.corsHandler.preflight(request);
776
+ }
777
+ let response = await this.router.handle(request);
778
+ if (this.corsHandler) {
779
+ response = this.corsHandler.corsify(response, request);
780
+ }
781
+ return response;
782
+ } catch (error) {
783
+ console.error("Server error:", error);
784
+ return new Response("Internal Server Error", { status: 500 });
785
+ }
786
+ };
787
+ this.server = Bun.serve({
788
+ port,
789
+ hostname,
790
+ reusePort: this.config.reusePort !== false,
791
+ fetch,
792
+ error: (error) => {
793
+ console.error("[ERROR] Server error:", error);
794
+ return new Response("Internal Server Error", { status: 500 });
795
+ }
796
+ });
797
+ console.log(`→ Vector server running at http://${hostname}:${port}`);
798
+ return this.server;
799
+ }
800
+ stop() {
801
+ if (this.server) {
802
+ this.server.stop();
803
+ this.server = null;
804
+ console.log("Server stopped");
805
+ }
806
+ }
807
+ getServer() {
808
+ return this.server;
809
+ }
810
+ getPort() {
811
+ return this.server?.port || this.config.port || 3000;
812
+ }
813
+ getHostname() {
814
+ return this.server?.hostname || this.config.hostname || "localhost";
815
+ }
816
+ getUrl() {
817
+ const port = this.getPort();
818
+ const hostname = this.getHostname();
819
+ return `http://${hostname}:${port}`;
820
+ }
821
+ }
822
+ var init_server = __esm(() => {
823
+ init_itty_router();
824
+ });
825
+
826
+ // src/dev/route-scanner.ts
827
+ var exports_route_scanner = {};
828
+ __export(exports_route_scanner, {
829
+ RouteScanner: () => RouteScanner2
830
+ });
831
+ import { existsSync as existsSync3, promises as fs3 } from "node:fs";
832
+ import { join as join2, relative as relative3, resolve as resolve3, sep as sep2 } from "node:path";
833
+
834
+ class RouteScanner2 {
835
+ routesDir;
836
+ constructor(routesDir = "./routes") {
837
+ this.routesDir = resolve3(process.cwd(), routesDir);
838
+ }
839
+ async scan() {
840
+ const routes = [];
841
+ if (!existsSync3(this.routesDir)) {
842
+ console.log(` → Routes directory not found: ${this.routesDir}`);
843
+ console.log(" → No routes will be auto-discovered");
844
+ return [];
845
+ }
846
+ try {
847
+ console.log(` → Scanning routes from: ${this.routesDir}`);
848
+ await this.scanDirectory(this.routesDir, routes);
849
+ if (routes.length > 0) {
850
+ console.log(` ✓ Found ${routes.length} route${routes.length === 1 ? "" : "s"}`);
851
+ }
852
+ } catch (error) {
853
+ if (error.code === "ENOENT") {
854
+ console.warn(` ✗ Routes directory not accessible: ${this.routesDir}`);
855
+ return [];
856
+ }
857
+ throw error;
858
+ }
859
+ return routes;
860
+ }
861
+ async scanDirectory(dir, routes, basePath = "") {
862
+ const entries = await fs3.readdir(dir);
863
+ for (const entry of entries) {
864
+ const fullPath = join2(dir, entry);
865
+ const stats = await fs3.stat(fullPath);
866
+ if (stats.isDirectory()) {
867
+ const newBasePath = basePath ? `${basePath}/${entry}` : entry;
868
+ await this.scanDirectory(fullPath, routes, newBasePath);
869
+ } else if (entry.endsWith(".ts") || entry.endsWith(".js")) {
870
+ const routePath = relative3(this.routesDir, fullPath).replace(/\.(ts|js)$/, "").split(sep2).join("/");
871
+ try {
872
+ const importPath = process.platform === "win32" ? `file:///${fullPath.replace(/\\/g, "/")}` : fullPath;
873
+ const module = await import(importPath);
874
+ if (module.default && typeof module.default === "function") {
875
+ routes.push({
876
+ name: "default",
877
+ path: fullPath,
878
+ method: "GET",
879
+ options: {
880
+ method: "GET",
881
+ path: `/${routePath}`,
882
+ expose: true
883
+ }
884
+ });
885
+ }
886
+ for (const [name, value] of Object.entries(module)) {
887
+ if (name === "default")
888
+ continue;
889
+ if (value && typeof value === "object" && "entry" in value && "options" in value && "handler" in value) {
890
+ const routeDef = value;
891
+ routes.push({
892
+ name,
893
+ path: fullPath,
894
+ method: routeDef.options.method,
895
+ options: routeDef.options
896
+ });
897
+ } else if (Array.isArray(value) && value.length >= 4) {
898
+ const [method, , , path] = value;
899
+ routes.push({
900
+ name,
901
+ path: fullPath,
902
+ method,
903
+ options: {
904
+ method,
905
+ path,
906
+ expose: true
907
+ }
908
+ });
909
+ }
910
+ }
911
+ } catch (error) {
912
+ console.error(`Failed to load route from ${fullPath}:`, error);
913
+ }
914
+ }
915
+ }
916
+ }
917
+ enableWatch(callback) {
918
+ if (typeof Bun !== "undefined" && Bun.env.NODE_ENV === "development") {
919
+ console.log(`Watching for route changes in ${this.routesDir}`);
920
+ setInterval(async () => {
921
+ await callback();
922
+ }, 1000);
923
+ }
924
+ }
925
+ }
926
+ var init_route_scanner2 = () => {};
927
+
928
+ // src/dev/route-generator.ts
929
+ var exports_route_generator = {};
930
+ __export(exports_route_generator, {
931
+ RouteGenerator: () => RouteGenerator2
932
+ });
933
+ import { promises as fs4 } from "node:fs";
934
+ import { dirname as dirname2, relative as relative4 } from "node:path";
935
+
936
+ class RouteGenerator2 {
937
+ outputPath;
938
+ constructor(outputPath = "./.vector/routes.generated.ts") {
939
+ this.outputPath = outputPath;
940
+ }
941
+ async generate(routes) {
942
+ const outputDir = dirname2(this.outputPath);
943
+ await fs4.mkdir(outputDir, { recursive: true });
944
+ const imports = [];
945
+ const groupedByFile = new Map;
946
+ for (const route of routes) {
947
+ if (!groupedByFile.has(route.path)) {
948
+ groupedByFile.set(route.path, []);
949
+ }
950
+ groupedByFile.get(route.path).push(route);
951
+ }
952
+ let importIndex = 0;
953
+ const routeEntries = [];
954
+ for (const [filePath, fileRoutes] of groupedByFile) {
955
+ const relativePath = relative4(dirname2(this.outputPath), filePath).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
956
+ const importName = `route_${importIndex++}`;
957
+ const namedImports = fileRoutes.filter((r2) => r2.name !== "default").map((r2) => r2.name);
958
+ if (fileRoutes.some((r2) => r2.name === "default")) {
959
+ if (namedImports.length > 0) {
960
+ imports.push(`import ${importName}, { ${namedImports.join(", ")} } from '${relativePath}';`);
961
+ } else {
962
+ imports.push(`import ${importName} from '${relativePath}';`);
963
+ }
964
+ } else if (namedImports.length > 0) {
965
+ imports.push(`import { ${namedImports.join(", ")} } from '${relativePath}';`);
966
+ }
967
+ for (const route of fileRoutes) {
968
+ const routeVar = route.name === "default" ? importName : route.name;
969
+ routeEntries.push(` ${routeVar},`);
970
+ }
971
+ }
972
+ const content = `// This file is auto-generated. Do not edit manually.
973
+ // Generated at: ${new Date().toISOString()}
974
+
975
+ ${imports.join(`
976
+ `)}
977
+
978
+ export const routes = [
979
+ ${routeEntries.join(`
980
+ `)}
981
+ ];
982
+
983
+ export default routes;
984
+ `;
985
+ await fs4.writeFile(this.outputPath, content, "utf-8");
986
+ console.log(`Generated routes file: ${this.outputPath}`);
987
+ }
988
+ async generateDynamic(routes) {
989
+ const routeEntries = [];
990
+ for (const route of routes) {
991
+ const routeObj = JSON.stringify({
992
+ method: route.method,
993
+ path: route.options.path,
994
+ options: route.options
995
+ });
996
+ routeEntries.push(` await import('${route.path}').then(m => ({
997
+ ...${routeObj},
998
+ handler: m.${route.name === "default" ? "default" : route.name}
999
+ }))`);
1000
+ }
1001
+ return `export const loadRoutes = async () => {
1002
+ return Promise.all([
1003
+ ${routeEntries.join(`,
1004
+ `)}
1005
+ ]);
1006
+ };`;
1007
+ }
1008
+ }
1009
+ var init_route_generator2 = () => {};
1010
+
1011
+ // src/cli/index.ts
1012
+ import { watch } from "fs";
1013
+ import { parseArgs } from "util";
1014
+
1015
+ // src/core/vector.ts
1016
+ init_manager();
1017
+ init_route_generator();
1018
+ init_route_scanner();
1019
+ init_router();
1020
+ init_server();
1021
+
1022
+ class Vector {
1023
+ static instance;
1024
+ router;
1025
+ server = null;
1026
+ middlewareManager;
1027
+ authManager;
1028
+ cacheManager;
1029
+ config = {};
1030
+ routeScanner = null;
1031
+ routeGenerator = null;
1032
+ _protectedHandler = null;
1033
+ _cacheHandler = null;
1034
+ constructor() {
1035
+ this.middlewareManager = new MiddlewareManager;
1036
+ this.authManager = new AuthManager;
1037
+ this.cacheManager = new CacheManager;
1038
+ this.router = new VectorRouter(this.middlewareManager, this.authManager, this.cacheManager);
1039
+ }
1040
+ static getInstance() {
1041
+ if (!Vector.instance) {
1042
+ Vector.instance = new Vector;
1043
+ }
1044
+ return Vector.instance;
1045
+ }
1046
+ setProtectedHandler(handler) {
1047
+ this._protectedHandler = handler;
1048
+ this.authManager.setProtectedHandler(handler);
1049
+ }
1050
+ getProtectedHandler() {
1051
+ return this._protectedHandler;
1052
+ }
1053
+ setCacheHandler(handler) {
1054
+ this._cacheHandler = handler;
1055
+ this.cacheManager.setCacheHandler(handler);
1056
+ }
1057
+ getCacheHandler() {
1058
+ return this._cacheHandler;
1059
+ }
1060
+ addRoute(options, handler) {
1061
+ return this.router.route(options, handler);
1062
+ }
1063
+ async startServer(config) {
1064
+ this.config = { ...this.config, ...config };
1065
+ this.middlewareManager.clear();
1066
+ if (config?.before) {
1067
+ this.middlewareManager.addBefore(...config.before);
1068
+ }
1069
+ if (config?.finally) {
1070
+ this.middlewareManager.addFinally(...config.finally);
1071
+ }
1072
+ if (this.config.autoDiscover !== false) {
1073
+ await this.discoverRoutes();
1074
+ }
1075
+ this.server = new VectorServer(this.router, this.config);
1076
+ const bunServer = await this.server.start();
1077
+ return bunServer;
1078
+ }
1079
+ async discoverRoutes() {
1080
+ const routesDir = this.config.routesDir || "./routes";
1081
+ if (!this.routeScanner) {
1082
+ this.routeScanner = new RouteScanner(routesDir);
1083
+ }
1084
+ if (!this.routeGenerator) {
1085
+ this.routeGenerator = new RouteGenerator;
1086
+ }
1087
+ try {
1088
+ const routes = await this.routeScanner.scan();
1089
+ if (routes.length > 0) {
1090
+ if (this.config.development) {
1091
+ await this.routeGenerator.generate(routes);
1092
+ }
1093
+ for (const route of routes) {
1094
+ try {
1095
+ const importPath = toFileUrl(route.path);
1096
+ const module = await import(importPath);
1097
+ const exported = route.name === "default" ? module.default : module[route.name];
1098
+ if (exported) {
1099
+ if (this.isRouteDefinition(exported)) {
1100
+ const routeDef = exported;
1101
+ this.router.route(routeDef.options, routeDef.handler);
1102
+ this.logRouteLoaded(routeDef.options);
1103
+ } else if (this.isRouteEntry(exported)) {
1104
+ this.router.addRoute(exported);
1105
+ this.logRouteLoaded(exported);
1106
+ } else if (typeof exported === "function") {
1107
+ this.router.route(route.options, exported);
1108
+ this.logRouteLoaded(route.options);
1109
+ }
1110
+ }
1111
+ } catch (error) {
1112
+ console.error(`Failed to load route ${route.name} from ${route.path}:`, error);
1113
+ }
1114
+ }
1115
+ this.router.sortRoutes();
1116
+ console.log(`✅ Loaded ${routes.length} routes from ${routesDir}`);
1117
+ }
1118
+ } catch (error) {
1119
+ if (error.code !== "ENOENT" && error.code !== "ENOTDIR") {
1120
+ console.error("Failed to discover routes:", error);
1121
+ }
1122
+ }
1123
+ }
1124
+ async loadRoute(routeModule) {
1125
+ if (typeof routeModule === "function") {
1126
+ const routeEntry = routeModule();
1127
+ if (Array.isArray(routeEntry)) {
1128
+ this.router.addRoute(routeEntry);
1129
+ }
1130
+ } else if (routeModule && typeof routeModule === "object") {
1131
+ for (const [, value] of Object.entries(routeModule)) {
1132
+ if (typeof value === "function") {
1133
+ const routeEntry = value();
1134
+ if (Array.isArray(routeEntry)) {
1135
+ this.router.addRoute(routeEntry);
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ isRouteEntry(value) {
1142
+ return Array.isArray(value) && value.length >= 3;
1143
+ }
1144
+ isRouteDefinition(value) {
1145
+ return value && typeof value === "object" && "entry" in value && "options" in value && "handler" in value;
1146
+ }
1147
+ logRouteLoaded(route) {
1148
+ if (Array.isArray(route)) {
1149
+ console.log(` ✓ Loaded route: ${route[0]} ${route[3] || route[1]}`);
1150
+ } else {
1151
+ console.log(` ✓ Loaded route: ${route.method} ${route.path}`);
1152
+ }
1153
+ }
1154
+ stop() {
1155
+ if (this.server) {
1156
+ this.server.stop();
1157
+ this.server = null;
1158
+ }
1159
+ this.router.clearRoutes();
1160
+ }
1161
+ getServer() {
1162
+ return this.server;
1163
+ }
1164
+ getRouter() {
1165
+ return this.router;
1166
+ }
1167
+ getCacheManager() {
1168
+ return this.cacheManager;
1169
+ }
1170
+ getAuthManager() {
1171
+ return this.authManager;
1172
+ }
1173
+ static resetInstance() {
1174
+ Vector.instance = null;
1175
+ }
1176
+ }
1177
+ var getVectorInstance = Vector.getInstance;
1178
+
1179
+ // src/core/config-loader.ts
1180
+ import { existsSync as existsSync2 } from "node:fs";
1181
+ import { resolve as resolve2 } from "node:path";
1182
+
1183
+ class ConfigLoader {
1184
+ configPath;
1185
+ config = null;
1186
+ configSource = "default";
1187
+ constructor(configPath = "vector.config.ts") {
1188
+ this.configPath = resolve2(process.cwd(), configPath);
1189
+ }
1190
+ async load() {
1191
+ if (existsSync2(this.configPath)) {
1192
+ try {
1193
+ console.log(`→ Loading config from: ${this.configPath}`);
1194
+ const userConfigPath = toFileUrl(this.configPath);
1195
+ const userConfig = await import(userConfigPath);
1196
+ this.config = userConfig.default || userConfig;
1197
+ this.configSource = "user";
1198
+ console.log(" ✓ User config loaded successfully");
1199
+ } catch (error) {
1200
+ console.error(` ✗ Failed to load config from ${this.configPath}:`, error);
1201
+ console.log(" → Using default configuration");
1202
+ this.config = {};
1203
+ }
1204
+ } else {
1205
+ console.log(` → No config file found at: ${this.configPath}`);
1206
+ console.log(" → Using default configuration");
1207
+ this.config = {};
1208
+ }
1209
+ return await this.buildLegacyConfig();
1210
+ }
1211
+ getConfigSource() {
1212
+ return this.configSource;
1213
+ }
1214
+ async buildLegacyConfig() {
1215
+ const config = {};
1216
+ if (this.config?.server) {
1217
+ config.port = this.config.server.port;
1218
+ config.hostname = this.config.server.hostname;
1219
+ config.reusePort = this.config.server.reusePort;
1220
+ config.development = this.config.server.development;
1221
+ }
1222
+ if (this.config?.routes) {
1223
+ config.routesDir = this.config.routes.dir || "./routes";
1224
+ config.autoDiscover = this.config.routes.autoDiscover !== false;
1225
+ } else {
1226
+ config.routesDir = "./routes";
1227
+ config.autoDiscover = true;
1228
+ }
1229
+ if (this.config?.cors) {
1230
+ if (typeof this.config.cors === "boolean") {
1231
+ config.cors = this.config.cors ? {
1232
+ origin: "*",
1233
+ credentials: true,
1234
+ allowHeaders: "Content-Type, Authorization",
1235
+ allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
1236
+ exposeHeaders: "Authorization",
1237
+ maxAge: 86400
1238
+ } : undefined;
1239
+ } else {
1240
+ config.cors = this.config.cors;
1241
+ }
1242
+ }
1243
+ if (this.config?.before) {
1244
+ console.log("Using direct before middleware functions:", this.config.before.length);
1245
+ config.before = this.config.before;
1246
+ } else if (this.config?.middleware?.before) {
1247
+ console.log("Loading before middleware from file paths:", this.config.middleware.before);
1248
+ config.before = await this.loadMiddleware(this.config.middleware.before);
1249
+ console.log("Loaded before middleware:", config.before?.length);
1250
+ }
1251
+ if (this.config?.after) {
1252
+ console.log("Using direct after middleware functions:", this.config.after.length);
1253
+ config.finally = this.config.after;
1254
+ } else if (this.config?.middleware?.after) {
1255
+ console.log("Loading after middleware from file paths:", this.config.middleware.after);
1256
+ config.finally = await this.loadMiddleware(this.config.middleware.after);
1257
+ console.log("Loaded after middleware:", config.finally?.length);
1258
+ }
1259
+ return config;
1260
+ }
1261
+ async loadMiddleware(paths) {
1262
+ const middleware = [];
1263
+ for (const path of paths) {
1264
+ try {
1265
+ const modulePath = resolve2(process.cwd(), path);
1266
+ const importPath = toFileUrl(modulePath);
1267
+ const module = await import(importPath);
1268
+ const handler = module.default || module;
1269
+ if (typeof handler === "function") {
1270
+ middleware.push(handler);
1271
+ } else {
1272
+ console.warn(`Middleware at ${path} does not export a function`);
1273
+ }
1274
+ } catch (error) {
1275
+ console.error(`Failed to load middleware from ${path}:`, error);
1276
+ }
1277
+ }
1278
+ return middleware;
1279
+ }
1280
+ async loadAuthHandler() {
1281
+ if (this.config?.auth) {
1282
+ console.log("Using direct auth handler function");
1283
+ return this.config.auth;
1284
+ }
1285
+ if (!this.config?.handlers?.auth) {
1286
+ return null;
1287
+ }
1288
+ try {
1289
+ const modulePath = resolve2(process.cwd(), this.config.handlers.auth);
1290
+ const importPath = toFileUrl(modulePath);
1291
+ const module = await import(importPath);
1292
+ const handler = module.default || module;
1293
+ if (typeof handler === "function") {
1294
+ return handler;
1295
+ } else {
1296
+ console.warn(`Auth handler at ${this.config.handlers.auth} does not export a function`);
1297
+ return null;
1298
+ }
1299
+ } catch (error) {
1300
+ console.error(`Failed to load auth handler from ${this.config.handlers.auth}:`, error);
1301
+ return null;
1302
+ }
1303
+ }
1304
+ async loadCacheHandler() {
1305
+ if (this.config?.cache) {
1306
+ console.log("Using direct cache handler function");
1307
+ return this.config.cache;
1308
+ }
1309
+ if (!this.config?.handlers?.cache) {
1310
+ return null;
1311
+ }
1312
+ try {
1313
+ const modulePath = resolve2(process.cwd(), this.config.handlers.cache);
1314
+ const importPath = toFileUrl(modulePath);
1315
+ const module = await import(importPath);
1316
+ const handler = module.default || module;
1317
+ if (typeof handler === "function") {
1318
+ return handler;
1319
+ } else {
1320
+ console.warn(`Cache handler at ${this.config.handlers.cache} does not export a function`);
1321
+ return null;
1322
+ }
1323
+ } catch (error) {
1324
+ console.error(`Failed to load cache handler from ${this.config.handlers.cache}:`, error);
1325
+ return null;
1326
+ }
1327
+ }
1328
+ getConfig() {
1329
+ return this.config;
1330
+ }
1331
+ }
1332
+
1333
+ // src/cli/index.ts
1334
+ var { values, positionals } = parseArgs({
1335
+ args: Bun.argv.slice(2),
1336
+ options: {
1337
+ port: {
1338
+ type: "string",
1339
+ short: "p",
1340
+ default: "3000"
1341
+ },
1342
+ host: {
1343
+ type: "string",
1344
+ short: "h",
1345
+ default: "localhost"
1346
+ },
1347
+ routes: {
1348
+ type: "string",
1349
+ short: "r",
1350
+ default: "./routes"
1351
+ },
1352
+ watch: {
1353
+ type: "boolean",
1354
+ short: "w",
1355
+ default: true
1356
+ },
1357
+ cors: {
1358
+ type: "boolean",
1359
+ default: true
1360
+ }
1361
+ },
1362
+ strict: true,
1363
+ allowPositionals: true
1364
+ });
1365
+ var command = positionals[0] || "dev";
1366
+ async function runDev() {
1367
+ const isDev = command === "dev";
1368
+ console.log(`
1369
+ \u2192 Starting Vector ${isDev ? "development" : "production"} server
1370
+ `);
1371
+ let server = null;
1372
+ let vector = null;
1373
+ async function startServer() {
1374
+ try {
1375
+ const configLoader = new ConfigLoader;
1376
+ const config = await configLoader.load();
1377
+ const configSource = configLoader.getConfigSource();
1378
+ config.port = config.port || Number.parseInt(values.port);
1379
+ config.hostname = config.hostname || values.host;
1380
+ config.routesDir = config.routesDir || values.routes;
1381
+ config.development = isDev;
1382
+ config.autoDiscover = true;
1383
+ if (!config.cors && values.cors) {
1384
+ config.cors = {
1385
+ origin: "*",
1386
+ credentials: true,
1387
+ allowHeaders: "Content-Type, Authorization",
1388
+ allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
1389
+ exposeHeaders: "Authorization",
1390
+ maxAge: 86400
1391
+ };
1392
+ }
1393
+ vector = getVectorInstance();
1394
+ const authHandler = await configLoader.loadAuthHandler();
1395
+ if (authHandler) {
1396
+ vector.setProtectedHandler(authHandler);
1397
+ }
1398
+ const cacheHandler = await configLoader.loadCacheHandler();
1399
+ if (cacheHandler) {
1400
+ vector.setCacheHandler(cacheHandler);
1401
+ }
1402
+ server = await vector.startServer(config);
1403
+ const gray = "\x1B[90m";
1404
+ const reset = "\x1B[0m";
1405
+ const cyan = "\x1B[36m";
1406
+ const green = "\x1B[32m";
1407
+ console.log(` ${gray}Config${reset} ${configSource === "user" ? "User config loaded" : "Using defaults"}`);
1408
+ console.log(` ${gray}Routes${reset} ${config.routesDir}`);
1409
+ if (isDev && values.watch) {
1410
+ console.log(` ${gray}Watching${reset} All project files`);
1411
+ }
1412
+ console.log(` ${gray}CORS${reset} ${values.cors ? "Enabled" : "Disabled"}`);
1413
+ console.log(` ${gray}Mode${reset} ${isDev ? "Development" : "Production"}
1414
+ `);
1415
+ console.log(` ${green}Ready${reset} \u2192 ${cyan}http://${config.hostname}:${config.port}${reset}
1416
+ `);
1417
+ return { server, vector, config };
1418
+ } catch (error) {
1419
+ console.error("[ERROR] Failed to start server:", error);
1420
+ throw error;
1421
+ }
1422
+ }
1423
+ try {
1424
+ const result = await startServer();
1425
+ server = result.server;
1426
+ if (isDev && values.watch) {
1427
+ try {
1428
+ let reloadTimeout = null;
1429
+ watch(process.cwd(), { recursive: true }, async (_, filename) => {
1430
+ if (filename && (filename.endsWith(".ts") || filename.endsWith(".js") || filename.endsWith(".json")) && !filename.includes("node_modules") && !filename.includes(".git")) {
1431
+ if (reloadTimeout) {
1432
+ clearTimeout(reloadTimeout);
1433
+ }
1434
+ reloadTimeout = setTimeout(async () => {
1435
+ console.log(`
1436
+ \uD83D\uDD04 File changed: ${filename}`);
1437
+ console.log(` \uD83D\uDD04 Reloading server...
1438
+ `);
1439
+ if (vector) {
1440
+ vector.stop();
1441
+ }
1442
+ for (const key in __require.cache) {
1443
+ if (!key.includes("node_modules")) {
1444
+ delete __require.cache[key];
1445
+ }
1446
+ }
1447
+ try {
1448
+ const result2 = await startServer();
1449
+ server = result2.server;
1450
+ } catch (error) {
1451
+ console.error(" \u274C Failed to reload server:", error);
1452
+ }
1453
+ }, 100);
1454
+ }
1455
+ });
1456
+ } catch (err) {
1457
+ console.warn(" \u26A0\uFE0F File watching not available");
1458
+ }
1459
+ }
1460
+ } catch (error) {
1461
+ console.error("[ERROR] Failed to start server:", error);
1462
+ process.exit(1);
1463
+ }
1464
+ }
1465
+ async function runBuild() {
1466
+ console.log(`
1467
+ \u2192 Building Vector application
1468
+ `);
1469
+ try {
1470
+ const { RouteScanner: RouteScanner3 } = await Promise.resolve().then(() => (init_route_scanner2(), exports_route_scanner));
1471
+ const { RouteGenerator: RouteGenerator3 } = await Promise.resolve().then(() => (init_route_generator2(), exports_route_generator));
1472
+ const scanner = new RouteScanner3(values.routes);
1473
+ const generator = new RouteGenerator3;
1474
+ const routes = await scanner.scan();
1475
+ await generator.generate(routes);
1476
+ console.log(` Generated ${routes.length} routes`);
1477
+ const buildProcess = Bun.spawn([
1478
+ "bun",
1479
+ "build",
1480
+ "src/index.ts",
1481
+ "--outdir",
1482
+ "dist",
1483
+ "--minify"
1484
+ ]);
1485
+ await buildProcess.exited;
1486
+ console.log(`
1487
+ \u2713 Build complete
1488
+ `);
1489
+ } catch (error) {
1490
+ console.error("[ERROR] Build failed:", error);
1491
+ process.exit(1);
1492
+ }
1493
+ }
1494
+ switch (command) {
1495
+ case "dev":
1496
+ await runDev();
1497
+ break;
1498
+ case "build":
1499
+ await runBuild();
1500
+ break;
1501
+ case "start":
1502
+ process.env.NODE_ENV = "production";
1503
+ await runDev();
1504
+ break;
1505
+ default:
1506
+ console.error(`Unknown command: ${command}`);
1507
+ console.log(`
1508
+ Usage: vector [command] [options]
1509
+
1510
+ Commands:
1511
+ dev Start development server (default)
1512
+ build Build for production
1513
+ start Start production server
1514
+
1515
+ Options:
1516
+ -p, --port <port> Port to listen on (default: 3000)
1517
+ -h, --host <host> Hostname to bind to (default: localhost)
1518
+ -r, --routes <dir> Routes directory (default: ./routes)
1519
+ -w, --watch Watch for file changes (default: true)
1520
+ --cors Enable CORS (default: true)
1521
+ `);
1522
+ process.exit(1);
1523
+ }