@veloxts/velox 0.7.1 → 0.7.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @veloxts/velox
2
2
 
3
+ ## 0.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(cli): auto-populate Zod schemas from Prisma model fields
8
+ - Updated dependencies
9
+ - @veloxts/auth@0.7.3
10
+ - @veloxts/cache@0.7.3
11
+ - @veloxts/core@0.7.3
12
+ - @veloxts/events@0.7.3
13
+ - @veloxts/mail@0.7.3
14
+ - @veloxts/orm@0.7.3
15
+ - @veloxts/queue@0.7.3
16
+ - @veloxts/router@0.7.3
17
+ - @veloxts/scheduler@0.7.3
18
+ - @veloxts/storage@0.7.3
19
+ - @veloxts/validation@0.7.3
20
+
21
+ ## 0.7.2
22
+
23
+ ### Patch Changes
24
+
25
+ - simplify code for clarity and maintainability
26
+ - Updated dependencies
27
+ - @veloxts/core@0.7.2
28
+
3
29
  ## 0.7.1
4
30
 
5
31
  ### Patch Changes
package/GUIDE.md CHANGED
@@ -100,7 +100,7 @@ const userProcedures = procedures('users', {
100
100
  getPublicProfile: procedure()
101
101
  .query(async ({ input, ctx }) => {
102
102
  const user = await ctx.db.user.findUnique({ where: { id: input.id } });
103
- return resource(user, UserSchema).forAnonymous();
103
+ return resource(user, UserSchema.public);
104
104
  }),
105
105
 
106
106
  // Authenticated: returns { id, name, email }
@@ -108,7 +108,7 @@ const userProcedures = procedures('users', {
108
108
  .guard(authenticated)
109
109
  .query(async ({ input, ctx }) => {
110
110
  const user = await ctx.db.user.findUnique({ where: { id: input.id } });
111
- return resource(user, UserSchema).forAuthenticated();
111
+ return resource(user, UserSchema.authenticated);
112
112
  }),
113
113
 
114
114
  // Admin: returns all fields
@@ -116,7 +116,7 @@ const userProcedures = procedures('users', {
116
116
  .guard(hasRole('admin'))
117
117
  .query(async ({ input, ctx }) => {
118
118
  const user = await ctx.db.user.findUnique({ where: { id: input.id } });
119
- return resource(user, UserSchema).forAdmin();
119
+ return resource(user, UserSchema.admin);
120
120
  }),
121
121
  });
122
122
  ```
@@ -160,77 +160,32 @@ const productionAuthPreset = {
160
160
  },
161
161
  };
162
162
  /**
163
- * Create production preset with validated environment variables.
163
+ * Build a production preset from environment variable values.
164
+ * Accepts either validated or raw values.
164
165
  */
165
- function createProductionPreset(env) {
166
+ function buildProductionPreset(vars) {
166
167
  return {
167
168
  cache: {
168
169
  driver: 'redis',
169
- config: {
170
- url: env.REDIS_URL,
171
- },
170
+ config: { url: vars.REDIS_URL },
172
171
  },
173
172
  queue: {
174
173
  driver: 'bullmq',
175
- config: {
176
- url: env.REDIS_URL,
177
- },
174
+ config: { url: vars.REDIS_URL },
178
175
  },
179
176
  mail: {
180
177
  driver: 'resend',
181
- config: {
182
- apiKey: env.RESEND_API_KEY,
183
- },
178
+ config: { apiKey: vars.RESEND_API_KEY },
184
179
  },
185
180
  storage: {
186
181
  driver: 's3',
187
- bucket: env.S3_BUCKET,
188
- region: env.AWS_REGION,
182
+ bucket: vars.S3_BUCKET,
183
+ region: vars.AWS_REGION,
189
184
  },
190
185
  events: {
191
186
  driver: 'ws',
192
187
  path: '/ws',
193
- redis: env.REDIS_URL,
194
- },
195
- scheduler: {
196
- tasks: [],
197
- },
198
- auth: productionAuthPreset,
199
- };
200
- }
201
- /**
202
- * Create production preset with current environment variables (unvalidated).
203
- * Used for the module-level export. Prefer getPreset('production') which validates.
204
- */
205
- function createProductionPresetFromEnv() {
206
- return {
207
- cache: {
208
- driver: 'redis',
209
- config: {
210
- url: process.env.REDIS_URL,
211
- },
212
- },
213
- queue: {
214
- driver: 'bullmq',
215
- config: {
216
- url: process.env.REDIS_URL,
217
- },
218
- },
219
- mail: {
220
- driver: 'resend',
221
- config: {
222
- apiKey: process.env.RESEND_API_KEY ?? '',
223
- },
224
- },
225
- storage: {
226
- driver: 's3',
227
- bucket: process.env.S3_BUCKET ?? '',
228
- region: process.env.AWS_REGION ?? 'us-east-1',
229
- },
230
- events: {
231
- driver: 'ws',
232
- path: '/ws',
233
- redis: process.env.REDIS_URL,
188
+ redis: vars.REDIS_URL,
234
189
  },
235
190
  scheduler: {
236
191
  tasks: [],
@@ -254,7 +209,12 @@ function createProductionPresetFromEnv() {
254
209
  *
255
210
  * @see validateProductionEnv() to check if all required vars are set
256
211
  */
257
- export const productionPreset = createProductionPresetFromEnv();
212
+ export const productionPreset = buildProductionPreset({
213
+ REDIS_URL: process.env.REDIS_URL,
214
+ RESEND_API_KEY: process.env.RESEND_API_KEY ?? '',
215
+ S3_BUCKET: process.env.S3_BUCKET ?? '',
216
+ AWS_REGION: process.env.AWS_REGION ?? 'us-east-1',
217
+ });
258
218
  /**
259
219
  * Get preset configuration for an environment.
260
220
  * For production, validates required environment variables first.
@@ -267,11 +227,8 @@ export function getPreset(env) {
267
227
  return developmentPreset;
268
228
  case 'test':
269
229
  return testPreset;
270
- case 'production': {
271
- const env = validateProductionEnv();
272
- // Return fresh preset to pick up env vars set after module load
273
- return createProductionPreset(env);
274
- }
230
+ case 'production':
231
+ return buildProductionPreset(validateProductionEnv());
275
232
  }
276
233
  }
277
234
  /**
@@ -2,23 +2,32 @@
2
2
  * Environment detection utilities.
3
3
  */
4
4
  /**
5
- * Detect current environment from NODE_ENV.
6
- * Defaults to 'development' if not set or unrecognized.
5
+ * Normalize a raw environment string to a recognized Environment value.
6
+ * Returns undefined if the value is not recognized.
7
7
  */
8
- export function detectEnvironment() {
9
- const env = process.env.NODE_ENV?.toLowerCase().trim();
10
- switch (env) {
8
+ function normalizeEnvironment(value) {
9
+ const normalized = value?.toLowerCase().trim();
10
+ switch (normalized) {
11
11
  case 'production':
12
12
  case 'prod':
13
13
  return 'production';
14
14
  case 'test':
15
15
  case 'testing':
16
16
  return 'test';
17
- default:
18
- // 'development', 'dev', undefined, or any unrecognized value
17
+ case 'development':
18
+ case 'dev':
19
19
  return 'development';
20
+ default:
21
+ return undefined;
20
22
  }
21
23
  }
24
+ /**
25
+ * Detect current environment from NODE_ENV.
26
+ * Defaults to 'development' if not set or unrecognized.
27
+ */
28
+ export function detectEnvironment() {
29
+ return normalizeEnvironment(process.env.NODE_ENV) ?? 'development';
30
+ }
22
31
  /**
23
32
  * Check if current environment is development.
24
33
  */
@@ -42,15 +51,9 @@ export function isTest() {
42
51
  * @throws Error if environment is not recognized.
43
52
  */
44
53
  export function validateEnvironment(env) {
45
- const normalized = env.toLowerCase().trim();
46
- if (normalized === 'production' || normalized === 'prod') {
47
- return 'production';
48
- }
49
- if (normalized === 'test' || normalized === 'testing') {
50
- return 'test';
51
- }
52
- if (normalized === 'development' || normalized === 'dev') {
53
- return 'development';
54
+ const result = normalizeEnvironment(env);
55
+ if (!result) {
56
+ throw new Error(`Invalid environment: "${env}". Must be one of: development, test, production`);
54
57
  }
55
- throw new Error(`Invalid environment: "${env}". Must be one of: development, test, production`);
58
+ return result;
56
59
  }
@@ -6,52 +6,28 @@ import { getPreset } from './defaults.js';
6
6
  import { detectEnvironment } from './env.js';
7
7
  import { mergeDeep } from './merge.js';
8
8
  const log = createLogger('velox');
9
+ /**
10
+ * Extract the driver name from a package config object.
11
+ */
12
+ function getDriver(config, defaultDriver) {
13
+ if (config !== null && typeof config === 'object' && 'driver' in config) {
14
+ const driver = config.driver;
15
+ if (typeof driver === 'string')
16
+ return driver;
17
+ }
18
+ return defaultDriver;
19
+ }
9
20
  /**
10
21
  * All ecosystem packages that can be registered.
11
22
  */
12
23
  const PACKAGES = {
13
- cache: {
14
- name: '@veloxts/cache',
15
- importPath: '@veloxts/cache',
16
- pluginExport: 'cachePlugin',
17
- getDriver: (c) => c?.driver ?? 'memory',
18
- },
19
- queue: {
20
- name: '@veloxts/queue',
21
- importPath: '@veloxts/queue',
22
- pluginExport: 'queuePlugin',
23
- getDriver: (c) => c?.driver ?? 'sync',
24
- },
25
- mail: {
26
- name: '@veloxts/mail',
27
- importPath: '@veloxts/mail',
28
- pluginExport: 'mailPlugin',
29
- getDriver: (c) => c?.driver ?? 'log',
30
- },
31
- storage: {
32
- name: '@veloxts/storage',
33
- importPath: '@veloxts/storage',
34
- pluginExport: 'storagePlugin',
35
- getDriver: (c) => c?.driver ?? 'local',
36
- },
37
- events: {
38
- name: '@veloxts/events',
39
- importPath: '@veloxts/events',
40
- pluginExport: 'eventsPlugin',
41
- getDriver: (c) => c?.driver ?? 'ws',
42
- },
43
- scheduler: {
44
- name: '@veloxts/scheduler',
45
- importPath: '@veloxts/scheduler',
46
- pluginExport: 'schedulerPlugin',
47
- getDriver: () => 'cron',
48
- },
49
- auth: {
50
- name: '@veloxts/auth',
51
- importPath: '@veloxts/auth',
52
- pluginExport: 'authPlugin',
53
- getDriver: () => 'jwt',
54
- },
24
+ cache: { name: '@veloxts/cache', pluginExport: 'cachePlugin', defaultDriver: 'memory' },
25
+ queue: { name: '@veloxts/queue', pluginExport: 'queuePlugin', defaultDriver: 'sync' },
26
+ mail: { name: '@veloxts/mail', pluginExport: 'mailPlugin', defaultDriver: 'log' },
27
+ storage: { name: '@veloxts/storage', pluginExport: 'storagePlugin', defaultDriver: 'local' },
28
+ events: { name: '@veloxts/events', pluginExport: 'eventsPlugin', defaultDriver: 'ws' },
29
+ scheduler: { name: '@veloxts/scheduler', pluginExport: 'schedulerPlugin', defaultDriver: 'cron' },
30
+ auth: { name: '@veloxts/auth', pluginExport: 'authPlugin', defaultDriver: 'jwt' },
55
31
  };
56
32
  /**
57
33
  * Packages that require special handling and are NOT auto-registered.
@@ -91,20 +67,18 @@ export async function registerEcosystemPlugins(app, preset, options) {
91
67
  const info = PACKAGES[pkg];
92
68
  try {
93
69
  // Dynamic import to avoid hard dependency
94
- const module = await import(info.importPath);
70
+ const module = await import(info.name);
95
71
  const plugin = module[info.pluginExport];
96
72
  if (!plugin) {
97
73
  throw new Error(`Plugin export '${info.pluginExport}' not found`);
98
74
  }
99
75
  await app.register(plugin(config));
100
76
  if (!options?.silent) {
101
- const driver = info.getDriver(config);
102
- log.info(` ✓ ${info.name} [${driver}]`);
77
+ log.info(` ✓ ${info.name} [${getDriver(config, info.defaultDriver)}]`);
103
78
  }
104
79
  }
105
80
  catch (error) {
106
81
  const err = error;
107
- // Check if it's a module not found error
108
82
  if (err.message?.includes('Cannot find module') || err.code === 'ERR_MODULE_NOT_FOUND') {
109
83
  throw new Error(`Package ${info.name} is not installed. Install it with: pnpm add ${info.name}`);
110
84
  }
@@ -145,25 +119,18 @@ export async function registerEcosystemPlugins(app, preset, options) {
145
119
  export async function usePresets(app, options = {}) {
146
120
  const env = options.env ?? detectEnvironment();
147
121
  const basePreset = getPreset(env);
148
- // Merge overrides with base preset using type-safe helper
149
- // Partial<T> is assignable to DeepPartial<T> for single-level overrides
150
- const merge = (base, override) => {
151
- if (!base)
152
- return undefined;
153
- if (!override)
154
- return base;
155
- // Partial<T> is compatible with DeepPartial<T> at the first level
156
- return mergeDeep(base, override);
157
- };
158
- const finalPreset = {
159
- cache: merge(basePreset.cache, options.overrides?.cache),
160
- queue: merge(basePreset.queue, options.overrides?.queue),
161
- mail: merge(basePreset.mail, options.overrides?.mail),
162
- storage: merge(basePreset.storage, options.overrides?.storage),
163
- events: merge(basePreset.events, options.overrides?.events),
164
- scheduler: merge(basePreset.scheduler, options.overrides?.scheduler),
165
- auth: merge(basePreset.auth, options.overrides?.auth),
166
- };
122
+ // Merge each package's overrides with its base preset
123
+ const finalPreset = { ...basePreset };
124
+ if (options.overrides) {
125
+ for (const key of Object.keys(options.overrides)) {
126
+ const base = basePreset[key];
127
+ const override = options.overrides[key];
128
+ if (base && override) {
129
+ // Partial<T> is compatible with DeepPartial<T> at the first level
130
+ finalPreset[key] = mergeDeep(base, override);
131
+ }
132
+ }
133
+ }
167
134
  if (!options.silent) {
168
135
  log.info(`\nVeloxTS Ecosystem Presets [${env}]`);
169
136
  }
@@ -24,13 +24,9 @@ function isValidLogLevel(value) {
24
24
  * @param defaultLevel - Fallback level if value is invalid
25
25
  */
26
26
  function parseLogLevel(value, defaultLevel) {
27
- if (value === undefined) {
28
- return defaultLevel;
29
- }
30
27
  if (isValidLogLevel(value)) {
31
28
  return value;
32
29
  }
33
- // Invalid log level - return default
34
30
  return defaultLevel;
35
31
  }
36
32
  /**
@@ -41,14 +37,11 @@ function parseLogLevel(value, defaultLevel) {
41
37
  * @param defaultPort - Fallback port if value is invalid
42
38
  */
43
39
  function parsePort(value, defaultPort) {
44
- if (value === undefined) {
40
+ if (value === undefined)
45
41
  return defaultPort;
46
- }
47
42
  const parsed = parseInt(value, 10);
48
- // Check for NaN and ensure port is in valid range (1-65535)
49
- if (Number.isNaN(parsed) || parsed < 1 || parsed > 65535) {
43
+ if (Number.isNaN(parsed) || parsed < 1 || parsed > 65535)
50
44
  return defaultPort;
51
- }
52
45
  return parsed;
53
46
  }
54
47
  /**
@@ -132,23 +132,16 @@ export function validateSecurityOrThrow(requirements) {
132
132
  }
133
133
  const result = validateSecurity(requirements);
134
134
  if (!result.valid) {
135
- const errorLines = result.errors.map((e) => {
136
- let line = ` [${e.category}] ${e.key}: ${e.message}`;
137
- if (e.suggestion) {
138
- line += `\n Suggestion: ${e.suggestion}`;
135
+ const formatIssue = (issue) => {
136
+ let line = ` [${issue.category}] ${issue.key}: ${issue.message}`;
137
+ if (issue.suggestion) {
138
+ line += `\n Suggestion: ${issue.suggestion}`;
139
139
  }
140
140
  return line;
141
- });
142
- const warningLines = result.warnings.map((w) => {
143
- let line = ` [${w.category}] ${w.key}: ${w.message}`;
144
- if (w.suggestion) {
145
- line += `\n Suggestion: ${w.suggestion}`;
146
- }
147
- return line;
148
- });
149
- let message = `Production security validation failed:\n\n${errorLines.join('\n\n')}`;
150
- if (warningLines.length > 0) {
151
- message += `\n\nWarnings:\n${warningLines.join('\n\n')}`;
141
+ };
142
+ let message = `Production security validation failed:\n\n${result.errors.map(formatIssue).join('\n\n')}`;
143
+ if (result.warnings.length > 0) {
144
+ message += `\n\nWarnings:\n${result.warnings.map(formatIssue).join('\n\n')}`;
152
145
  }
153
146
  throw new Error(message);
154
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/velox",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Complete VeloxTS framework - batteries included",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,30 +39,30 @@
39
39
  "CHANGELOG.md"
40
40
  ],
41
41
  "dependencies": {
42
- "@veloxts/validation": "0.7.1",
43
- "@veloxts/core": "0.7.1",
44
- "@veloxts/router": "0.7.1",
45
- "@veloxts/auth": "0.7.1",
46
- "@veloxts/orm": "0.7.1"
42
+ "@veloxts/core": "0.7.3",
43
+ "@veloxts/validation": "0.7.3",
44
+ "@veloxts/auth": "0.7.3",
45
+ "@veloxts/router": "0.7.3",
46
+ "@veloxts/orm": "0.7.3"
47
47
  },
48
48
  "devDependencies": {
49
49
  "typescript": "5.9.3",
50
50
  "vitest": "4.0.18",
51
- "@veloxts/cache": "0.7.1",
52
- "@veloxts/events": "0.7.1",
53
- "@veloxts/scheduler": "0.7.1",
54
- "@veloxts/mail": "0.7.1",
55
- "@veloxts/storage": "0.7.1",
56
- "@veloxts/queue": "0.7.1"
51
+ "@veloxts/mail": "0.7.3",
52
+ "@veloxts/queue": "0.7.3",
53
+ "@veloxts/events": "0.7.3",
54
+ "@veloxts/cache": "0.7.3",
55
+ "@veloxts/storage": "0.7.3",
56
+ "@veloxts/scheduler": "0.7.3"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "zod": "^4.3.0",
60
- "@veloxts/mail": "0.7.1",
61
- "@veloxts/storage": "0.7.1",
62
- "@veloxts/queue": "0.7.1",
63
- "@veloxts/cache": "0.7.1",
64
- "@veloxts/scheduler": "0.7.1",
65
- "@veloxts/events": "0.7.1"
60
+ "@veloxts/queue": "0.7.3",
61
+ "@veloxts/cache": "0.7.3",
62
+ "@veloxts/mail": "0.7.3",
63
+ "@veloxts/scheduler": "0.7.3",
64
+ "@veloxts/events": "0.7.3",
65
+ "@veloxts/storage": "0.7.3"
66
66
  },
67
67
  "peerDependenciesMeta": {
68
68
  "@veloxts/cache": {