vector-framework 1.2.0 → 1.2.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.
Files changed (77) hide show
  1. package/README.md +19 -0
  2. package/dist/auth/protected.d.ts +1 -0
  3. package/dist/auth/protected.d.ts.map +1 -1
  4. package/dist/auth/protected.js +3 -0
  5. package/dist/auth/protected.js.map +1 -1
  6. package/dist/cache/manager.d.ts +1 -0
  7. package/dist/cache/manager.d.ts.map +1 -1
  8. package/dist/cache/manager.js +3 -0
  9. package/dist/cache/manager.js.map +1 -1
  10. package/dist/cli/graceful-shutdown.d.ts +15 -0
  11. package/dist/cli/graceful-shutdown.d.ts.map +1 -0
  12. package/dist/cli/graceful-shutdown.js +42 -0
  13. package/dist/cli/graceful-shutdown.js.map +1 -0
  14. package/dist/cli/index.js +37 -43
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli.js +967 -222
  17. package/dist/core/config-loader.d.ts.map +1 -1
  18. package/dist/core/config-loader.js +5 -2
  19. package/dist/core/config-loader.js.map +1 -1
  20. package/dist/core/server.d.ts +4 -0
  21. package/dist/core/server.d.ts.map +1 -1
  22. package/dist/core/server.js +240 -9
  23. package/dist/core/server.js.map +1 -1
  24. package/dist/core/vector.d.ts +4 -2
  25. package/dist/core/vector.d.ts.map +1 -1
  26. package/dist/core/vector.js +32 -2
  27. package/dist/core/vector.js.map +1 -1
  28. package/dist/errors/index.cjs +2 -0
  29. package/dist/index.cjs +1434 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +12 -1327
  33. package/dist/index.js.map +1 -1
  34. package/dist/index.mjs +153 -46
  35. package/dist/openapi/docs-ui.d.ts +1 -1
  36. package/dist/openapi/docs-ui.d.ts.map +1 -1
  37. package/dist/openapi/docs-ui.js +147 -35
  38. package/dist/openapi/docs-ui.js.map +1 -1
  39. package/dist/openapi/generator.d.ts.map +1 -1
  40. package/dist/openapi/generator.js +318 -6
  41. package/dist/openapi/generator.js.map +1 -1
  42. package/dist/start-vector.d.ts +3 -0
  43. package/dist/start-vector.d.ts.map +1 -0
  44. package/dist/start-vector.js +38 -0
  45. package/dist/start-vector.js.map +1 -0
  46. package/dist/types/index.d.ts +25 -0
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/utils/logger.js +1 -1
  49. package/dist/utils/validation.d.ts.map +1 -1
  50. package/dist/utils/validation.js +2 -0
  51. package/dist/utils/validation.js.map +1 -1
  52. package/package.json +10 -14
  53. package/src/auth/protected.ts +4 -0
  54. package/src/cache/manager.ts +4 -0
  55. package/src/cli/graceful-shutdown.ts +60 -0
  56. package/src/cli/index.ts +42 -49
  57. package/src/core/config-loader.ts +5 -2
  58. package/src/core/server.ts +304 -9
  59. package/src/core/vector.ts +38 -4
  60. package/src/index.ts +4 -3
  61. package/src/openapi/assets/favicon/android-chrome-192x192.png +0 -0
  62. package/src/openapi/assets/favicon/android-chrome-512x512.png +0 -0
  63. package/src/openapi/assets/favicon/apple-touch-icon.png +0 -0
  64. package/src/openapi/assets/favicon/favicon-16x16.png +0 -0
  65. package/src/openapi/assets/favicon/favicon-32x32.png +0 -0
  66. package/src/openapi/assets/favicon/favicon.ico +0 -0
  67. package/src/openapi/assets/favicon/site.webmanifest +11 -0
  68. package/src/openapi/assets/logo.svg +12 -0
  69. package/src/openapi/assets/logo_dark.svg +6 -0
  70. package/src/openapi/assets/logo_icon.png +0 -0
  71. package/src/openapi/assets/logo_white.svg +6 -0
  72. package/src/openapi/docs-ui.ts +153 -35
  73. package/src/openapi/generator.ts +341 -6
  74. package/src/start-vector.ts +50 -0
  75. package/src/types/index.ts +34 -0
  76. package/src/utils/logger.ts +1 -1
  77. package/src/utils/validation.ts +2 -0
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "vector-framework",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "author": "webhie-com",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/webhie-com/vector.git"
8
8
  },
9
- "main": "./dist/index.js",
9
+ "main": "./dist/index.cjs",
10
10
  "module": "./dist/index.mjs",
11
11
  "dependencies": {},
12
12
  "devDependencies": {
13
13
  "@biomejs/biome": "^2.4.6",
14
14
  "@biomejs/cli-darwin-arm64": "^2.4.6",
15
15
  "@types/bun": "latest",
16
+ "arktype": "^2.2.0",
16
17
  "oxlint": "^1.15.0",
17
18
  "typescript": "^5.0.0",
19
+ "valibot": "^1.2.0",
18
20
  "zod": "^4.3.6"
19
21
  },
20
22
  "peerDependencies": {
@@ -24,22 +26,16 @@
24
26
  ".": {
25
27
  "types": "./dist/index.d.ts",
26
28
  "import": "./dist/index.mjs",
27
- "require": "./dist/index.js"
28
- },
29
- "./middleware": {
30
- "types": "./dist/middleware/index.d.ts",
31
- "import": "./dist/middleware/index.mjs",
32
- "require": "./dist/middleware/index.js"
29
+ "require": "./dist/index.cjs"
33
30
  },
34
31
  "./types": {
35
32
  "types": "./dist/types/index.d.ts",
36
- "import": "./dist/types/index.mjs",
37
- "require": "./dist/types/index.js"
33
+ "default": "./dist/types/index.js"
38
34
  },
39
35
  "./errors": {
40
36
  "types": "./dist/errors/index.d.ts",
41
- "import": "./dist/errors/index.mjs",
42
- "require": "./dist/errors/index.js"
37
+ "import": "./dist/errors/index.js",
38
+ "require": "./dist/errors/index.cjs"
43
39
  }
44
40
  },
45
41
  "bin": {
@@ -79,7 +75,7 @@
79
75
  "build:lib": "bun run build:clean && bun run build:ts && bun run build:bundle && bun run build:cli",
80
76
  "build:clean": "rm -rf dist",
81
77
  "build:ts": "tsc",
82
- "build:bundle": "bun build src/index.ts --format esm --minify --outfile dist/index.mjs && bun build src/index.ts --format cjs --minify --outfile dist/index.js",
78
+ "build:bundle": "bun build src/index.ts --target bun --format esm --minify --outfile dist/index.mjs && bun build src/index.ts --target bun --format cjs --minify --outfile dist/index.cjs && bun build src/errors/index.ts --target bun --format cjs --minify --outfile dist/errors/index.cjs",
83
79
  "build:cli": "bun build src/cli/index.ts --target bun --outfile dist/cli.js",
84
80
  "local:cli:build": "bun run build:lib",
85
81
  "local:cli:dev": "bun run dist/cli.js dev --config ./vector.config.ts",
@@ -88,7 +84,7 @@
88
84
  "test:unit": "bun test tests/*.test.ts",
89
85
  "test:watch": "bun test --watch tests/*.test.ts",
90
86
  "test:coverage": "bun test --coverage tests/*.test.ts",
91
- "test:e2e": "bun test --max-concurrency 1 tests/e2e/e2e.test.ts tests/e2e/zod-io.e2e.test.ts",
87
+ "test:e2e": "bun test --max-concurrency 1 tests/e2e/e2e.test.ts tests/e2e/zod-io.e2e.test.ts tests/e2e/package-exports.test.ts",
92
88
  "test:load": "bun run tests/e2e/load.test.ts",
93
89
  "test:soak": "bun run tests/e2e/soak.test.ts",
94
90
  "test:benchmark": "bun run tests/e2e/benchmark.test.ts",
@@ -7,6 +7,10 @@ export class AuthManager<TTypes extends VectorTypes = DefaultVectorTypes> {
7
7
  this.protectedHandler = handler;
8
8
  }
9
9
 
10
+ clearProtectedHandler() {
11
+ this.protectedHandler = null;
12
+ }
13
+
10
14
  async authenticate(request: VectorRequest<TTypes>): Promise<GetAuthType<TTypes> | null> {
11
15
  if (!this.protectedHandler) {
12
16
  throw new Error('Protected handler not configured. Use vector.protected() to set authentication handler.');
@@ -16,6 +16,10 @@ export class CacheManager<TTypes extends VectorTypes = DefaultVectorTypes> {
16
16
  this.cacheHandler = handler;
17
17
  }
18
18
 
19
+ clearCacheHandler() {
20
+ this.cacheHandler = null;
21
+ }
22
+
19
23
  async get<T = GetCacheType<TTypes>>(
20
24
  key: string,
21
25
  factory: () => Promise<T>,
@@ -0,0 +1,60 @@
1
+ type SignalEvent = 'SIGINT' | 'SIGTERM';
2
+
3
+ interface ShutdownTarget {
4
+ shutdown?: () => Promise<void> | void;
5
+ stop?: () => void;
6
+ }
7
+
8
+ interface GracefulShutdownOptions {
9
+ getTarget: () => ShutdownTarget | null | undefined;
10
+ on?: (event: SignalEvent, listener: () => void) => void;
11
+ off?: (event: SignalEvent, listener: () => void) => void;
12
+ exit?: (code: number) => void;
13
+ logError?: (message?: any, ...optionalParams: any[]) => void;
14
+ }
15
+
16
+ export function installGracefulShutdownHandlers(options: GracefulShutdownOptions): () => void {
17
+ const on = options.on ?? ((event: SignalEvent, listener: () => void) => process.on(event, listener));
18
+ const off = options.off ?? ((event: SignalEvent, listener: () => void) => process.off(event, listener));
19
+ const exit = options.exit ?? ((code: number) => process.exit(code));
20
+ const logError = options.logError ?? console.error;
21
+ let shuttingDown = false;
22
+
23
+ const handleSignal = async (signal: SignalEvent): Promise<void> => {
24
+ if (shuttingDown) {
25
+ return;
26
+ }
27
+ shuttingDown = true;
28
+
29
+ try {
30
+ const target = options.getTarget();
31
+ if (target) {
32
+ if (typeof target.shutdown === 'function') {
33
+ await target.shutdown();
34
+ } else if (typeof target.stop === 'function') {
35
+ target.stop();
36
+ }
37
+ }
38
+ exit(0);
39
+ } catch (error) {
40
+ logError(`[vector] Graceful shutdown failed after ${signal}:`, error);
41
+ exit(1);
42
+ }
43
+ };
44
+
45
+ const onSigint = () => {
46
+ void handleSignal('SIGINT');
47
+ };
48
+
49
+ const onSigterm = () => {
50
+ void handleSignal('SIGTERM');
51
+ };
52
+
53
+ on('SIGINT', onSigint);
54
+ on('SIGTERM', onSigterm);
55
+
56
+ return () => {
57
+ off('SIGINT', onSigint);
58
+ off('SIGTERM', onSigterm);
59
+ };
60
+ }
package/src/cli/index.ts CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  import { watch } from 'node:fs';
4
4
  import { parseArgs } from 'node:util';
5
- import { getVectorInstance } from '../core/vector';
6
- import { ConfigLoader } from '../core/config-loader';
7
5
  import { resolveHost, resolvePort, resolveRoutesDir } from './option-resolution';
6
+ import { installGracefulShutdownHandlers } from './graceful-shutdown';
7
+ import { startVector } from '../start-vector';
8
+ import type { StartedVectorApp } from '../types';
8
9
 
9
10
  // Compatibility layer for both Node and Bun
10
11
  const args = typeof Bun !== 'undefined' ? Bun.argv.slice(2) : process.argv.slice(2);
@@ -54,9 +55,10 @@ async function runDev() {
54
55
  const isDev = command === 'dev';
55
56
 
56
57
  let server: any = null;
57
- let vector: any = null;
58
+ let app: StartedVectorApp<any> | null = null;
59
+ let removeShutdownHandlers: (() => void) | null = null;
58
60
 
59
- async function startServer(): Promise<{ server: any; vector: any; config: any }> {
61
+ async function startServer(): Promise<{ server: any; app: StartedVectorApp<any>; config: any }> {
60
62
  // Create a timeout promise that rejects after 10 seconds
61
63
  const timeoutPromise = new Promise<never>((_, reject) => {
62
64
  setTimeout(() => {
@@ -65,50 +67,35 @@ async function runDev() {
65
67
  });
66
68
 
67
69
  // Create the actual server start promise
68
- const serverStartPromise = (async (): Promise<{ server: any; vector: any; config: any }> => {
70
+ const serverStartPromise = (async (): Promise<{ server: any; app: StartedVectorApp<any>; config: any }> => {
69
71
  const explicitConfigPath = values.config as string | undefined;
70
- const configLoader = new ConfigLoader(explicitConfigPath);
71
- const loadedConfig = await configLoader.load();
72
- const config = { ...loadedConfig } as Record<string, any>;
73
-
74
- // Merge CLI options with loaded config.
75
- // Explicit --port/--host always override config values.
76
- config.port = resolvePort(config.port, hasPortOption, values.port as string);
77
- config.hostname = resolveHost(config.hostname, hasHostOption, values.host as string);
78
- config.routesDir = resolveRoutesDir(config.routesDir, hasRoutesOption, values.routes as string);
79
- config.development = config.development ?? isDev;
80
- config.autoDiscover = true; // Always auto-discover routes
81
-
82
- // Apply CLI CORS option if not explicitly set in config
83
- // Only apply default CORS if config.cors is undefined (not set)
84
- if (config.cors === undefined && values.cors) {
85
- config.cors = {
86
- origin: '*',
87
- credentials: true,
88
- allowHeaders: 'Content-Type, Authorization',
89
- allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
90
- exposeHeaders: 'Authorization',
91
- maxAge: 86400,
92
- };
93
- }
94
-
95
- // Get Vector instance and configure handlers
96
- vector = getVectorInstance();
97
-
98
- // Load and set auth handler if configured
99
- const authHandler = await configLoader.loadAuthHandler();
100
- if (authHandler) {
101
- vector.setProtectedHandler(authHandler);
102
- }
72
+ app = await startVector({
73
+ configPath: explicitConfigPath,
74
+ mutateConfig: (loadedConfig) => {
75
+ const config = { ...loadedConfig } as Record<string, any>;
76
+ config.port = resolvePort(config.port, hasPortOption, values.port as string);
77
+ config.hostname = resolveHost(config.hostname, hasHostOption, values.host as string);
78
+ config.routesDir = resolveRoutesDir(config.routesDir, hasRoutesOption, values.routes as string);
79
+ config.development = config.development ?? isDev;
80
+ config.autoDiscover = true; // Always auto-discover routes
81
+
82
+ if (config.cors === undefined && values.cors) {
83
+ config.cors = {
84
+ origin: '*',
85
+ credentials: true,
86
+ allowHeaders: 'Content-Type, Authorization',
87
+ allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
88
+ exposeHeaders: 'Authorization',
89
+ maxAge: 86400,
90
+ };
91
+ }
103
92
 
104
- // Load and set cache handler if configured
105
- const cacheHandler = await configLoader.loadCacheHandler();
106
- if (cacheHandler) {
107
- vector.setCacheHandler(cacheHandler);
108
- }
93
+ return config;
94
+ },
95
+ });
109
96
 
110
- // Start the server
111
- server = await vector.startServer(config);
97
+ server = app.server;
98
+ const config = app.config as Record<string, any>;
112
99
 
113
100
  // Verify the server is actually running
114
101
  if (!server || !server.port) {
@@ -120,7 +107,7 @@ async function runDev() {
120
107
 
121
108
  console.log(`\nListening on ${cyan}http://${config.hostname}:${config.port}${reset}\n`);
122
109
 
123
- return { server, vector, config };
110
+ return { server, app, config };
124
111
  })();
125
112
 
126
113
  // Race between server startup and timeout
@@ -132,6 +119,12 @@ async function runDev() {
132
119
  const result = await startServer();
133
120
  server = result.server;
134
121
 
122
+ if (!removeShutdownHandlers) {
123
+ removeShutdownHandlers = installGracefulShutdownHandlers({
124
+ getTarget: () => app,
125
+ });
126
+ }
127
+
135
128
  // Setup file watching for hot reload
136
129
  if (isDev && values.watch) {
137
130
  try {
@@ -173,8 +166,8 @@ async function runDev() {
173
166
  changedFiles.clear();
174
167
 
175
168
  // Stop the current server
176
- if (vector) {
177
- vector.stop();
169
+ if (app) {
170
+ app.stop();
178
171
  }
179
172
 
180
173
  // Small delay to ensure file system operations complete
@@ -184,7 +177,7 @@ async function runDev() {
184
177
  try {
185
178
  const result = await startServer();
186
179
  server = result.server;
187
- vector = result.vector;
180
+ app = result.app;
188
181
  } catch (error: any) {
189
182
  console.error('\n[Reload Error]', error.message || error);
190
183
  // Don't exit the process on reload failures, just continue watching
@@ -35,8 +35,8 @@ export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
35
35
  this.configSource = 'user';
36
36
  } catch (error: any) {
37
37
  const msg = error instanceof Error ? error.message : String(error);
38
- console.error(`[Vector] Failed to load config from ${this.configPath}: ${msg}`);
39
- console.error('[Vector] Server is using default configuration. Fix your config file and restart.');
38
+ console.error(`[vector] Failed to load config from ${this.configPath}: ${msg}`);
39
+ console.error('[vector] Server is using default configuration. Fix your config file and restart.');
40
40
  this.config = {};
41
41
  }
42
42
  } else {
@@ -62,9 +62,12 @@ export class ConfigLoader<TTypes extends VectorTypes = DefaultVectorTypes> {
62
62
  config.reusePort = this.config.reusePort;
63
63
  config.development = this.config.development;
64
64
  config.routesDir = this.config.routesDir || './routes';
65
+ config.routeExcludePatterns = this.config.routeExcludePatterns;
65
66
  config.idleTimeout = this.config.idleTimeout;
66
67
  config.defaults = this.config.defaults;
67
68
  config.openapi = this.config.openapi;
69
+ config.startup = this.config.startup;
70
+ config.shutdown = this.config.shutdown;
68
71
  }
69
72
 
70
73
  // Always auto-discover routes