vector-framework 0.9.0 → 0.9.2

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