@veloxts/core 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 VeloxTS Framework Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,573 @@
1
+ # @veloxts/core
2
+
3
+ Foundation package for the VeloxTS framework - provides the core Fastify wrapper, plugin system, and base context.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @veloxts/core
9
+ # or
10
+ pnpm add @veloxts/core
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createVeloxApp } from '@veloxts/core';
17
+
18
+ // Create application
19
+ const app = await createVeloxApp({
20
+ port: 3000,
21
+ host: '0.0.0.0',
22
+ logger: true,
23
+ });
24
+
25
+ // Start server
26
+ await app.start();
27
+ console.log(`Server running on ${app.address}`);
28
+ ```
29
+
30
+ ## Core API
31
+
32
+ ### `createVeloxApp(config?)`
33
+
34
+ Creates a new VeloxTS application instance with sensible defaults.
35
+
36
+ **Configuration Options:**
37
+
38
+ ```typescript
39
+ interface VeloxAppConfig {
40
+ port?: number; // Port to listen on (default: 3000)
41
+ host?: string; // Host to bind to (default: '0.0.0.0')
42
+ logger?: boolean | object; // Enable logging (default: true in dev)
43
+ fastify?: FastifyOptions; // Additional Fastify options
44
+ }
45
+ ```
46
+
47
+ **Example:**
48
+
49
+ ```typescript
50
+ const app = await createVeloxApp({
51
+ port: 4000,
52
+ host: '127.0.0.1',
53
+ logger: {
54
+ level: 'info',
55
+ prettyPrint: true,
56
+ },
57
+ fastify: {
58
+ requestTimeout: 30000,
59
+ bodyLimit: 1048576 * 10, // 10MB
60
+ },
61
+ });
62
+ ```
63
+
64
+ **Returns:** Promise resolving to `VeloxApp` instance
65
+
66
+ ### VeloxApp Instance
67
+
68
+ The application instance provides methods for lifecycle management:
69
+
70
+ ```typescript
71
+ interface VeloxApp {
72
+ server: FastifyInstance; // Underlying Fastify server
73
+ config: VeloxAppConfig; // Application configuration
74
+ isRunning: boolean; // Server running state
75
+ address: string | null; // Server address if running
76
+
77
+ // Methods
78
+ start(): Promise<void>; // Start the server
79
+ stop(): Promise<void>; // Stop the server gracefully
80
+ register(plugin, options?): Promise<void>; // Register a plugin
81
+ onShutdown(handler): void; // Add shutdown handler
82
+ }
83
+ ```
84
+
85
+ **Example:**
86
+
87
+ ```typescript
88
+ const app = await createVeloxApp();
89
+
90
+ // Register plugins
91
+ await app.register(databasePlugin);
92
+ await app.register(routerPlugin);
93
+
94
+ // Add shutdown hook
95
+ app.onShutdown(async () => {
96
+ console.log('Cleaning up resources...');
97
+ });
98
+
99
+ // Start server
100
+ await app.start();
101
+ console.log(`Server running at ${app.address}`);
102
+
103
+ // Graceful shutdown
104
+ process.on('SIGTERM', async () => {
105
+ await app.stop();
106
+ });
107
+ ```
108
+
109
+ ## Plugin System
110
+
111
+ VeloxTS's plugin system extends functionality while maintaining type safety and encapsulation.
112
+
113
+ ### Defining Plugins
114
+
115
+ Use `definePlugin()` to create reusable plugins:
116
+
117
+ ```typescript
118
+ import { definePlugin } from '@veloxts/core';
119
+ import { PrismaClient } from '@prisma/client';
120
+
121
+ export const databasePlugin = definePlugin({
122
+ name: '@myapp/database',
123
+ version: '1.0.0',
124
+ async register(server, options) {
125
+ const db = new PrismaClient();
126
+
127
+ // Decorate server with database client
128
+ server.decorate('db', db);
129
+
130
+ // Add lifecycle hook
131
+ server.addHook('onClose', async () => {
132
+ await db.$disconnect();
133
+ });
134
+ },
135
+ });
136
+ ```
137
+
138
+ ### Extending Context
139
+
140
+ Use TypeScript declaration merging to extend the base context:
141
+
142
+ ```typescript
143
+ import type { PrismaClient } from '@prisma/client';
144
+
145
+ declare module '@veloxts/core' {
146
+ interface BaseContext {
147
+ db: PrismaClient;
148
+ }
149
+ }
150
+ ```
151
+
152
+ Now `ctx.db` is available in all route handlers with full type safety.
153
+
154
+ ### Registering Plugins
155
+
156
+ ```typescript
157
+ // Register with app instance
158
+ await app.register(databasePlugin);
159
+
160
+ // Register with options
161
+ await app.register(databasePlugin, {
162
+ connectionString: process.env.DATABASE_URL,
163
+ });
164
+ ```
165
+
166
+ ### Plugin Example: Database Integration
167
+
168
+ Complete example integrating Prisma:
169
+
170
+ ```typescript
171
+ import { definePlugin, type BaseContext } from '@veloxts/core';
172
+ import { PrismaClient } from '@prisma/client';
173
+
174
+ // Define plugin
175
+ export const databasePlugin = definePlugin({
176
+ name: '@myapp/database',
177
+ version: '1.0.0',
178
+ async register(server, options) {
179
+ const prisma = new PrismaClient({
180
+ log: options?.log || ['error'],
181
+ });
182
+
183
+ // Test connection
184
+ await prisma.$connect();
185
+
186
+ // Decorate Fastify instance
187
+ server.decorate('db', prisma);
188
+
189
+ // Graceful shutdown
190
+ server.addHook('onClose', async () => {
191
+ await prisma.$disconnect();
192
+ });
193
+
194
+ console.log('Database connected');
195
+ },
196
+ });
197
+
198
+ // Extend context
199
+ declare module '@veloxts/core' {
200
+ interface BaseContext {
201
+ db: PrismaClient;
202
+ }
203
+ }
204
+
205
+ // Use in app
206
+ const app = await createVeloxApp();
207
+ await app.register(databasePlugin);
208
+ ```
209
+
210
+ ## Context System
211
+
212
+ The context object provides request-scoped state accessible throughout the request lifecycle.
213
+
214
+ ### Base Context
215
+
216
+ Every request has a base context:
217
+
218
+ ```typescript
219
+ interface BaseContext {
220
+ request: FastifyRequest; // Fastify request object
221
+ reply: FastifyReply; // Fastify reply object
222
+ }
223
+ ```
224
+
225
+ ### Accessing Context
226
+
227
+ Context is available in route handlers:
228
+
229
+ ```typescript
230
+ app.server.get('/users', async (request, reply) => {
231
+ // Access base context
232
+ const { request: req, reply: rep } = request.context;
233
+
234
+ // Access plugin-extended properties
235
+ const users = await request.context.db.user.findMany();
236
+
237
+ return users;
238
+ });
239
+ ```
240
+
241
+ ### Context in Procedures
242
+
243
+ When using `@veloxts/router`, context is passed to procedure handlers:
244
+
245
+ ```typescript
246
+ import { procedure } from '@veloxts/router';
247
+
248
+ const getUsers = procedure()
249
+ .query(async ({ ctx }) => {
250
+ // ctx has type BaseContext with extensions
251
+ return ctx.db.user.findMany();
252
+ });
253
+ ```
254
+
255
+ ## Error Handling
256
+
257
+ VeloxTS provides structured error classes for consistent API responses.
258
+
259
+ ### Error Classes
260
+
261
+ ```typescript
262
+ import {
263
+ VeloxError,
264
+ ValidationError,
265
+ NotFoundError,
266
+ UnauthorizedError,
267
+ ForbiddenError,
268
+ } from '@veloxts/core';
269
+ ```
270
+
271
+ ### `VeloxError`
272
+
273
+ Base error class with status code:
274
+
275
+ ```typescript
276
+ throw new VeloxError('Something went wrong', 500);
277
+
278
+ // Response:
279
+ // {
280
+ // "error": "Something went wrong",
281
+ // "statusCode": 500
282
+ // }
283
+ ```
284
+
285
+ ### `ValidationError`
286
+
287
+ Validation errors with field-level details:
288
+
289
+ ```typescript
290
+ throw new ValidationError('Invalid input', {
291
+ email: 'Must be a valid email address',
292
+ age: 'Must be at least 18',
293
+ });
294
+
295
+ // Response:
296
+ // {
297
+ // "error": "Invalid input",
298
+ // "statusCode": 400,
299
+ // "fields": {
300
+ // "email": "Must be a valid email address",
301
+ // "age": "Must be at least 18"
302
+ // }
303
+ // }
304
+ ```
305
+
306
+ ### `NotFoundError`
307
+
308
+ Resource not found errors:
309
+
310
+ ```typescript
311
+ throw new NotFoundError('User', userId);
312
+
313
+ // Response:
314
+ // {
315
+ // "error": "User not found",
316
+ // "statusCode": 404,
317
+ // "resource": "User",
318
+ // "id": "123"
319
+ // }
320
+ ```
321
+
322
+ ### `UnauthorizedError`
323
+
324
+ Authentication required:
325
+
326
+ ```typescript
327
+ throw new UnauthorizedError('Invalid credentials');
328
+
329
+ // Response:
330
+ // {
331
+ // "error": "Invalid credentials",
332
+ // "statusCode": 401
333
+ // }
334
+ ```
335
+
336
+ ### `ForbiddenError`
337
+
338
+ Insufficient permissions:
339
+
340
+ ```typescript
341
+ throw new ForbiddenError('Insufficient permissions');
342
+
343
+ // Response:
344
+ // {
345
+ // "error": "Insufficient permissions",
346
+ // "statusCode": 403
347
+ // }
348
+ ```
349
+
350
+ ### Error Handler Example
351
+
352
+ ```typescript
353
+ import { VeloxError, NotFoundError } from '@veloxts/core';
354
+
355
+ app.server.get('/users/:id', async (request, reply) => {
356
+ try {
357
+ const user = await request.context.db.user.findUnique({
358
+ where: { id: request.params.id },
359
+ });
360
+
361
+ if (!user) {
362
+ throw new NotFoundError('User', request.params.id);
363
+ }
364
+
365
+ return user;
366
+ } catch (error) {
367
+ if (error instanceof VeloxError) {
368
+ // VeloxError is automatically handled by Fastify
369
+ throw error;
370
+ }
371
+
372
+ // Handle unexpected errors
373
+ throw new VeloxError('Internal server error', 500);
374
+ }
375
+ });
376
+ ```
377
+
378
+ ## Configuration
379
+
380
+ ### Default Configuration
381
+
382
+ ```typescript
383
+ {
384
+ port: 3000,
385
+ host: '0.0.0.0',
386
+ logger: process.env.NODE_ENV !== 'production',
387
+ }
388
+ ```
389
+
390
+ ### Environment-Based Configuration
391
+
392
+ ```typescript
393
+ const app = await createVeloxApp({
394
+ port: Number(process.env.PORT) || 3000,
395
+ host: process.env.HOST || '0.0.0.0',
396
+ logger: process.env.NODE_ENV !== 'production',
397
+ fastify: {
398
+ trustProxy: process.env.TRUST_PROXY === 'true',
399
+ },
400
+ });
401
+ ```
402
+
403
+ ### Production Configuration
404
+
405
+ ```typescript
406
+ const app = await createVeloxApp({
407
+ port: 3000,
408
+ host: '0.0.0.0',
409
+ logger: {
410
+ level: 'warn',
411
+ prettyPrint: false,
412
+ },
413
+ fastify: {
414
+ trustProxy: true,
415
+ requestTimeout: 30000,
416
+ bodyLimit: 1048576, // 1MB
417
+ disableRequestLogging: true,
418
+ },
419
+ });
420
+ ```
421
+
422
+ ## Lifecycle Management
423
+
424
+ ### Graceful Shutdown
425
+
426
+ ```typescript
427
+ const app = await createVeloxApp();
428
+
429
+ // Add custom shutdown handlers
430
+ app.onShutdown(async () => {
431
+ console.log('Cleaning up resources...');
432
+ await database.disconnect();
433
+ await cache.flush();
434
+ });
435
+
436
+ // Handle SIGTERM (e.g., from Kubernetes, Docker)
437
+ process.on('SIGTERM', async () => {
438
+ console.log('SIGTERM received, shutting down gracefully...');
439
+ await app.stop();
440
+ process.exit(0);
441
+ });
442
+
443
+ // Handle SIGINT (Ctrl+C)
444
+ process.on('SIGINT', async () => {
445
+ console.log('SIGINT received, shutting down gracefully...');
446
+ await app.stop();
447
+ process.exit(0);
448
+ });
449
+
450
+ await app.start();
451
+ ```
452
+
453
+ ### Startup and Shutdown Hooks
454
+
455
+ Plugins can register lifecycle hooks:
456
+
457
+ ```typescript
458
+ export const lifecyclePlugin = definePlugin({
459
+ name: 'lifecycle',
460
+ version: '1.0.0',
461
+ async register(server, options) {
462
+ // Called when server starts listening
463
+ server.addHook('onListen', async () => {
464
+ console.log('Server is listening');
465
+ });
466
+
467
+ // Called when server is closing
468
+ server.addHook('onClose', async () => {
469
+ console.log('Server is closing');
470
+ });
471
+
472
+ // Called for each request
473
+ server.addHook('onRequest', async (request, reply) => {
474
+ console.log(`Request: ${request.method} ${request.url}`);
475
+ });
476
+ },
477
+ });
478
+ ```
479
+
480
+ ## Advanced Usage
481
+
482
+ ### Custom Fastify Instance
483
+
484
+ For advanced scenarios, you can pass a custom Fastify instance:
485
+
486
+ ```typescript
487
+ import Fastify from 'fastify';
488
+
489
+ const fastify = Fastify({
490
+ logger: true,
491
+ requestIdHeader: 'x-request-id',
492
+ trustProxy: true,
493
+ });
494
+
495
+ const app = await createVeloxApp({ fastify });
496
+ ```
497
+
498
+ ### Accessing Fastify Directly
499
+
500
+ The underlying Fastify instance is available via `app.server`:
501
+
502
+ ```typescript
503
+ const app = await createVeloxApp();
504
+
505
+ // Add Fastify plugins
506
+ await app.server.register(fastifyCors, {
507
+ origin: true,
508
+ });
509
+
510
+ // Add raw routes
511
+ app.server.get('/custom', async (request, reply) => {
512
+ return { message: 'Custom route' };
513
+ });
514
+ ```
515
+
516
+ ## Practical Examples
517
+
518
+ ### Complete Application Setup
519
+
520
+ ```typescript
521
+ import { createVeloxApp } from '@veloxts/core';
522
+ import { createDatabasePlugin } from '@veloxts/orm';
523
+ import { registerRestRoutes } from '@veloxts/router';
524
+ import { PrismaClient } from '@prisma/client';
525
+ import { userProcedures } from './procedures/users';
526
+
527
+ // Initialize
528
+ const prisma = new PrismaClient();
529
+ const app = await createVeloxApp({
530
+ port: Number(process.env.PORT) || 3000,
531
+ logger: true,
532
+ });
533
+
534
+ // Register database plugin
535
+ await app.register(createDatabasePlugin({ client: prisma }));
536
+
537
+ // Register API routes
538
+ await registerRestRoutes(app.server, {
539
+ prefix: '/api',
540
+ procedures: {
541
+ users: userProcedures,
542
+ },
543
+ });
544
+
545
+ // Graceful shutdown
546
+ const shutdown = async () => {
547
+ await app.stop();
548
+ process.exit(0);
549
+ };
550
+
551
+ process.on('SIGTERM', shutdown);
552
+ process.on('SIGINT', shutdown);
553
+
554
+ // Start server
555
+ await app.start();
556
+ console.log(`Server running at ${app.address}`);
557
+ ```
558
+
559
+ ## Related Packages
560
+
561
+ - [@veloxts/router](/packages/router) - Procedure-based routing
562
+ - [@veloxts/validation](/packages/validation) - Schema validation with Zod
563
+ - [@veloxts/orm](/packages/orm) - Prisma integration
564
+ - [@veloxts/client](/packages/client) - Type-safe frontend API client
565
+ - [@veloxts/cli](/packages/cli) - Developer tooling
566
+
567
+ ## TypeScript Support
568
+
569
+ All exports are fully typed with comprehensive JSDoc documentation. The package includes type definitions and declaration maps for excellent IDE support.
570
+
571
+ ## License
572
+
573
+ MIT