vector-framework 0.9.4 → 0.9.5

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.
@@ -1,9 +1,7 @@
1
- import { existsSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { toFileUrl } from '../utils/path';
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { toFileUrl } from "../utils/path";
4
4
  import type {
5
- AfterMiddlewareHandler,
6
- BeforeMiddlewareHandler,
7
5
  CacheHandler,
8
6
  CorsOptions,
9
7
  DefaultVectorTypes,
@@ -11,14 +9,14 @@ import type {
11
9
  VectorConfig,
12
10
  VectorConfigSchema,
13
11
  VectorTypes,
14
- } from '../types';
12
+ } from "../types";
15
13
 
16
14
  export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
17
15
  private configPath: string;
18
16
  private config: VectorConfigSchema<TTypes> | null = null;
19
- private configSource: 'user' | 'default' = 'default';
17
+ private configSource: "user" | "default" = "default";
20
18
 
21
- constructor(configPath = 'vector.config.ts') {
19
+ constructor(configPath = "vector.config.ts") {
22
20
  // Always resolve from the current working directory (user's project)
23
21
  this.configPath = resolve(process.cwd(), configPath);
24
22
  }
@@ -28,69 +26,62 @@ export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
28
26
  if (existsSync(this.configPath)) {
29
27
  try {
30
28
  console.log(`→ Loading config from: ${this.configPath}`);
31
-
29
+
32
30
  // Use explicit file:// URL to ensure correct resolution
33
31
  const userConfigPath = toFileUrl(this.configPath);
34
32
  const userConfig = await import(userConfigPath);
35
33
  this.config = userConfig.default || userConfig;
36
- this.configSource = 'user';
37
-
38
- console.log(' ✓ User config loaded successfully');
34
+ this.configSource = "user";
35
+
36
+ console.log(" ✓ User config loaded successfully");
39
37
  } catch (error) {
40
- console.error(` ✗ Failed to load config from ${this.configPath}:`, error);
41
- console.log(' Using default configuration');
38
+ console.error(
39
+ ` Failed to load config from ${this.configPath}:`,
40
+ error
41
+ );
42
+ console.log(" → Using default configuration");
42
43
  this.config = {};
43
44
  }
44
45
  } else {
45
46
  // Config file doesn't exist, use defaults
46
47
  console.log(` → No config file found at: ${this.configPath}`);
47
- console.log(' → Using default configuration');
48
+ console.log(" → Using default configuration");
48
49
  this.config = {};
49
50
  }
50
51
 
51
52
  // Convert new config schema to legacy VectorConfig format
52
53
  return await this.buildLegacyConfig();
53
54
  }
54
-
55
- getConfigSource(): 'user' | 'default' {
55
+
56
+ getConfigSource(): "user" | "default" {
56
57
  return this.configSource;
57
58
  }
58
59
 
59
60
  private async buildLegacyConfig(): Promise<VectorConfig<TTypes>> {
60
61
  const config: VectorConfig<TTypes> = {};
61
62
 
62
- // Server configuration
63
- if (this.config?.server) {
64
- config.port = this.config.server.port;
65
- config.hostname = this.config.server.hostname;
66
- config.reusePort = this.config.server.reusePort;
67
- config.development = this.config.server.development;
63
+ // Direct mapping - schemas are now the same (flat)
64
+ if (this.config) {
65
+ config.port = this.config.port;
66
+ config.hostname = this.config.hostname;
67
+ config.reusePort = this.config.reusePort;
68
+ config.development = this.config.development;
69
+ config.routesDir = this.config.routesDir || "./routes";
68
70
  }
69
71
 
70
- // Routes configuration - support both new and legacy formats
71
- if (this.config?.routes) {
72
- // New format: { routes: { dir: string } }
73
- config.routesDir = this.config.routes.dir || './routes';
74
- config.autoDiscover = this.config.routes.autoDiscover !== false;
75
- } else if ((this.config as any)?.routesDir) {
76
- // Legacy format: { routesDir: string }
77
- config.routesDir = (this.config as any).routesDir;
78
- config.autoDiscover = (this.config as any).autoDiscover !== false;
79
- } else {
80
- config.routesDir = './routes';
81
- config.autoDiscover = true;
82
- }
72
+ // Always auto-discover routes
73
+ config.autoDiscover = true;
83
74
 
84
75
  // CORS configuration
85
76
  if (this.config?.cors) {
86
- if (typeof this.config.cors === 'boolean') {
77
+ if (typeof this.config.cors === "boolean") {
87
78
  config.cors = this.config.cors
88
79
  ? {
89
- origin: '*',
80
+ origin: "*",
90
81
  credentials: true,
91
- allowHeaders: 'Content-Type, Authorization',
92
- allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
93
- exposeHeaders: 'Authorization',
82
+ allowHeaders: "Content-Type, Authorization",
83
+ allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
84
+ exposeHeaders: "Authorization",
94
85
  maxAge: 86400,
95
86
  }
96
87
  : undefined;
@@ -99,112 +90,27 @@ export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
99
90
  }
100
91
  }
101
92
 
102
- // Load middleware - support both direct functions and file paths
93
+ // Middleware mapping (VectorConfig uses 'finally' instead of 'after')
103
94
  if (this.config?.before) {
104
- // Direct functions provided
105
95
  config.before = this.config.before;
106
- } else if (this.config?.middleware?.before) {
107
- // File paths provided (legacy)
108
- config.before = await this.loadMiddleware<BeforeMiddlewareHandler<TTypes>>(
109
- this.config.middleware.before
110
- );
111
96
  }
112
97
 
113
98
  if (this.config?.after) {
114
- // Direct functions provided
115
99
  config.finally = this.config.after;
116
- } else if (this.config?.middleware?.after) {
117
- // File paths provided (legacy)
118
- config.finally = await this.loadMiddleware<AfterMiddlewareHandler<TTypes>>(
119
- this.config.middleware.after
120
- );
121
100
  }
122
101
 
123
102
  return config;
124
103
  }
125
104
 
126
- private async loadMiddleware<T>(paths: string[]): Promise<T[]> {
127
- const middleware: T[] = [];
128
-
129
- for (const path of paths) {
130
- try {
131
- const modulePath = resolve(process.cwd(), path);
132
- const importPath = toFileUrl(modulePath);
133
- const module = await import(importPath);
134
- const handler = module.default || module;
135
-
136
- if (typeof handler === 'function') {
137
- middleware.push(handler as T);
138
- } else {
139
- console.warn(`Middleware at ${path} does not export a function`);
140
- }
141
- } catch (error) {
142
- console.error(`Failed to load middleware from ${path}:`, error);
143
- }
144
- }
145
-
146
- return middleware;
147
- }
148
-
149
105
  async loadAuthHandler(): Promise<ProtectedHandler<TTypes> | null> {
150
- // Direct function provided
151
- if (this.config?.auth) {
152
- return this.config.auth;
153
- }
154
-
155
- // File path provided (legacy)
156
- if (!this.config?.handlers?.auth) {
157
- return null;
158
- }
159
-
160
- try {
161
- const modulePath = resolve(process.cwd(), this.config.handlers.auth);
162
- const importPath = toFileUrl(modulePath);
163
- const module = await import(importPath);
164
- const handler = module.default || module;
165
-
166
- if (typeof handler === 'function') {
167
- return handler as ProtectedHandler<TTypes>;
168
- } else {
169
- console.warn(`Auth handler at ${this.config.handlers.auth} does not export a function`);
170
- return null;
171
- }
172
- } catch (error) {
173
- console.error(`Failed to load auth handler from ${this.config.handlers.auth}:`, error);
174
- return null;
175
- }
106
+ return this.config?.auth || null;
176
107
  }
177
108
 
178
109
  async loadCacheHandler(): Promise<CacheHandler | null> {
179
- // Direct function provided
180
- if (this.config?.cache) {
181
- return this.config.cache;
182
- }
183
-
184
- // File path provided (legacy)
185
- if (!this.config?.handlers?.cache) {
186
- return null;
187
- }
188
-
189
- try {
190
- const modulePath = resolve(process.cwd(), this.config.handlers.cache);
191
- const importPath = toFileUrl(modulePath);
192
- const module = await import(importPath);
193
- const handler = module.default || module;
194
-
195
- if (typeof handler === 'function') {
196
- return handler as CacheHandler;
197
- } else {
198
- console.warn(`Cache handler at ${this.config.handlers.cache} does not export a function`);
199
- return null;
200
- }
201
- } catch (error) {
202
- console.error(`Failed to load cache handler from ${this.config.handlers.cache}:`, error);
203
- return null;
204
- }
110
+ return this.config?.cache || null;
205
111
  }
206
112
 
207
113
  getConfig(): VectorConfigSchema<TTypes> | null {
208
114
  return this.config;
209
115
  }
210
- }
116
+ }
@@ -1,11 +1,11 @@
1
- import type { Server } from 'bun';
2
- import type { RouteEntry } from 'itty-router';
3
- import { AuthManager } from '../auth/protected';
4
- import { CacheManager } from '../cache/manager';
5
- import { RouteGenerator } from '../dev/route-generator';
6
- import { RouteScanner } from '../dev/route-scanner';
7
- import { MiddlewareManager } from '../middleware/manager';
8
- import { toFileUrl } from '../utils/path';
1
+ import type { Server } from "bun";
2
+ import type { RouteEntry } from "itty-router";
3
+ import { AuthManager } from "../auth/protected";
4
+ import { CacheManager } from "../cache/manager";
5
+ import { RouteGenerator } from "../dev/route-generator";
6
+ import { RouteScanner } from "../dev/route-scanner";
7
+ import { MiddlewareManager } from "../middleware/manager";
8
+ import { toFileUrl } from "../utils/path";
9
9
  import type {
10
10
  CacheHandler,
11
11
  DefaultVectorTypes,
@@ -14,9 +14,9 @@ import type {
14
14
  RouteOptions,
15
15
  VectorConfig,
16
16
  VectorTypes,
17
- } from '../types';
18
- import { VectorRouter } from './router';
19
- import { VectorServer } from './server';
17
+ } from "../types";
18
+ import { VectorRouter } from "./router";
19
+ import { VectorServer } from "./server";
20
20
 
21
21
  // Internal-only class - not exposed to users
22
22
  export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
@@ -72,7 +72,10 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
72
72
  }
73
73
 
74
74
  // Internal method to add route
75
- addRoute(options: RouteOptions<TTypes>, handler: RouteHandler<TTypes>): RouteEntry {
75
+ addRoute(
76
+ options: RouteOptions<TTypes>,
77
+ handler: RouteHandler<TTypes>
78
+ ): RouteEntry {
76
79
  return this.router.route(options, handler);
77
80
  }
78
81
 
@@ -102,7 +105,7 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
102
105
  }
103
106
 
104
107
  private async discoverRoutes() {
105
- const routesDir = this.config.routesDir || './routes';
108
+ const routesDir = this.config.routesDir || "./routes";
106
109
 
107
110
  // Always create a new RouteScanner with the current config's routesDir
108
111
  // to ensure we're using the correct path from the user's config
@@ -125,7 +128,8 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
125
128
  const importPath = toFileUrl(route.path);
126
129
 
127
130
  const module = await import(importPath);
128
- const exported = route.name === 'default' ? module.default : module[route.name];
131
+ const exported =
132
+ route.name === "default" ? module.default : module[route.name];
129
133
 
130
134
  if (exported) {
131
135
  if (this.isRouteDefinition(exported)) {
@@ -137,36 +141,41 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
137
141
  // Legacy support for direct RouteEntry (won't have middleware)
138
142
  this.router.addRoute(exported as RouteEntry);
139
143
  this.logRouteLoaded(exported as RouteEntry);
140
- } else if (typeof exported === 'function') {
144
+ } else if (typeof exported === "function") {
141
145
  this.router.route(route.options as any, exported);
142
146
  this.logRouteLoaded(route.options);
143
147
  }
144
148
  }
145
149
  } catch (error) {
146
- console.error(`Failed to load route ${route.name} from ${route.path}:`, error);
150
+ console.error(
151
+ `Failed to load route ${route.name} from ${route.path}:`,
152
+ error
153
+ );
147
154
  }
148
155
  }
149
156
 
150
157
  // Ensure routes are properly sorted after loading all
151
158
  this.router.sortRoutes();
152
- console.log(`✅ Loaded ${routes.length} routes from ${routesDir}`);
153
159
  }
154
160
  } catch (error) {
155
- if ((error as any).code !== 'ENOENT' && (error as any).code !== 'ENOTDIR') {
156
- console.error('Failed to discover routes:', error);
161
+ if (
162
+ (error as any).code !== "ENOENT" &&
163
+ (error as any).code !== "ENOTDIR"
164
+ ) {
165
+ console.error("Failed to discover routes:", error);
157
166
  }
158
167
  }
159
168
  }
160
169
 
161
170
  async loadRoute(routeModule: any) {
162
- if (typeof routeModule === 'function') {
171
+ if (typeof routeModule === "function") {
163
172
  const routeEntry = routeModule();
164
173
  if (Array.isArray(routeEntry)) {
165
174
  this.router.addRoute(routeEntry as RouteEntry);
166
175
  }
167
- } else if (routeModule && typeof routeModule === 'object') {
176
+ } else if (routeModule && typeof routeModule === "object") {
168
177
  for (const [, value] of Object.entries(routeModule)) {
169
- if (typeof value === 'function') {
178
+ if (typeof value === "function") {
170
179
  const routeEntry = (value as any)();
171
180
  if (Array.isArray(routeEntry)) {
172
181
  this.router.addRoute(routeEntry as RouteEntry);
@@ -181,15 +190,17 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
181
190
  }
182
191
 
183
192
  private isRouteDefinition(value: any): boolean {
184
- return value && typeof value === 'object' && 'entry' in value && 'options' in value && 'handler' in value;
193
+ return (
194
+ value &&
195
+ typeof value === "object" &&
196
+ "entry" in value &&
197
+ "options" in value &&
198
+ "handler" in value
199
+ );
185
200
  }
186
201
 
187
- private logRouteLoaded(route: RouteEntry | RouteOptions): void {
188
- if (Array.isArray(route)) {
189
- console.log(` ✓ Loaded route: ${route[0]} ${route[3] || route[1]}`);
190
- } else {
191
- console.log(` ✓ Loaded route: ${route.method} ${route.path}`);
192
- }
202
+ private logRouteLoaded(_: RouteEntry | RouteOptions): void {
203
+ // Silent - no logging
193
204
  }
194
205
 
195
206
  stop(): void {
@@ -65,7 +65,6 @@ export default routes;
65
65
  `;
66
66
 
67
67
  await fs.writeFile(this.outputPath, content, 'utf-8');
68
- console.log(`Generated routes file: ${this.outputPath}`);
69
68
  }
70
69
 
71
70
  async generateDynamic(routes: GeneratedRoute[]): Promise<string> {
@@ -15,17 +15,11 @@ export class RouteScanner {
15
15
 
16
16
  // Check if routes directory exists before attempting to scan
17
17
  if (!existsSync(this.routesDir)) {
18
- console.log(` → Routes directory not found: ${this.routesDir}`);
19
- console.log(' → No routes will be auto-discovered');
20
18
  return [];
21
19
  }
22
20
 
23
21
  try {
24
- console.log(` → Scanning routes from: ${this.routesDir}`);
25
22
  await this.scanDirectory(this.routesDir, routes);
26
- if (routes.length > 0) {
27
- console.log(` ✓ Found ${routes.length} route${routes.length === 1 ? '' : 's'}`);
28
- }
29
23
  } catch (error) {
30
24
  if ((error as any).code === 'ENOENT') {
31
25
  console.warn(` ✗ Routes directory not accessible: ${this.routesDir}`);
@@ -85,39 +85,20 @@ export interface VectorConfig<TTypes extends VectorTypes = DefaultVectorTypes> {
85
85
  autoDiscover?: boolean;
86
86
  }
87
87
 
88
- // New config-driven schema
88
+ // New config-driven schema - flat structure
89
89
  export interface VectorConfigSchema<TTypes extends VectorTypes = DefaultVectorTypes> {
90
90
  // Server configuration
91
- server?: {
92
- port?: number;
93
- hostname?: string;
94
- reusePort?: boolean;
95
- development?: boolean;
96
- };
97
-
98
- // Routes configuration
99
- routes?: {
100
- dir?: string;
101
- autoDiscover?: boolean;
102
- };
103
-
104
- // Middleware configuration - supports both file paths and direct functions
105
- middleware?: {
106
- before?: string[];
107
- after?: string[];
108
- };
91
+ port?: number;
92
+ hostname?: string;
93
+ reusePort?: boolean;
94
+ development?: boolean;
95
+ routesDir?: string;
109
96
 
110
- // Direct middleware functions (preferred approach)
97
+ // Middleware functions
111
98
  before?: BeforeMiddlewareHandler<TTypes>[];
112
99
  after?: AfterMiddlewareHandler<TTypes>[];
113
100
 
114
- // Handler configuration - supports both file paths and direct functions
115
- handlers?: {
116
- auth?: string;
117
- cache?: string;
118
- };
119
-
120
- // Direct handler functions (preferred approach)
101
+ // Handler functions
121
102
  auth?: ProtectedHandler<TTypes>;
122
103
  cache?: CacheHandler;
123
104