@naman_deep_singh/server-utils 1.0.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.
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGracefulShutdown = createGracefulShutdown;
4
+ exports.withGracefulShutdown = withGracefulShutdown;
5
+ exports.startServerWithShutdown = startServerWithShutdown;
6
+ function createGracefulShutdown(server, config = {}) {
7
+ const { timeout = 10000, onShutdown } = config;
8
+ const shutdown = async (signal) => {
9
+ console.log(`🛑 Received ${signal}, shutting down gracefully...`);
10
+ const shutdownTimer = setTimeout(() => {
11
+ console.log('⏰ Shutdown timeout reached, forcing exit');
12
+ process.exit(1);
13
+ }, timeout);
14
+ try {
15
+ // Run custom shutdown logic
16
+ if (onShutdown) {
17
+ await onShutdown();
18
+ }
19
+ // Close server
20
+ server.close(() => {
21
+ clearTimeout(shutdownTimer);
22
+ console.log('👋 Server closed. Exiting now.');
23
+ process.exit(0);
24
+ });
25
+ }
26
+ catch (error) {
27
+ clearTimeout(shutdownTimer);
28
+ console.error('❌ Error during shutdown:', error);
29
+ process.exit(1);
30
+ }
31
+ };
32
+ process.on('SIGINT', () => shutdown('SIGINT'));
33
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
34
+ }
35
+ function withGracefulShutdown(config = {}) {
36
+ return (app, serverConfig) => {
37
+ // This plugin needs to be applied after server.listen()
38
+ // Store config for later use
39
+ app.__gracefulShutdownConfig = config;
40
+ };
41
+ }
42
+ function startServerWithShutdown(app, port, shutdownConfig = {}) {
43
+ const server = app.listen(port, () => {
44
+ console.log(`🚀 Server running on http://localhost:${port}`);
45
+ });
46
+ // Apply graceful shutdown from stored config or provided config
47
+ const config = app.__gracefulShutdownConfig || shutdownConfig;
48
+ createGracefulShutdown(server, config);
49
+ return server;
50
+ }
@@ -0,0 +1,24 @@
1
+ import express from 'express';
2
+ import { CorsOptions } from 'cors';
3
+ export interface ServerConfig {
4
+ port?: number;
5
+ cors?: boolean | CorsOptions;
6
+ helmet?: boolean;
7
+ json?: boolean;
8
+ customMiddleware?: express.RequestHandler[];
9
+ healthCheck?: boolean | string;
10
+ gracefulShutdown?: boolean;
11
+ }
12
+ export interface HealthCheckConfig {
13
+ path?: string;
14
+ customChecks?: HealthCheck[];
15
+ }
16
+ export interface HealthCheck {
17
+ name: string;
18
+ check: () => Promise<boolean>;
19
+ }
20
+ export interface GracefulShutdownConfig {
21
+ timeout?: number;
22
+ onShutdown?: () => Promise<void>;
23
+ }
24
+ export type ServerPlugin = (app: express.Application, config: ServerConfig) => void;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export declare function getEnv(key: string, defaultValue?: string): string;
2
+ export declare function getEnvNumber(key: string, defaultValue?: number): number;
3
+ export declare function getEnvBoolean(key: string, defaultValue?: boolean): boolean;
package/dist/utils.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEnv = getEnv;
4
+ exports.getEnvNumber = getEnvNumber;
5
+ exports.getEnvBoolean = getEnvBoolean;
6
+ // Environment utilities
7
+ function getEnv(key, defaultValue) {
8
+ const value = process.env[key];
9
+ if (value === undefined && defaultValue === undefined) {
10
+ throw new Error(`Environment variable ${key} is required`);
11
+ }
12
+ return value || defaultValue;
13
+ }
14
+ function getEnvNumber(key, defaultValue) {
15
+ const value = process.env[key];
16
+ if (value === undefined) {
17
+ if (defaultValue === undefined) {
18
+ throw new Error(`Environment variable ${key} is required`);
19
+ }
20
+ return defaultValue;
21
+ }
22
+ const parsed = parseInt(value, 10);
23
+ if (isNaN(parsed)) {
24
+ throw new Error(`Environment variable ${key} must be a number`);
25
+ }
26
+ return parsed;
27
+ }
28
+ function getEnvBoolean(key, defaultValue) {
29
+ const value = process.env[key];
30
+ if (value === undefined) {
31
+ if (defaultValue === undefined) {
32
+ throw new Error(`Environment variable ${key} is required`);
33
+ }
34
+ return defaultValue;
35
+ }
36
+ return value.toLowerCase() === 'true';
37
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@naman_deep_singh/server-utils",
3
+ "version": "1.0.0",
4
+ "description": "Extensible server utilities for Express.js microservices with TypeScript",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "keywords": [
11
+ "server",
12
+ "express",
13
+ "microservices",
14
+ "utils",
15
+ "typescript"
16
+ ],
17
+ "author": "Naman Deep Singh",
18
+ "license": "ISC",
19
+ "packageManager": "pnpm@10.20.0",
20
+ "dependencies": {
21
+ "express": "^5.1.0",
22
+ "cors": "^2.8.5",
23
+ "helmet": "^8.1.0",
24
+ "cookie-parser": "^1.4.6"
25
+ },
26
+ "devDependencies": {
27
+ "@types/express": "^5.0.5",
28
+ "@types/cors": "^2.8.19",
29
+ "@types/cookie-parser": "^1.4.7",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }
package/src/health.ts ADDED
@@ -0,0 +1,47 @@
1
+ import express from 'express';
2
+ import { HealthCheckConfig, HealthCheck, ServerPlugin } from './types';
3
+
4
+ export function createHealthCheck(config: HealthCheckConfig = {}): express.RequestHandler {
5
+ const { customChecks = [] } = config;
6
+
7
+ return async (req: express.Request, res: express.Response) => {
8
+ try {
9
+ const checks: Record<string, boolean> = {
10
+ server: true,
11
+ timestamp: Date.now() as any
12
+ };
13
+
14
+ // Run custom health checks
15
+ for (const check of customChecks) {
16
+ try {
17
+ checks[check.name] = await check.check();
18
+ } catch (error) {
19
+ checks[check.name] = false;
20
+ }
21
+ }
22
+
23
+ const isHealthy = Object.values(checks).every(status => status === true || typeof status === 'number');
24
+
25
+ res.status(isHealthy ? 200 : 503).json({
26
+ status: isHealthy ? 'healthy' : 'unhealthy',
27
+ checks
28
+ });
29
+ } catch (error) {
30
+ res.status(503).json({
31
+ status: 'unhealthy',
32
+ error: 'Health check failed'
33
+ });
34
+ }
35
+ };
36
+ }
37
+
38
+ export function withHealthCheck(path: string = '/health', config: HealthCheckConfig = {}): ServerPlugin {
39
+ return (app: express.Application) => {
40
+ app.get(path, createHealthCheck(config));
41
+ };
42
+ }
43
+
44
+ // Convenience function for direct use
45
+ export function addHealthCheck(app: express.Application, path: string = '/health', config: HealthCheckConfig = {}): void {
46
+ app.get(path, createHealthCheck(config));
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,46 @@
1
+ // Core server utilities
2
+ export { createServer, withPlugin, createServerWithPlugins } from './server';
3
+
4
+ // Health check utilities
5
+ export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
6
+
7
+ // Graceful shutdown utilities
8
+ export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
9
+
10
+ // Middleware utilities
11
+ export {
12
+ createLoggingMiddleware,
13
+ createErrorHandler,
14
+ createRequestIdMiddleware,
15
+ createValidationMiddleware,
16
+ createRateLimitMiddleware,
17
+ createAuthMiddleware,
18
+ withLogging,
19
+ withErrorHandler,
20
+ withRequestId,
21
+ withValidation,
22
+ withRateLimit,
23
+ withAuth,
24
+ validateFields,
25
+ rateLimit,
26
+ requireAuth,
27
+ type ValidationRule,
28
+ type RateLimitConfig,
29
+ type AuthConfig
30
+ } from './middleware';
31
+
32
+ // Utility functions
33
+ export {
34
+ getEnv,
35
+ getEnvNumber,
36
+ getEnvBoolean
37
+ } from './utils';
38
+
39
+ // Types
40
+ export type {
41
+ ServerConfig,
42
+ HealthCheckConfig,
43
+ HealthCheck,
44
+ GracefulShutdownConfig,
45
+ ServerPlugin
46
+ } from './types';
@@ -0,0 +1,272 @@
1
+ import express from 'express';
2
+ import { ServerPlugin } from './types';
3
+
4
+ // Logging middleware
5
+ export function createLoggingMiddleware(format: 'simple' | 'detailed' = 'simple'): express.RequestHandler {
6
+ return (req: express.Request, res: express.Response, next: express.NextFunction) => {
7
+ const start = Date.now();
8
+
9
+ res.on('finish', () => {
10
+ const duration = Date.now() - start;
11
+
12
+ if (format === 'detailed') {
13
+ console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms - ${req.ip}`);
14
+ } else {
15
+ console.log(`${req.method} ${req.url} - ${res.statusCode}`);
16
+ }
17
+ });
18
+
19
+ next();
20
+ };
21
+ }
22
+
23
+ // Error handling middleware
24
+ export function createErrorHandler(): express.ErrorRequestHandler {
25
+ return (err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
26
+ console.error('Error:', err);
27
+
28
+ if (res.headersSent) {
29
+ return next(err);
30
+ }
31
+
32
+ const status = err.status || err.statusCode || 500;
33
+ const message = process.env.NODE_ENV === 'production'
34
+ ? 'Internal Server Error'
35
+ : err.message;
36
+
37
+ res.status(status).json({
38
+ status: false,
39
+ message,
40
+ ...(process.env.NODE_ENV !== 'production' && { stack: err.stack })
41
+ });
42
+ };
43
+ }
44
+
45
+ // Request ID middleware
46
+ export function createRequestIdMiddleware(): express.RequestHandler {
47
+ return (req: express.Request, res: express.Response, next: express.NextFunction) => {
48
+ const requestId = Math.random().toString(36).substring(2, 15);
49
+ (req as any).requestId = requestId;
50
+ res.setHeader('X-Request-ID', requestId);
51
+ next();
52
+ };
53
+ }
54
+
55
+ // Validation middleware
56
+ export interface ValidationRule {
57
+ field: string;
58
+ required?: boolean;
59
+ type?: 'string' | 'number' | 'email' | 'boolean';
60
+ minLength?: number;
61
+ maxLength?: number;
62
+ pattern?: RegExp;
63
+ custom?: (value: any) => boolean | string;
64
+ }
65
+
66
+ export function createValidationMiddleware(rules: ValidationRule[]): express.RequestHandler {
67
+ return (req: express.Request, res: express.Response, next: express.NextFunction) => {
68
+ const errors: string[] = [];
69
+
70
+ for (const rule of rules) {
71
+ const value = req.body[rule.field];
72
+
73
+ if (rule.required && (value === undefined || value === null || value === '')) {
74
+ errors.push(`${rule.field} is required`);
75
+ continue;
76
+ }
77
+
78
+ if (value === undefined || value === null) continue;
79
+
80
+ if (rule.type) {
81
+ switch (rule.type) {
82
+ case 'email':
83
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
84
+ if (!emailRegex.test(value)) {
85
+ errors.push(`${rule.field} must be a valid email`);
86
+ }
87
+ break;
88
+ case 'string':
89
+ if (typeof value !== 'string') {
90
+ errors.push(`${rule.field} must be a string`);
91
+ }
92
+ break;
93
+ case 'number':
94
+ if (typeof value !== 'number' && isNaN(Number(value))) {
95
+ errors.push(`${rule.field} must be a number`);
96
+ }
97
+ break;
98
+ case 'boolean':
99
+ if (typeof value !== 'boolean') {
100
+ errors.push(`${rule.field} must be a boolean`);
101
+ }
102
+ break;
103
+ }
104
+ }
105
+
106
+ if (rule.minLength && value.length < rule.minLength) {
107
+ errors.push(`${rule.field} must be at least ${rule.minLength} characters`);
108
+ }
109
+ if (rule.maxLength && value.length > rule.maxLength) {
110
+ errors.push(`${rule.field} must be no more than ${rule.maxLength} characters`);
111
+ }
112
+
113
+ if (rule.pattern && !rule.pattern.test(value)) {
114
+ errors.push(`${rule.field} format is invalid`);
115
+ }
116
+
117
+ if (rule.custom) {
118
+ const result = rule.custom(value);
119
+ if (result !== true) {
120
+ errors.push(typeof result === 'string' ? result : `${rule.field} is invalid`);
121
+ }
122
+ }
123
+ }
124
+
125
+ if (errors.length > 0) {
126
+ return res.status(400).json({
127
+ status: false,
128
+ message: 'Validation failed',
129
+ errors
130
+ });
131
+ }
132
+
133
+ next();
134
+ };
135
+ }
136
+
137
+ // Rate limiting middleware
138
+ export interface RateLimitConfig {
139
+ windowMs?: number;
140
+ maxRequests?: number;
141
+ message?: string;
142
+ keyGenerator?: (req: express.Request) => string;
143
+ }
144
+
145
+ const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
146
+
147
+ export function createRateLimitMiddleware(config: RateLimitConfig = {}): express.RequestHandler {
148
+ const {
149
+ windowMs = 15 * 60 * 1000,
150
+ maxRequests = 100,
151
+ message = 'Too many requests, please try again later',
152
+ keyGenerator = (req) => req.ip || 'unknown'
153
+ } = config;
154
+
155
+ return (req: express.Request, res: express.Response, next: express.NextFunction) => {
156
+ const key = keyGenerator(req);
157
+ const now = Date.now();
158
+ const record = rateLimitStore.get(key);
159
+
160
+ if (!record || now > record.resetTime) {
161
+ rateLimitStore.set(key, {
162
+ count: 1,
163
+ resetTime: now + windowMs
164
+ });
165
+ return next();
166
+ }
167
+
168
+ if (record.count >= maxRequests) {
169
+ return res.status(429).json({
170
+ status: false,
171
+ message,
172
+ retryAfter: Math.ceil((record.resetTime - now) / 1000)
173
+ });
174
+ }
175
+
176
+ record.count++;
177
+ next();
178
+ };
179
+ }
180
+
181
+ // Authentication middleware helper
182
+ export interface AuthConfig {
183
+ tokenExtractor?: (req: express.Request) => string | null;
184
+ tokenValidator?: (token: string) => Promise<any> | any;
185
+ unauthorizedMessage?: string;
186
+ }
187
+
188
+ export function createAuthMiddleware(config: AuthConfig): express.RequestHandler {
189
+ const {
190
+ tokenExtractor = (req) => {
191
+ const authHeader = req.headers.authorization;
192
+ if (authHeader && authHeader.startsWith('Bearer ')) {
193
+ return authHeader.substring(7);
194
+ }
195
+ return req.cookies?.token || null;
196
+ },
197
+ tokenValidator = () => { throw new Error('Token validator not implemented'); },
198
+ unauthorizedMessage = 'Unauthorized access'
199
+ } = config;
200
+
201
+ return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
202
+ try {
203
+ const token = tokenExtractor(req);
204
+
205
+ if (!token) {
206
+ return res.status(401).json({
207
+ status: false,
208
+ message: unauthorizedMessage
209
+ });
210
+ }
211
+
212
+ const user = await tokenValidator(token);
213
+ (req as any).user = user;
214
+ next();
215
+ } catch (error) {
216
+ return res.status(401).json({
217
+ status: false,
218
+ message: unauthorizedMessage
219
+ });
220
+ }
221
+ };
222
+ }
223
+
224
+ // Plugin versions
225
+ export function withLogging(format: 'simple' | 'detailed' = 'simple'): ServerPlugin {
226
+ return (app: express.Application) => {
227
+ app.use(createLoggingMiddleware(format));
228
+ };
229
+ }
230
+
231
+ export function withErrorHandler(): ServerPlugin {
232
+ return (app: express.Application) => {
233
+ app.use(createErrorHandler());
234
+ };
235
+ }
236
+
237
+ export function withRequestId(): ServerPlugin {
238
+ return (app: express.Application) => {
239
+ app.use(createRequestIdMiddleware());
240
+ };
241
+ }
242
+
243
+ export function withValidation(rules: ValidationRule[]): ServerPlugin {
244
+ return (app: express.Application) => {
245
+ app.use(createValidationMiddleware(rules));
246
+ };
247
+ }
248
+
249
+ export function withRateLimit(config: RateLimitConfig = {}): ServerPlugin {
250
+ return (app: express.Application) => {
251
+ app.use(createRateLimitMiddleware(config));
252
+ };
253
+ }
254
+
255
+ export function withAuth(config: AuthConfig): ServerPlugin {
256
+ return (app: express.Application) => {
257
+ app.use(createAuthMiddleware(config));
258
+ };
259
+ }
260
+
261
+ // Convenience functions for route-specific middleware
262
+ export function validateFields(rules: ValidationRule[]): express.RequestHandler {
263
+ return createValidationMiddleware(rules);
264
+ }
265
+
266
+ export function rateLimit(config: RateLimitConfig = {}): express.RequestHandler {
267
+ return createRateLimitMiddleware(config);
268
+ }
269
+
270
+ export function requireAuth(config: AuthConfig): express.RequestHandler {
271
+ return createAuthMiddleware(config);
272
+ }
package/src/server.ts ADDED
@@ -0,0 +1,46 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import helmet from 'helmet';
4
+ import { ServerConfig, ServerPlugin } from './types';
5
+
6
+ export function createServer(config: ServerConfig = {}): express.Application {
7
+ const app = express();
8
+
9
+ // Apply default middleware
10
+ if (config.helmet !== false) {
11
+ app.use(helmet());
12
+ }
13
+
14
+ if (config.cors !== false) {
15
+ const corsOptions = typeof config.cors === 'object' ? config.cors : undefined;
16
+ app.use(cors(corsOptions));
17
+ }
18
+
19
+ if (config.json !== false) {
20
+ app.use(express.json());
21
+ }
22
+
23
+ // Apply custom middleware
24
+ if (config.customMiddleware) {
25
+ config.customMiddleware.forEach(middleware => {
26
+ app.use(middleware);
27
+ });
28
+ }
29
+
30
+ return app;
31
+ }
32
+
33
+ export function withPlugin(app: express.Application, plugin: ServerPlugin, config: ServerConfig = {}): express.Application {
34
+ plugin(app, config);
35
+ return app;
36
+ }
37
+
38
+ export function createServerWithPlugins(config: ServerConfig = {}, ...plugins: ServerPlugin[]): express.Application {
39
+ const app = createServer(config);
40
+
41
+ plugins.forEach(plugin => {
42
+ plugin(app, config);
43
+ });
44
+
45
+ return app;
46
+ }
@@ -0,0 +1,60 @@
1
+ import { Server } from 'http';
2
+ import { GracefulShutdownConfig, ServerPlugin } from './types';
3
+
4
+ export function createGracefulShutdown(server: Server, config: GracefulShutdownConfig = {}): void {
5
+ const { timeout = 10000, onShutdown } = config;
6
+
7
+ const shutdown = async (signal: string) => {
8
+ console.log(`🛑 Received ${signal}, shutting down gracefully...`);
9
+
10
+ const shutdownTimer = setTimeout(() => {
11
+ console.log('⏰ Shutdown timeout reached, forcing exit');
12
+ process.exit(1);
13
+ }, timeout);
14
+
15
+ try {
16
+ // Run custom shutdown logic
17
+ if (onShutdown) {
18
+ await onShutdown();
19
+ }
20
+
21
+ // Close server
22
+ server.close(() => {
23
+ clearTimeout(shutdownTimer);
24
+ console.log('👋 Server closed. Exiting now.');
25
+ process.exit(0);
26
+ });
27
+ } catch (error) {
28
+ clearTimeout(shutdownTimer);
29
+ console.error('❌ Error during shutdown:', error);
30
+ process.exit(1);
31
+ }
32
+ };
33
+
34
+ process.on('SIGINT', () => shutdown('SIGINT'));
35
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
36
+ }
37
+
38
+ export function withGracefulShutdown(config: GracefulShutdownConfig = {}): ServerPlugin {
39
+ return (app, serverConfig) => {
40
+ // This plugin needs to be applied after server.listen()
41
+ // Store config for later use
42
+ (app as any).__gracefulShutdownConfig = config;
43
+ };
44
+ }
45
+
46
+ export function startServerWithShutdown(
47
+ app: any,
48
+ port: number,
49
+ shutdownConfig: GracefulShutdownConfig = {}
50
+ ): Server {
51
+ const server = app.listen(port, () => {
52
+ console.log(`🚀 Server running on http://localhost:${port}`);
53
+ });
54
+
55
+ // Apply graceful shutdown from stored config or provided config
56
+ const config = app.__gracefulShutdownConfig || shutdownConfig;
57
+ createGracefulShutdown(server, config);
58
+
59
+ return server;
60
+ }
package/src/types.ts ADDED
@@ -0,0 +1,29 @@
1
+ import express from 'express';
2
+ import { CorsOptions } from 'cors';
3
+
4
+ export interface ServerConfig {
5
+ port?: number;
6
+ cors?: boolean | CorsOptions;
7
+ helmet?: boolean;
8
+ json?: boolean;
9
+ customMiddleware?: express.RequestHandler[];
10
+ healthCheck?: boolean | string;
11
+ gracefulShutdown?: boolean;
12
+ }
13
+
14
+ export interface HealthCheckConfig {
15
+ path?: string;
16
+ customChecks?: HealthCheck[];
17
+ }
18
+
19
+ export interface HealthCheck {
20
+ name: string;
21
+ check: () => Promise<boolean>;
22
+ }
23
+
24
+ export interface GracefulShutdownConfig {
25
+ timeout?: number;
26
+ onShutdown?: () => Promise<void>;
27
+ }
28
+
29
+ export type ServerPlugin = (app: express.Application, config: ServerConfig) => void;
package/src/utils.ts ADDED
@@ -0,0 +1,34 @@
1
+ // Environment utilities
2
+ export function getEnv(key: string, defaultValue?: string): string {
3
+ const value = process.env[key];
4
+ if (value === undefined && defaultValue === undefined) {
5
+ throw new Error(`Environment variable ${key} is required`);
6
+ }
7
+ return value || defaultValue!;
8
+ }
9
+
10
+ export function getEnvNumber(key: string, defaultValue?: number): number {
11
+ const value = process.env[key];
12
+ if (value === undefined) {
13
+ if (defaultValue === undefined) {
14
+ throw new Error(`Environment variable ${key} is required`);
15
+ }
16
+ return defaultValue;
17
+ }
18
+ const parsed = parseInt(value, 10);
19
+ if (isNaN(parsed)) {
20
+ throw new Error(`Environment variable ${key} must be a number`);
21
+ }
22
+ return parsed;
23
+ }
24
+
25
+ export function getEnvBoolean(key: string, defaultValue?: boolean): boolean {
26
+ const value = process.env[key];
27
+ if (value === undefined) {
28
+ if (defaultValue === undefined) {
29
+ throw new Error(`Environment variable ${key} is required`);
30
+ }
31
+ return defaultValue;
32
+ }
33
+ return value.toLowerCase() === 'true';
34
+ }