vector-framework 0.8.1 → 0.8.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/src/cli/index.ts CHANGED
@@ -1,34 +1,35 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { join } from 'node:path';
4
- import { parseArgs } from 'node:util';
5
- import vector from '../core/vector';
3
+ import { watch } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { parseArgs } from "node:util";
6
+ import vector from "../core/vector";
6
7
 
7
8
  const { values, positionals } = parseArgs({
8
9
  args: Bun.argv.slice(2),
9
10
  options: {
10
11
  port: {
11
- type: 'string',
12
- short: 'p',
13
- default: '3000',
12
+ type: "string",
13
+ short: "p",
14
+ default: "3000",
14
15
  },
15
16
  host: {
16
- type: 'string',
17
- short: 'h',
18
- default: 'localhost',
17
+ type: "string",
18
+ short: "h",
19
+ default: "localhost",
19
20
  },
20
21
  routes: {
21
- type: 'string',
22
- short: 'r',
23
- default: './routes',
22
+ type: "string",
23
+ short: "r",
24
+ default: "./routes",
24
25
  },
25
26
  watch: {
26
- type: 'boolean',
27
- short: 'w',
27
+ type: "boolean",
28
+ short: "w",
28
29
  default: true,
29
30
  },
30
31
  cors: {
31
- type: 'boolean',
32
+ type: "boolean",
32
33
  default: true,
33
34
  },
34
35
  },
@@ -36,13 +37,15 @@ const { values, positionals } = parseArgs({
36
37
  allowPositionals: true,
37
38
  });
38
39
 
39
- const command = positionals[0] || 'dev';
40
+ const command = positionals[0] || "dev";
40
41
 
41
42
  async function runDev() {
42
- const isDev = command === 'dev';
43
- console.log(`\n→ Starting Vector ${isDev ? 'development' : 'production'} server\n`);
43
+ const isDev = command === "dev";
44
+ console.log(
45
+ `\n→ Starting Vector ${isDev ? "development" : "production"} server\n`
46
+ );
44
47
 
45
- const config = {
48
+ const config: any = {
46
49
  port: Number.parseInt(values.port as string),
47
50
  hostname: values.host as string,
48
51
  routesDir: values.routes as string,
@@ -50,22 +53,39 @@ async function runDev() {
50
53
  autoDiscover: true,
51
54
  cors: values.cors
52
55
  ? {
53
- origin: '*',
56
+ origin: "*",
54
57
  credentials: true,
55
- allowHeaders: 'Content-Type, Authorization',
56
- allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
57
- exposeHeaders: 'Authorization',
58
+ allowHeaders: "Content-Type, Authorization",
59
+ allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
60
+ exposeHeaders: "Authorization",
58
61
  maxAge: 86400,
59
62
  }
60
63
  : undefined,
61
64
  };
62
65
 
63
66
  try {
64
- const userConfigPath = join(process.cwd(), 'vector.config.ts');
67
+ const userConfigPath = join(process.cwd(), "vector.config.ts");
65
68
  try {
66
69
  const userConfig = await import(userConfigPath);
67
70
  if (userConfig.default) {
68
- Object.assign(config, userConfig.default);
71
+ // Properly merge config, preserving middleware arrays
72
+ const {
73
+ before,
74
+ finally: finallyMiddleware,
75
+ ...otherConfig
76
+ } = userConfig.default;
77
+
78
+ // Merge non-middleware config
79
+ Object.assign(config, otherConfig);
80
+
81
+ // Handle middleware arrays properly - these need to be set after Object.assign
82
+ // to avoid being overwritten
83
+ if (before) {
84
+ config.before = before;
85
+ }
86
+ if (finallyMiddleware) {
87
+ config.finally = finallyMiddleware;
88
+ }
69
89
  }
70
90
  } catch {
71
91
  // No user config file, use defaults
@@ -73,32 +93,56 @@ async function runDev() {
73
93
 
74
94
  await vector.serve(config);
75
95
 
76
- const gray = '\x1b[90m';
77
- const reset = '\x1b[0m';
78
- const cyan = '\x1b[36m';
79
- const green = '\x1b[32m';
96
+ const gray = "\x1b[90m";
97
+ const reset = "\x1b[0m";
98
+ const cyan = "\x1b[36m";
99
+ const green = "\x1b[32m";
80
100
 
81
101
  console.log(` ${gray}Routes${reset} ${config.routesDir}`);
82
102
  if (isDev && values.watch) {
83
- console.log(` ${gray}Watching${reset} Enabled`);
103
+ console.log(` ${gray}Watching${reset} All project files`);
104
+
105
+ try {
106
+ // Watch entire project directory for changes
107
+ watch(process.cwd(), { recursive: true }, async (_, filename) => {
108
+ if (
109
+ filename &&
110
+ (filename.endsWith(".ts") ||
111
+ filename.endsWith(".js") ||
112
+ filename.endsWith(".json"))
113
+ ) {
114
+ console.log(`\n 🔄 File changed: ${filename}`);
115
+ console.log(" 🔄 Restarting server...\n");
116
+
117
+ // Exit the current process, which will trigger a restart if using --watch flag
118
+ process.exit(0);
119
+ }
120
+ });
121
+ } catch (err) {
122
+ console.warn(" ⚠️ File watching not available");
123
+ }
84
124
  }
85
- console.log(` ${gray}CORS${reset} ${values.cors ? 'Enabled' : 'Disabled'}`);
86
- console.log(` ${gray}Mode${reset} ${isDev ? 'Development' : 'Production'}\n`);
125
+ console.log(
126
+ ` ${gray}CORS${reset} ${values.cors ? "Enabled" : "Disabled"}`
127
+ );
128
+ console.log(
129
+ ` ${gray}Mode${reset} ${isDev ? "Development" : "Production"}\n`
130
+ );
87
131
  console.log(
88
132
  ` ${green}Ready${reset} → ${cyan}http://${config.hostname}:${config.port}${reset}\n`
89
133
  );
90
134
  } catch (error) {
91
- console.error('[ERROR] Failed to start server:', error);
135
+ console.error("[ERROR] Failed to start server:", error);
92
136
  process.exit(1);
93
137
  }
94
138
  }
95
139
 
96
140
  async function runBuild() {
97
- console.log('\n→ Building Vector application\n');
141
+ console.log("\n→ Building Vector application\n");
98
142
 
99
143
  try {
100
- const { RouteScanner } = await import('../dev/route-scanner');
101
- const { RouteGenerator } = await import('../dev/route-generator');
144
+ const { RouteScanner } = await import("../dev/route-scanner");
145
+ const { RouteGenerator } = await import("../dev/route-generator");
102
146
 
103
147
  const scanner = new RouteScanner(values.routes as string);
104
148
  const generator = new RouteGenerator();
@@ -109,31 +153,31 @@ async function runBuild() {
109
153
  console.log(` Generated ${routes.length} routes`);
110
154
 
111
155
  const buildProcess = Bun.spawn([
112
- 'bun',
113
- 'build',
114
- 'src/index.ts',
115
- '--outdir',
116
- 'dist',
117
- '--minify',
156
+ "bun",
157
+ "build",
158
+ "src/index.ts",
159
+ "--outdir",
160
+ "dist",
161
+ "--minify",
118
162
  ]);
119
163
  await buildProcess.exited;
120
164
 
121
- console.log('\n ✓ Build complete\n');
165
+ console.log("\n ✓ Build complete\n");
122
166
  } catch (error) {
123
- console.error('[ERROR] Build failed:', error);
167
+ console.error("[ERROR] Build failed:", error);
124
168
  process.exit(1);
125
169
  }
126
170
  }
127
171
 
128
172
  switch (command) {
129
- case 'dev':
173
+ case "dev":
130
174
  await runDev();
131
175
  break;
132
- case 'build':
176
+ case "build":
133
177
  await runBuild();
134
178
  break;
135
- case 'start':
136
- process.env.NODE_ENV = 'production';
179
+ case "start":
180
+ process.env.NODE_ENV = "production";
137
181
  await runDev();
138
182
  break;
139
183
  default:
@@ -140,7 +140,8 @@ export class VectorRouter<TTypes extends VectorTypes = DefaultVectorTypes> {
140
140
 
141
141
  request = vectorRequest;
142
142
  try {
143
- if (!options.expose) {
143
+ // Default expose to true if not specified
144
+ if (options.expose === false) {
144
145
  return APIError.forbidden('Forbidden');
145
146
  }
146
147
 
@@ -105,12 +105,6 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
105
105
  this.server = new VectorServer<TTypes>(this.router, this.config);
106
106
  const bunServer = await this.server.start();
107
107
 
108
- if (this.config.development && this.routeScanner) {
109
- this.routeScanner.enableWatch(async () => {
110
- await this.discoverRoutes();
111
- });
112
- }
113
-
114
108
  return bunServer;
115
109
  }
116
110
 
@@ -163,7 +157,7 @@ export class Vector<TTypes extends VectorTypes = DefaultVectorTypes> {
163
157
  console.log(`✅ Loaded ${routes.length} routes from ${routesDir}`);
164
158
  }
165
159
  } catch (error) {
166
- if ((error as any).code !== 'ENOENT') {
160
+ if ((error as any).code !== 'ENOENT' && (error as any).code !== 'ENOTDIR') {
167
161
  console.error('Failed to discover routes:', error);
168
162
  }
169
163
  }
@@ -1,4 +1,4 @@
1
- import { mkdir, writeFile } from 'node:fs/promises';
1
+ import { promises as fs } from 'node:fs';
2
2
  import { dirname, relative } from 'node:path';
3
3
  import type { GeneratedRoute } from '../types';
4
4
 
@@ -11,7 +11,7 @@ export class RouteGenerator {
11
11
 
12
12
  async generate(routes: GeneratedRoute[]): Promise<void> {
13
13
  const outputDir = dirname(this.outputPath);
14
- await mkdir(outputDir, { recursive: true });
14
+ await fs.mkdir(outputDir, { recursive: true });
15
15
 
16
16
  const imports: string[] = [];
17
17
  const groupedByFile = new Map<string, GeneratedRoute[]>();
@@ -64,7 +64,7 @@ ${routeEntries.join('\n')}
64
64
  export default routes;
65
65
  `;
66
66
 
67
- await writeFile(this.outputPath, content, 'utf-8');
67
+ await fs.writeFile(this.outputPath, content, 'utf-8');
68
68
  console.log(`Generated routes file: ${this.outputPath}`);
69
69
  }
70
70
 
@@ -1,4 +1,4 @@
1
- import { readdir, stat } from 'node:fs/promises';
1
+ import { promises as fs } from 'node:fs';
2
2
  import { join, relative, resolve, sep } from 'node:path';
3
3
  import type { GeneratedRoute } from '../types';
4
4
 
@@ -26,11 +26,11 @@ export class RouteScanner {
26
26
  }
27
27
 
28
28
  private async scanDirectory(dir: string, routes: GeneratedRoute[], basePath = ''): Promise<void> {
29
- const entries = await readdir(dir);
29
+ const entries = await fs.readdir(dir);
30
30
 
31
31
  for (const entry of entries) {
32
32
  const fullPath = join(dir, entry);
33
- const stats = await stat(fullPath);
33
+ const stats = await fs.stat(fullPath);
34
34
 
35
35
  if (stats.isDirectory()) {
36
36
  const newBasePath = basePath ? `${basePath}/${entry}` : entry;
@@ -64,7 +64,7 @@ export interface RouteOptions<TTypes extends VectorTypes = DefaultVectorTypes> {
64
64
  method: string;
65
65
  path: string;
66
66
  auth?: boolean;
67
- expose?: boolean;
67
+ expose?: boolean; // defaults to true
68
68
  cache?: CacheOptions | number;
69
69
  rawRequest?: boolean;
70
70
  rawResponse?: boolean;