@naman_deep_singh/server-utils 1.0.0 → 1.0.1

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/dist/server.js CHANGED
@@ -3,41 +3,197 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ExpressServer = void 0;
6
7
  exports.createServer = createServer;
7
- exports.withPlugin = withPlugin;
8
- exports.createServerWithPlugins = createServerWithPlugins;
9
8
  const express_1 = __importDefault(require("express"));
10
- const cors_1 = __importDefault(require("cors"));
11
- const helmet_1 = __importDefault(require("helmet"));
12
- function createServer(config = {}) {
13
- const app = (0, express_1.default)();
14
- // Apply default middleware
15
- if (config.helmet !== false) {
16
- app.use((0, helmet_1.default)());
17
- }
18
- if (config.cors !== false) {
19
- const corsOptions = typeof config.cors === 'object' ? config.cors : undefined;
20
- app.use((0, cors_1.default)(corsOptions));
21
- }
22
- if (config.json !== false) {
23
- app.use(express_1.default.json());
24
- }
25
- // Apply custom middleware
26
- if (config.customMiddleware) {
27
- config.customMiddleware.forEach(middleware => {
28
- app.use(middleware);
9
+ const shutdown_1 = require("./shutdown");
10
+ const crypto_1 = __importDefault(require("crypto"));
11
+ class ExpressServer {
12
+ constructor(name = 'Express Server', version = '1.0.0', config = {}) {
13
+ this.status = 'stopped';
14
+ this.grpcServices = [];
15
+ this.rpcMethods = {};
16
+ this.app = (0, express_1.default)();
17
+ this.config = {
18
+ name,
19
+ version,
20
+ startTime: new Date(),
21
+ port: config.port || 3000,
22
+ cors: config.cors ?? true,
23
+ helmet: config.helmet ?? true,
24
+ json: config.json ?? true,
25
+ customMiddleware: config.customMiddleware || [],
26
+ healthCheck: config.healthCheck ?? true,
27
+ gracefulShutdown: config.gracefulShutdown ?? true,
28
+ socketIO: config.socketIO
29
+ };
30
+ }
31
+ async start() {
32
+ this.status = 'starting';
33
+ return new Promise((resolve, reject) => {
34
+ try {
35
+ this.server = this.app.listen(this.config.port, () => {
36
+ this.status = 'running';
37
+ console.log(`🚀 ${this.config.name} v${this.config.version} running on http://localhost:${this.config.port}`);
38
+ if (this.config.gracefulShutdown) {
39
+ (0, shutdown_1.createGracefulShutdown)(this.server, {
40
+ onShutdown: async () => {
41
+ this.status = 'stopping';
42
+ }
43
+ });
44
+ }
45
+ resolve(this);
46
+ });
47
+ this.server.on('error', reject);
48
+ }
49
+ catch (error) {
50
+ this.status = 'stopped';
51
+ reject(error);
52
+ }
29
53
  });
30
54
  }
31
- return app;
32
- }
33
- function withPlugin(app, plugin, config = {}) {
34
- plugin(app, config);
35
- return app;
55
+ async stop() {
56
+ this.status = 'stopping';
57
+ // Stop gRPC server if running
58
+ if (this.grpcServer) {
59
+ this.grpcServer.forceShutdown();
60
+ }
61
+ // Stop Socket.IO server if running
62
+ if (this.socketIO) {
63
+ this.socketIO.close();
64
+ }
65
+ if (!this.server) {
66
+ this.status = 'stopped';
67
+ return;
68
+ }
69
+ return new Promise((resolve) => {
70
+ this.server.close(() => {
71
+ this.status = 'stopped';
72
+ console.log(`👋 ${this.config.name} stopped`);
73
+ resolve();
74
+ });
75
+ });
76
+ }
77
+ getInfo() {
78
+ return {
79
+ name: this.config.name,
80
+ version: this.config.version,
81
+ port: this.config.port,
82
+ uptime: Date.now() - this.config.startTime.getTime(),
83
+ status: this.status,
84
+ startTime: this.config.startTime
85
+ };
86
+ }
87
+ addGrpcService(service, implementation, port = 50051) {
88
+ this.grpcServices.push({ service, implementation });
89
+ // Lazy load gRPC to avoid dependency issues
90
+ if (!this.grpcServer) {
91
+ try {
92
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
93
+ const grpc = require('@grpc/grpc-js');
94
+ this.grpcServer = new grpc.Server();
95
+ // Add all services
96
+ this.grpcServices.forEach(({ service, implementation }) => {
97
+ this.grpcServer.addService(service, implementation);
98
+ });
99
+ this.grpcServer.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), () => {
100
+ this.grpcServer.start();
101
+ console.log(`🔗 gRPC server running on port ${port}`);
102
+ });
103
+ }
104
+ catch (error) {
105
+ console.warn('gRPC not available. Install @grpc/grpc-js to use gRPC features.');
106
+ }
107
+ }
108
+ }
109
+ addRpcMethods(methods, path = '/rpc') {
110
+ Object.assign(this.rpcMethods, methods);
111
+ try {
112
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
113
+ const jayson = require('jayson');
114
+ const rpcServer = jayson.server(this.rpcMethods);
115
+ this.app.use(path, rpcServer.middleware());
116
+ console.log(`📡 JSON-RPC server mounted on ${path}`);
117
+ }
118
+ catch (error) {
119
+ console.warn('JSON-RPC not available. Install jayson to use RPC features.');
120
+ }
121
+ }
122
+ addWebhook(config) {
123
+ this.app.post(config.path, express_1.default.raw({ type: 'application/json' }), async (req, res) => {
124
+ try {
125
+ // Verify signature if secret provided
126
+ if (config.secret) {
127
+ const signature = req.headers['x-hub-signature-256'] || req.headers['x-signature-256'];
128
+ if (signature) {
129
+ const expectedSignature = crypto_1.default
130
+ .createHmac('sha256', config.secret)
131
+ .update(req.body)
132
+ .digest('hex');
133
+ const providedSignature = Array.isArray(signature) ? signature[0] : signature;
134
+ if (!providedSignature.includes(expectedSignature)) {
135
+ return res.status(401).json({ error: 'Invalid signature' });
136
+ }
137
+ }
138
+ }
139
+ // Parse JSON payload
140
+ const payload = JSON.parse(req.body.toString());
141
+ // Call handler
142
+ await config.handler(payload, req.headers);
143
+ res.status(200).json({ success: true });
144
+ }
145
+ catch (error) {
146
+ console.error('Webhook error:', error);
147
+ res.status(500).json({ error: 'Webhook processing failed' });
148
+ }
149
+ });
150
+ console.log(`🪝 Webhook registered at ${config.path}`);
151
+ }
152
+ addSocketIO(config = {}) {
153
+ if (!this.server) {
154
+ throw new Error('Server must be started before adding Socket.IO');
155
+ }
156
+ try {
157
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
158
+ const { Server } = require('socket.io');
159
+ // Configure CORS
160
+ const corsConfig = config.cors === true
161
+ ? { origin: '*', methods: ['GET', 'POST'] }
162
+ : config.cors || undefined;
163
+ // Create Socket.IO server
164
+ const io = new Server(this.server, {
165
+ cors: config.cors ? corsConfig : undefined,
166
+ path: config.path || '/socket.io'
167
+ });
168
+ // Store reference for cleanup
169
+ this.socketIO = io;
170
+ // Handle connections
171
+ io.on('connection', (socket) => {
172
+ const typedSocket = socket;
173
+ console.log(`🔌 Socket connected: ${typedSocket.id}`);
174
+ // Call user-defined connection handler
175
+ if (config.onConnection) {
176
+ config.onConnection(socket);
177
+ }
178
+ // Handle disconnection
179
+ typedSocket.on('disconnect', (reason) => {
180
+ console.log(`🔌 Socket disconnected: ${typedSocket.id} - ${reason}`);
181
+ // Call user-defined disconnection handler
182
+ if (config.onDisconnection) {
183
+ config.onDisconnection(socket, reason);
184
+ }
185
+ });
186
+ });
187
+ console.log(`🔌 Socket.IO server attached${config.path ? ` at ${config.path}` : ''}`);
188
+ return io;
189
+ }
190
+ catch (error) {
191
+ console.warn('Socket.IO not available. Install socket.io to use WebSocket features.');
192
+ return null;
193
+ }
194
+ }
36
195
  }
37
- function createServerWithPlugins(config = {}, ...plugins) {
38
- const app = createServer(config);
39
- plugins.forEach(plugin => {
40
- plugin(app, config);
41
- });
42
- return app;
196
+ exports.ExpressServer = ExpressServer;
197
+ function createServer(name, version, config) {
198
+ return new ExpressServer(name, version, config);
43
199
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import express from 'express';
2
- import { CorsOptions } from 'cors';
2
+ export interface CorsOptions {
3
+ origin?: string | string[] | boolean | RegExp | ((origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void);
4
+ methods?: string | string[];
5
+ allowedHeaders?: string | string[];
6
+ exposedHeaders?: string | string[];
7
+ credentials?: boolean;
8
+ maxAge?: number;
9
+ preflightContinue?: boolean;
10
+ optionsSuccessStatus?: number;
11
+ }
3
12
  export interface ServerConfig {
4
13
  port?: number;
5
14
  cors?: boolean | CorsOptions;
@@ -8,6 +17,20 @@ export interface ServerConfig {
8
17
  customMiddleware?: express.RequestHandler[];
9
18
  healthCheck?: boolean | string;
10
19
  gracefulShutdown?: boolean;
20
+ socketIO?: SocketIOConfig;
21
+ name?: string;
22
+ version?: string;
23
+ }
24
+ export interface SocketIOConfig {
25
+ enabled?: boolean;
26
+ cors?: boolean | {
27
+ origin?: string | string[] | boolean;
28
+ methods?: string[];
29
+ credentials?: boolean;
30
+ };
31
+ onConnection?: (socket: unknown) => void;
32
+ onDisconnection?: (socket: unknown, reason: string) => void;
33
+ path?: string;
11
34
  }
12
35
  export interface HealthCheckConfig {
13
36
  path?: string;
@@ -21,4 +44,13 @@ export interface GracefulShutdownConfig {
21
44
  timeout?: number;
22
45
  onShutdown?: () => Promise<void>;
23
46
  }
47
+ export interface SocketInstance {
48
+ id: string;
49
+ emit: (event: string, data?: unknown) => void;
50
+ on: (event: string, handler: (...args: unknown[]) => void) => void;
51
+ broadcast: {
52
+ emit: (event: string, data?: unknown) => void;
53
+ };
54
+ disconnect: () => void;
55
+ }
24
56
  export type ServerPlugin = (app: express.Application, config: ServerConfig) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naman_deep_singh/server-utils",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Extensible server utilities for Express.js microservices with TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,15 +18,10 @@
18
18
  "license": "ISC",
19
19
  "packageManager": "pnpm@10.20.0",
20
20
  "dependencies": {
21
- "express": "^5.1.0",
22
- "cors": "^2.8.5",
23
- "helmet": "^8.1.0",
24
- "cookie-parser": "^1.4.6"
21
+ "express": "^4.21.1"
25
22
  },
26
23
  "devDependencies": {
27
- "@types/express": "^5.0.5",
28
- "@types/cors": "^2.8.19",
29
- "@types/cookie-parser": "^1.4.7",
24
+ "@types/express": "^4.17.21",
30
25
  "typescript": "^5.9.3"
31
26
  }
32
27
  }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Core server utilities
2
- export { createServer, withPlugin, createServerWithPlugins } from './server';
2
+ export { ExpressServer, createServer } from './server';
3
+ export type { ServerInstance, ServerInfo, GrpcService, RpcMethod, WebhookConfig } from './server';
3
4
 
4
5
  // Health check utilities
5
6
  export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
@@ -42,5 +43,71 @@ export type {
42
43
  HealthCheckConfig,
43
44
  HealthCheck,
44
45
  GracefulShutdownConfig,
45
- ServerPlugin
46
- } from './types';
46
+ ServerPlugin,
47
+ SocketIOConfig,
48
+ SocketInstance
49
+ } from './types';
50
+
51
+ // Import all exports for default export
52
+ import { ExpressServer, createServer } from './server';
53
+ import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
54
+ import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
55
+ import {
56
+ createLoggingMiddleware,
57
+ createErrorHandler,
58
+ createRequestIdMiddleware,
59
+ createValidationMiddleware,
60
+ createRateLimitMiddleware,
61
+ createAuthMiddleware,
62
+ withLogging,
63
+ withErrorHandler,
64
+ withRequestId,
65
+ withValidation,
66
+ withRateLimit,
67
+ withAuth,
68
+ validateFields,
69
+ rateLimit,
70
+ requireAuth
71
+ } from './middleware';
72
+ import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
73
+
74
+ // Default export for namespace usage
75
+ const ServerUtils = {
76
+ // Server creation
77
+ createServer,
78
+ ExpressServer,
79
+
80
+ // Health checks
81
+ createHealthCheck,
82
+ withHealthCheck,
83
+ addHealthCheck,
84
+
85
+ // Graceful shutdown
86
+ createGracefulShutdown,
87
+ withGracefulShutdown,
88
+ startServerWithShutdown,
89
+
90
+ // Middleware
91
+ createLoggingMiddleware,
92
+ createErrorHandler,
93
+ createRequestIdMiddleware,
94
+ createValidationMiddleware,
95
+ createRateLimitMiddleware,
96
+ createAuthMiddleware,
97
+ withLogging,
98
+ withErrorHandler,
99
+ withRequestId,
100
+ withValidation,
101
+ withRateLimit,
102
+ withAuth,
103
+ validateFields,
104
+ rateLimit,
105
+ requireAuth,
106
+
107
+ // Utils
108
+ getEnv,
109
+ getEnvNumber,
110
+ getEnvBoolean
111
+ };
112
+
113
+ export default ServerUtils;
package/src/middleware.ts CHANGED
@@ -22,22 +22,25 @@ export function createLoggingMiddleware(format: 'simple' | 'detailed' = 'simple'
22
22
 
23
23
  // Error handling middleware
24
24
  export function createErrorHandler(): express.ErrorRequestHandler {
25
- return (err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
25
+ return (err: unknown, req: express.Request, res: express.Response, next: express.NextFunction) => {
26
26
  console.error('Error:', err);
27
27
 
28
28
  if (res.headersSent) {
29
29
  return next(err);
30
30
  }
31
31
 
32
- const status = err.status || err.statusCode || 500;
32
+ // Type guard for error objects
33
+ const errorObj = err as { status?: number; statusCode?: number; message?: string; stack?: string };
34
+
35
+ const status = errorObj.status || errorObj.statusCode || 500;
33
36
  const message = process.env.NODE_ENV === 'production'
34
37
  ? 'Internal Server Error'
35
- : err.message;
38
+ : errorObj.message || 'Unknown error';
36
39
 
37
40
  res.status(status).json({
38
41
  status: false,
39
42
  message,
40
- ...(process.env.NODE_ENV !== 'production' && { stack: err.stack })
43
+ ...(process.env.NODE_ENV !== 'production' && { stack: errorObj.stack })
41
44
  });
42
45
  };
43
46
  }
@@ -46,7 +49,7 @@ export function createErrorHandler(): express.ErrorRequestHandler {
46
49
  export function createRequestIdMiddleware(): express.RequestHandler {
47
50
  return (req: express.Request, res: express.Response, next: express.NextFunction) => {
48
51
  const requestId = Math.random().toString(36).substring(2, 15);
49
- (req as any).requestId = requestId;
52
+ (req as express.Request & { requestId: string }).requestId = requestId;
50
53
  res.setHeader('X-Request-ID', requestId);
51
54
  next();
52
55
  };
@@ -60,7 +63,7 @@ export interface ValidationRule {
60
63
  minLength?: number;
61
64
  maxLength?: number;
62
65
  pattern?: RegExp;
63
- custom?: (value: any) => boolean | string;
66
+ custom?: (value: unknown) => boolean | string;
64
67
  }
65
68
 
66
69
  export function createValidationMiddleware(rules: ValidationRule[]): express.RequestHandler {
@@ -181,7 +184,7 @@ export function createRateLimitMiddleware(config: RateLimitConfig = {}): express
181
184
  // Authentication middleware helper
182
185
  export interface AuthConfig {
183
186
  tokenExtractor?: (req: express.Request) => string | null;
184
- tokenValidator?: (token: string) => Promise<any> | any;
187
+ tokenValidator?: (token: string) => Promise<unknown> | unknown;
185
188
  unauthorizedMessage?: string;
186
189
  }
187
190
 
@@ -210,7 +213,7 @@ export function createAuthMiddleware(config: AuthConfig): express.RequestHandler
210
213
  }
211
214
 
212
215
  const user = await tokenValidator(token);
213
- (req as any).user = user;
216
+ (req as express.Request & { user: unknown }).user = user;
214
217
  next();
215
218
  } catch (error) {
216
219
  return res.status(401).json({