@veloxts/core 0.7.6 → 0.7.8

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,17 @@
1
1
  # @veloxts/core
2
2
 
3
+ ## 0.7.8
4
+
5
+ ### Patch Changes
6
+
7
+ - New feature: Domain Module. defineModule() implementation
8
+
9
+ ## 0.7.7
10
+
11
+ ### Patch Changes
12
+
13
+ - refactor(router): rename swaggerUIPlugin → swaggerPlugin, remove redundant exports
14
+
3
15
  ## 0.7.6
4
16
 
5
17
  ### Patch Changes
package/dist/app.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * @module app
5
5
  */
6
6
  import { type FastifyInstance, type FastifyPluginAsync } from 'fastify';
7
+ import type { ServiceDefinitions, VeloxModule } from './module/types.js';
7
8
  import type { PluginOptions, VeloxPlugin } from './plugin.js';
8
9
  import type { StaticOptions } from './plugins/static.js';
9
10
  import type { ShutdownHandler } from './types.js';
@@ -41,6 +42,9 @@ export declare class VeloxApp {
41
42
  private readonly _lifecycle;
42
43
  private _isRunning;
43
44
  private _address;
45
+ private readonly _bootHooks;
46
+ private readonly _moduleNames;
47
+ private readonly _serviceNames;
44
48
  /**
45
49
  * Creates a new VeloxApp instance
46
50
  *
@@ -122,6 +126,20 @@ export declare class VeloxApp {
122
126
  * ```
123
127
  */
124
128
  register<Options extends PluginOptions>(plugin: VeloxPlugin<Options> | FastifyPluginAsync<Options>, options?: Options): Promise<void>;
129
+ /**
130
+ * Registers a domain module with the application.
131
+ *
132
+ * Modules are self-contained vertical slices that bundle services,
133
+ * middleware, routes, and lifecycle hooks into a single unit.
134
+ *
135
+ * @param mod - VeloxModule created via defineModule()
136
+ * @returns The app instance for method chaining
137
+ * @throws {VeloxError} INVALID_MODULE - If module was not created via defineModule()
138
+ * @throws {VeloxError} DUPLICATE_MODULE - If a module with the same name is already registered
139
+ * @throws {VeloxError} SERVICE_NAME_COLLISION - If a service name conflicts with another module
140
+ * @throws {VeloxError} MODULE_REGISTRATION_TOO_LATE - If called after server has started
141
+ */
142
+ module<TName extends string, TServices extends ServiceDefinitions>(mod: VeloxModule<TName, TServices>): this;
125
143
  /**
126
144
  * Registers routes with the application
127
145
  *
package/dist/app.js CHANGED
@@ -7,6 +7,8 @@ import fastify from 'fastify';
7
7
  import fp from 'fastify-plugin';
8
8
  import { setupContextHook } from './context.js';
9
9
  import { ConflictError, isVeloxError, VeloxError } from './errors.js';
10
+ import { isVeloxModule } from './module/define-module.js';
11
+ import { createModulePlugin } from './module/register.js';
10
12
  import { isFastifyPlugin, isVeloxPlugin, validatePluginMetadata } from './plugin.js';
11
13
  import { requestLogger } from './plugins/request-logger.js';
12
14
  import { registerStatic } from './plugins/static.js';
@@ -41,6 +43,9 @@ export class VeloxApp {
41
43
  _lifecycle;
42
44
  _isRunning = false;
43
45
  _address = null;
46
+ _bootHooks = [];
47
+ _moduleNames = new Set();
48
+ _serviceNames = new Map();
44
49
  /**
45
50
  * Creates a new VeloxApp instance
46
51
  *
@@ -232,6 +237,71 @@ export class VeloxApp {
232
237
  }
233
238
  throw new VeloxError('Invalid plugin: must be a VeloxPlugin object or FastifyPluginAsync function', 500, 'INVALID_PLUGIN_TYPE');
234
239
  }
240
+ /**
241
+ * Registers a domain module with the application.
242
+ *
243
+ * Modules are self-contained vertical slices that bundle services,
244
+ * middleware, routes, and lifecycle hooks into a single unit.
245
+ *
246
+ * @param mod - VeloxModule created via defineModule()
247
+ * @returns The app instance for method chaining
248
+ * @throws {VeloxError} INVALID_MODULE - If module was not created via defineModule()
249
+ * @throws {VeloxError} DUPLICATE_MODULE - If a module with the same name is already registered
250
+ * @throws {VeloxError} SERVICE_NAME_COLLISION - If a service name conflicts with another module
251
+ * @throws {VeloxError} MODULE_REGISTRATION_TOO_LATE - If called after server has started
252
+ */
253
+ module(mod) {
254
+ if (this._isRunning) {
255
+ throw new VeloxError('Cannot register modules after server has started', 500, 'MODULE_REGISTRATION_TOO_LATE');
256
+ }
257
+ if (!isVeloxModule(mod)) {
258
+ throw new VeloxError('Invalid module: must be created via defineModule()', 500, 'INVALID_MODULE');
259
+ }
260
+ if (this._moduleNames.has(mod.name)) {
261
+ throw new VeloxError(`Module "${mod.name}" is already registered`, 500, 'DUPLICATE_MODULE');
262
+ }
263
+ // Check for service name collisions (all checks before any mutations)
264
+ const serviceNames = mod.config.services ? Object.keys(mod.config.services) : [];
265
+ for (const serviceName of serviceNames) {
266
+ const existingModule = this._serviceNames.get(serviceName);
267
+ if (existingModule) {
268
+ throw new VeloxError(`Service name "${serviceName}" in module "${mod.name}" conflicts with module "${existingModule}"`, 500, 'SERVICE_NAME_COLLISION');
269
+ }
270
+ }
271
+ // All checks passed — commit all mutations
272
+ this._moduleNames.add(mod.name);
273
+ for (const serviceName of serviceNames) {
274
+ this._serviceNames.set(serviceName, mod.name);
275
+ }
276
+ // Track resolved services for boot/shutdown callbacks
277
+ let resolvedServices;
278
+ const plugin = createModulePlugin(mod, (services) => {
279
+ resolvedServices = services;
280
+ });
281
+ // Register module as encapsulated Fastify plugin
282
+ this._server.register(plugin);
283
+ // Register boot hook (runs between ready() and listen())
284
+ if (mod.config.boot) {
285
+ const bootFn = mod.config.boot;
286
+ this._bootHooks.push(async () => {
287
+ if (resolvedServices) {
288
+ await bootFn(resolvedServices);
289
+ }
290
+ });
291
+ }
292
+ // Register shutdown hook
293
+ if (mod.config.shutdown) {
294
+ const shutdownFn = mod.config.shutdown;
295
+ this.beforeShutdown(async () => {
296
+ if (!resolvedServices) {
297
+ log.warn(`Module "${mod.name}" shutdown skipped: services were never initialized`);
298
+ return;
299
+ }
300
+ await shutdownFn(resolvedServices);
301
+ });
302
+ }
303
+ return this;
304
+ }
235
305
  /**
236
306
  * Registers routes with the application
237
307
  *
@@ -318,6 +388,10 @@ export class VeloxApp {
318
388
  const startTime = performance.now();
319
389
  try {
320
390
  await this._server.ready();
391
+ // Execute module boot hooks (after plugins loaded, before accepting traffic)
392
+ for (const bootHook of this._bootHooks) {
393
+ await bootHook();
394
+ }
321
395
  const address = await this._server.listen({
322
396
  port: this._config.port,
323
397
  host: this._config.host,
package/dist/errors.d.ts CHANGED
@@ -8,7 +8,7 @@ export { ERROR_CATALOG, ERROR_DOMAINS, type ErrorCatalogEntry, type ErrorCode, t
8
8
  * Known error codes in the VeloxTS framework core
9
9
  * Can be extended via declaration merging by plugins
10
10
  */
11
- export type VeloxCoreErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'CONFLICT' | 'FORBIDDEN' | 'UNAUTHORIZED' | 'SERVICE_UNAVAILABLE' | 'RATE_LIMITED' | 'UNPROCESSABLE' | 'CONFIGURATION_ERROR' | 'PLUGIN_REGISTRATION_ERROR' | 'SERVER_ALREADY_RUNNING' | 'SERVER_NOT_RUNNING' | 'SERVER_START_ERROR' | 'SERVER_STOP_ERROR' | 'INVALID_PLUGIN_METADATA';
11
+ export type VeloxCoreErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'CONFLICT' | 'FORBIDDEN' | 'UNAUTHORIZED' | 'SERVICE_UNAVAILABLE' | 'RATE_LIMITED' | 'UNPROCESSABLE' | 'CONFIGURATION_ERROR' | 'PLUGIN_REGISTRATION_ERROR' | 'SERVER_ALREADY_RUNNING' | 'SERVER_NOT_RUNNING' | 'SERVER_START_ERROR' | 'SERVER_STOP_ERROR' | 'INVALID_PLUGIN_METADATA' | 'INVALID_MODULE' | 'INVALID_MODULE_NAME' | 'DUPLICATE_MODULE' | 'SERVICE_NAME_COLLISION' | 'MODULE_REGISTRATION_TOO_LATE';
12
12
  /**
13
13
  * Registry for error codes - allows plugins to extend via declaration merging
14
14
  *
package/dist/index.d.ts CHANGED
@@ -39,3 +39,5 @@ export type { FireAndForgetOptions } from './utils/fire-and-forget.js';
39
39
  export { fireAndForget } from './utils/fire-and-forget.js';
40
40
  export type { RetryOptions } from './utils/retry.js';
41
41
  export { withRetry } from './utils/retry.js';
42
+ export type { InferModuleServices, InferServices, ModuleConfig, ModuleMiddleware, ServiceDefinition, ServiceDefinitions, VeloxModule, } from './module/index.js';
43
+ export { defineModule, isVeloxModule, MODULE_BRAND } from './module/index.js';
package/dist/index.js CHANGED
@@ -36,3 +36,4 @@ export { requestLogger } from './plugins/request-logger.js';
36
36
  export { createLogger } from './utils/logger.js';
37
37
  export { fireAndForget } from './utils/fire-and-forget.js';
38
38
  export { withRetry } from './utils/retry.js';
39
+ export { defineModule, isVeloxModule, MODULE_BRAND } from './module/index.js';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * defineModule() factory and isVeloxModule() type guard
3
+ *
4
+ * Creates self-contained vertical domain slices with services,
5
+ * middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module/define-module
8
+ */
9
+ import type { ModuleConfig, ServiceDefinitions, VeloxModule } from './types.js';
10
+ /**
11
+ * Defines a VeloxTS module — a self-contained vertical domain slice.
12
+ *
13
+ * Modules encapsulate services, middleware, routes, and lifecycle hooks
14
+ * for a specific domain (e.g., billing, analytics, notifications).
15
+ *
16
+ * @template TName - Literal string name of the module
17
+ * @template TServices - The service definitions provided by this module
18
+ * @param name - Unique module name (used as route prefix default)
19
+ * @param config - Module configuration
20
+ * @returns A frozen VeloxModule ready for registration
21
+ *
22
+ * @throws {VeloxError} If name is empty or whitespace-only
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * export const billingModule = defineModule('billing', {
27
+ * services: {
28
+ * stripe: {
29
+ * factory: () => new Stripe(process.env.STRIPE_KEY!),
30
+ * close: (s) => s.close(),
31
+ * },
32
+ * },
33
+ * routes: rest([billingProcedures]),
34
+ * boot: async ({ stripe }) => {
35
+ * await stripe.verifyConnection();
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function defineModule<const TName extends string, TServices extends ServiceDefinitions = ServiceDefinitions>(name: TName, config: ModuleConfig<TServices>): VeloxModule<TName, TServices>;
41
+ /**
42
+ * Type guard to check if a value is a VeloxModule.
43
+ *
44
+ * Uses the MODULE_BRAND symbol for reliable runtime identification,
45
+ * avoiding false positives from duck-typing.
46
+ *
47
+ * @param value - Value to check
48
+ * @returns true if value is a VeloxModule created by defineModule()
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * if (isVeloxModule(someValue)) {
53
+ * console.log('Module name:', someValue.name);
54
+ * await app.module(someValue);
55
+ * }
56
+ * ```
57
+ */
58
+ export declare function isVeloxModule(value: unknown): value is VeloxModule;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * defineModule() factory and isVeloxModule() type guard
3
+ *
4
+ * Creates self-contained vertical domain slices with services,
5
+ * middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module/define-module
8
+ */
9
+ import { VeloxError } from '../errors.js';
10
+ import { MODULE_BRAND } from './types.js';
11
+ /**
12
+ * Defines a VeloxTS module — a self-contained vertical domain slice.
13
+ *
14
+ * Modules encapsulate services, middleware, routes, and lifecycle hooks
15
+ * for a specific domain (e.g., billing, analytics, notifications).
16
+ *
17
+ * @template TName - Literal string name of the module
18
+ * @template TServices - The service definitions provided by this module
19
+ * @param name - Unique module name (used as route prefix default)
20
+ * @param config - Module configuration
21
+ * @returns A frozen VeloxModule ready for registration
22
+ *
23
+ * @throws {VeloxError} If name is empty or whitespace-only
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * export const billingModule = defineModule('billing', {
28
+ * services: {
29
+ * stripe: {
30
+ * factory: () => new Stripe(process.env.STRIPE_KEY!),
31
+ * close: (s) => s.close(),
32
+ * },
33
+ * },
34
+ * routes: rest([billingProcedures]),
35
+ * boot: async ({ stripe }) => {
36
+ * await stripe.verifyConnection();
37
+ * },
38
+ * });
39
+ * ```
40
+ */
41
+ export function defineModule(name, config) {
42
+ if (name.trim() === '') {
43
+ throw new VeloxError('Module must have a non-empty name', 500, 'INVALID_MODULE_NAME');
44
+ }
45
+ return {
46
+ [MODULE_BRAND]: true,
47
+ name,
48
+ config: Object.freeze({ ...config }),
49
+ };
50
+ }
51
+ /**
52
+ * Type guard to check if a value is a VeloxModule.
53
+ *
54
+ * Uses the MODULE_BRAND symbol for reliable runtime identification,
55
+ * avoiding false positives from duck-typing.
56
+ *
57
+ * @param value - Value to check
58
+ * @returns true if value is a VeloxModule created by defineModule()
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * if (isVeloxModule(someValue)) {
63
+ * console.log('Module name:', someValue.name);
64
+ * await app.module(someValue);
65
+ * }
66
+ * ```
67
+ */
68
+ export function isVeloxModule(value) {
69
+ if (typeof value !== 'object' || value === null) {
70
+ return false;
71
+ }
72
+ return MODULE_BRAND in value && value[MODULE_BRAND] === true;
73
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Module system for VeloxTS framework
3
+ *
4
+ * Provides defineModule() for creating self-contained vertical domain slices
5
+ * with services, middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module
8
+ */
9
+ export { defineModule, isVeloxModule } from './define-module.js';
10
+ export type { InferModuleServices, InferServices, ModuleConfig, ModuleMiddleware, ServiceDefinition, ServiceDefinitions, VeloxModule, } from './types.js';
11
+ export { MODULE_BRAND } from './types.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Module system for VeloxTS framework
3
+ *
4
+ * Provides defineModule() for creating self-contained vertical domain slices
5
+ * with services, middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module
8
+ */
9
+ export { defineModule, isVeloxModule } from './define-module.js';
10
+ export { MODULE_BRAND } from './types.js';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * createModulePlugin() — creates a Fastify plugin from a VeloxModule definition.
3
+ *
4
+ * The plugin creates an encapsulated scope that:
5
+ * 1. Creates service instances via factories (sync or async)
6
+ * 2. Injects services into every request via decorateRequest + onRequest hook
7
+ * 3. Applies module middleware as onRequest hooks
8
+ * 4. Registers routes with the module prefix (auto from name, custom, or disabled)
9
+ * 5. Adds onClose hooks for service cleanup
10
+ *
11
+ * @module module/register
12
+ */
13
+ import type { FastifyInstance } from 'fastify';
14
+ import type { InferServices, ServiceDefinitions, VeloxModule } from './types.js';
15
+ /**
16
+ * Creates a Fastify plugin from a VeloxModule definition.
17
+ *
18
+ * @param mod - The module definition
19
+ * @param onServicesResolved - Optional callback invoked after all services are created,
20
+ * during plugin registration (before server.ready()). Used by app.module() to capture
21
+ * resolved service references for boot/shutdown hooks.
22
+ * @returns A Fastify plugin function
23
+ *
24
+ * @internal Used by app.module() — not typically called directly.
25
+ */
26
+ export declare function createModulePlugin<TName extends string, TServices extends ServiceDefinitions>(mod: VeloxModule<TName, TServices>, onServicesResolved?: (services: InferServices<TServices>) => void): (server: FastifyInstance) => Promise<void>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * createModulePlugin() — creates a Fastify plugin from a VeloxModule definition.
3
+ *
4
+ * The plugin creates an encapsulated scope that:
5
+ * 1. Creates service instances via factories (sync or async)
6
+ * 2. Injects services into every request via decorateRequest + onRequest hook
7
+ * 3. Applies module middleware as onRequest hooks
8
+ * 4. Registers routes with the module prefix (auto from name, custom, or disabled)
9
+ * 5. Adds onClose hooks for service cleanup
10
+ *
11
+ * @module module/register
12
+ */
13
+ import { createLogger } from '../utils/logger.js';
14
+ const log = createLogger('module');
15
+ /**
16
+ * Creates a Fastify plugin from a VeloxModule definition.
17
+ *
18
+ * @param mod - The module definition
19
+ * @param onServicesResolved - Optional callback invoked after all services are created,
20
+ * during plugin registration (before server.ready()). Used by app.module() to capture
21
+ * resolved service references for boot/shutdown hooks.
22
+ * @returns A Fastify plugin function
23
+ *
24
+ * @internal Used by app.module() — not typically called directly.
25
+ */
26
+ export function createModulePlugin(mod, onServicesResolved) {
27
+ const { config } = mod;
28
+ return async (server) => {
29
+ const services = {};
30
+ if (config.services) {
31
+ for (const [key, def] of Object.entries(config.services)) {
32
+ const service = await def.factory();
33
+ services[key] = service;
34
+ server.decorateRequest(key, undefined);
35
+ server.addHook('onRequest', async (request) => {
36
+ request[key] = service;
37
+ });
38
+ // Cleanup on close
39
+ if (def.close) {
40
+ addCloseHook(server, service, def.close);
41
+ }
42
+ }
43
+ }
44
+ // Notify caller of resolved services (for boot/shutdown)
45
+ if (onServicesResolved) {
46
+ onServicesResolved(services);
47
+ }
48
+ // Apply module middleware
49
+ if (config.middleware) {
50
+ for (const mw of config.middleware) {
51
+ server.addHook('onRequest', mw);
52
+ }
53
+ }
54
+ // Register routes with prefix
55
+ if (config.routes) {
56
+ const prefix = resolvePrefix(mod.name, config.prefix);
57
+ if (prefix) {
58
+ await server.register(config.routes, { prefix });
59
+ }
60
+ else {
61
+ await server.register(config.routes);
62
+ }
63
+ }
64
+ log.debug(`Module "${mod.name}" registered`);
65
+ };
66
+ }
67
+ /**
68
+ * Registers an onClose hook with properly correlated types.
69
+ * Object.entries() erases per-key generics, so we use a helper
70
+ * to preserve the relationship between the service and its close function.
71
+ */
72
+ function addCloseHook(server, service, closeFn) {
73
+ server.addHook('onClose', async () => {
74
+ await closeFn(service);
75
+ });
76
+ }
77
+ /**
78
+ * Resolve the effective route prefix for a module.
79
+ *
80
+ * - undefined -> /${name} (auto from module name)
81
+ * - false -> no prefix
82
+ * - string -> custom prefix (ensures leading slash)
83
+ */
84
+ function resolvePrefix(name, prefix) {
85
+ if (prefix === false) {
86
+ return undefined;
87
+ }
88
+ if (typeof prefix === 'string') {
89
+ return prefix.startsWith('/') ? prefix : `/${prefix}`;
90
+ }
91
+ return `/${name}`;
92
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Module system type definitions for VeloxTS framework
3
+ *
4
+ * Defines the shape of a VeloxTS module — a self-contained vertical domain slice
5
+ * with services, middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module/types
8
+ */
9
+ import type { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify';
10
+ /**
11
+ * Service definition within a module.
12
+ * Each service has a factory and optional cleanup.
13
+ */
14
+ export interface ServiceDefinition<T = unknown> {
15
+ /** Factory function to create the service instance */
16
+ factory: () => T | Promise<T>;
17
+ /** Optional cleanup called on server close */
18
+ close?: (service: T) => void | Promise<void>;
19
+ }
20
+ /**
21
+ * Record of named service definitions
22
+ */
23
+ export type ServiceDefinitions = Record<string, ServiceDefinition>;
24
+ /**
25
+ * Infer resolved service instances from their definitions.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const services = {
30
+ * db: { factory: () => new Database() },
31
+ * cache: { factory: () => new Cache() },
32
+ * } satisfies ServiceDefinitions;
33
+ *
34
+ * type Resolved = InferServices<typeof services>;
35
+ * // { db: Database; cache: Cache }
36
+ * ```
37
+ */
38
+ export type InferServices<T extends ServiceDefinitions> = {
39
+ [K in keyof T]: T[K] extends ServiceDefinition<infer S> ? S : never;
40
+ };
41
+ /**
42
+ * Module middleware — a Fastify onRequest hook applied to all routes in the module scope.
43
+ */
44
+ export type ModuleMiddleware = (request: FastifyRequest, reply: FastifyReply) => void | Promise<void>;
45
+ /**
46
+ * Configuration for defineModule().
47
+ *
48
+ * @template TServices - The service definitions provided by this module
49
+ */
50
+ export interface ModuleConfig<TServices extends ServiceDefinitions = ServiceDefinitions> {
51
+ /** Services this module provides (created once, injected per-request) */
52
+ services?: TServices;
53
+ /** Module-wide middleware applied to all routes in the module scope */
54
+ middleware?: ModuleMiddleware[];
55
+ /**
56
+ * Fastify plugin for route registration.
57
+ * Typically the result of rest([...]) from @veloxts/router.
58
+ */
59
+ routes?: FastifyPluginAsync;
60
+ /**
61
+ * REST route prefix. Defaults to /${moduleName}.
62
+ * Set false to disable auto-prefix.
63
+ * Set a string for a custom prefix.
64
+ */
65
+ prefix?: string | false;
66
+ /** Called after all modules registered, before server starts listening */
67
+ boot?: (services: InferServices<TServices>) => void | Promise<void>;
68
+ /** Called during graceful shutdown */
69
+ shutdown?: (services: InferServices<TServices>) => void | Promise<void>;
70
+ }
71
+ /** Brand symbol for VeloxModule type guard */
72
+ export declare const MODULE_BRAND: unique symbol;
73
+ /**
74
+ * A VeloxTS module — the return type of defineModule().
75
+ *
76
+ * Branded with a unique symbol for reliable runtime type checking
77
+ * via isVeloxModule().
78
+ *
79
+ * @template TName - Literal string name of the module
80
+ * @template TServices - The service definitions provided by this module
81
+ */
82
+ export interface VeloxModule<TName extends string = string, TServices extends ServiceDefinitions = ServiceDefinitions> {
83
+ readonly [MODULE_BRAND]: true;
84
+ readonly name: TName;
85
+ readonly config: Readonly<ModuleConfig<TServices>>;
86
+ }
87
+ /**
88
+ * Infer the services type from a VeloxModule instance.
89
+ * Useful for extending BaseContext via declaration merging.
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const billing = defineModule('billing', {
94
+ * services: {
95
+ * stripe: { factory: () => new Stripe(process.env.STRIPE_KEY!) },
96
+ * },
97
+ * });
98
+ *
99
+ * type BillingServices = InferModuleServices<typeof billing>;
100
+ * // { stripe: Stripe }
101
+ * ```
102
+ */
103
+ export type InferModuleServices<M> = M extends VeloxModule<string, infer S> ? InferServices<S> : never;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Module system type definitions for VeloxTS framework
3
+ *
4
+ * Defines the shape of a VeloxTS module — a self-contained vertical domain slice
5
+ * with services, middleware, routes, and lifecycle hooks.
6
+ *
7
+ * @module module/types
8
+ */
9
+ /** Brand symbol for VeloxModule type guard */
10
+ export const MODULE_BRAND = Symbol.for('velox:module');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/core",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "description": "Fastify wrapper and plugin system for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",