Production-ready common utilities shared across all Exyconn projects - includes server utilities, client hooks, shared types, data modules, validation helpers, and dynamic configurations
npm install @exyconn/common






Production-ready common utilities, hooks, types, and data shared across all Exyconn projects (botify.life, exyconn.com, partywings.fun, sibera.work, spentiva.com).
Full documentation available at: common-docs.exyconn.com
---
- Installation
- Features
- Quick Start
- Module Reference
- Server
- Client
- Shared
- Data
- React Hooks
- API Reference
- Development
- Peer Dependencies
- Contributing
- License
---
``bash`
npm install @exyconn/commonor
yarn add @exyconn/commonor
pnpm add @exyconn/common
- Node.js >= 18.0.0
- React >= 18.0.0 (for hooks)
- TypeScript >= 5.0.0 (recommended)
| Category | Description |
|----------|-------------|
| π₯οΈ Server utilities | Response helpers, middleware, logging, database connections, dynamic configs |
| π Client utilities | HTTP clients, React hooks (54+), response parsing |
| π Shared types | TypeScript interfaces for API responses, users, etc. |
| ποΈ Data modules | Countries, currencies, phone codes, timezones, brand logos |
| β
Validation | Common patterns, regex, and validation helpers (Zod integration) |
| π
Date utilities | Enhanced date-fns with timezone support |
| π¨ Brand Identity | Complete brand configs with logos, colors, and SEO |
| π’ Enums & Constants | STATUS, SORT, ROLE, BREAKPOINTS, and more |
| βοΈ Dynamic Configs | Production-ready CORS, Rate Limiting, Server configs |
| π§ͺ Fully Tested | Comprehensive test coverage with Vitest |
---
`typescript
// Server utilities
import { successResponse, errorResponse } from '@exyconn/common/server/response';
import { createCorsOptions, createRateLimiter } from '@exyconn/common/server/configs';
import { createLogger } from '@exyconn/common/server/logger';
import { connectDB } from '@exyconn/common/server/db';
import { createCrudControllers, queryParser, queryPagination } from '@exyconn/common/server/middleware';
// Client utilities
import { useLocalStorage, useDebounce } from '@exyconn/common/client/hooks';
import { getRequest, postRequest, extractData, isSuccess } from '@exyconn/common/client/http';
// Shared utilities
import { isValidEmail, VALIDATION_PATTERNS } from '@exyconn/common/shared/validation';
import { getEnv, isProd } from '@exyconn/common/shared/config';
// Data modules
import { countries, getCountryByCode } from '@exyconn/common/data/countries';
import { BRANDS, getBrandById } from '@exyconn/common/data/brand-identity';
`
`typescript`
import { server, client, shared, data } from '@exyconn/common';
---
Import: @exyconn/common/server or specific submodules
#### Server Configs (@exyconn/common/server/configs)
Dynamic configuration system for production applications.
##### ConfigBuilder
`typescript
import {
createConfig,
buildConfig,
ConfigBuilder,
// Types
type AppConfig,
type ServerConfig,
type DatabaseConfig,
type AuthConfig,
type LoggingConfig,
} from '@exyconn/common/server/configs';
// Method 1: Using ConfigBuilder (fluent API)
const config = createConfig()
.setServer({
name: 'my-api-server',
port: 4000,
environment: 'production',
})
.setDatabase({
uri: process.env.MONGODB_URI!,
maxPoolSize: 50,
})
.setAuth({
jwtSecret: process.env.JWT_SECRET!,
jwtExpiresIn: '7d',
})
.setLogging({
level: 'info',
file: true,
})
.addProductionOrigin('https://myapp.com')
.addProductionOrigin('https://api.myapp.com')
.addCorsPattern('.myapp.com')
.addRateLimitTier('upload', {
windowMs: 60000,
maxRequests: 5,
message: 'Upload limit exceeded',
})
.setCustom('featureFlags', { newFeature: true })
.loadFromEnv()
.build();
// Method 2: Quick build from partial config
const quickConfig = buildConfig({
server: { name: 'quick-server', port: 3000 },
database: { uri: 'mongodb://localhost/mydb' },
});
// Validate configuration
const validation = createConfig().setServer({ name: '' }).validate();
if (!validation.valid) {
console.error('Config errors:', validation.errors);
}
`
ConfigBuilder Methods:
| Method | Parameters | Description |
|--------|------------|-------------|
| setServer(config) | Partial | Set server configuration |setDatabase(config)
| | Partial | Set database configuration |setAuth(config)
| | Partial | Set auth configuration |setLogging(config)
| | Partial | Set logging configuration |setCorsOrigins(config)
| | Partial | Set CORS origins |addProductionOrigin(origin)
| | string | Add a production CORS origin |addDevelopmentOrigin(origin)
| | string | Add a development CORS origin |addCorsPattern(pattern)
| | string | Add subdomain pattern |setRateLimit(config)
| | Partial | Set rate limit config |addRateLimitTier(name, tier)
| | string, RateLimitTier | Add custom rate limit tier |setCustom(key, value)
| | string, unknown | Set custom config value |loadFromEnv()
| | - | Load config from environment variables |validate()
| | - | Validate configuration, returns { valid, errors } |build()
| | - | Build final configuration |
##### CORS Configuration
`typescript
import {
createCorsOptions,
createBrandCorsOptions,
createMultiBrandCorsOptions,
// Presets
DEFAULT_CORS_CONFIG,
EXYCONN_CORS_CONFIG,
STRICT_CORS_CONFIG,
PERMISSIVE_CORS_CONFIG,
// Types
type CorsConfig,
} from '@exyconn/common/server/configs';
// Custom CORS configuration
const corsOptions = createCorsOptions({
productionOrigins: ['https://myapp.com', 'https://api.myapp.com'],
developmentOrigins: ['http://localhost:3000', 'http://localhost:5173'],
allowedSubdomains: ['.myapp.com'],
originPatterns: [/\.myapp\.(com|io)$/],
allowNoOrigin: true, // Allow mobile apps, curl
allowAllInDev: true, // Allow all in development
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'],
exposedHeaders: ['X-Total-Count'],
maxAge: 86400, // 24 hours preflight cache
customValidator: (origin) => origin.includes('trusted'), // Custom validation
});
// Single brand CORS (auto-includes www and subdomains)
const brandCors = createBrandCorsOptions('myapp.com', {
credentials: true,
});
// Multi-brand CORS
const multiBrandCors = createMultiBrandCorsOptions(
['myapp.com', 'myapp.io', 'api.myapp.com'],
{ allowNoOrigin: false }
);
// Usage with Express
import cors from 'cors';
app.use(cors(corsOptions));
`
CorsConfig Options:
| Option | Type | Default | Required | Description |
|--------|------|---------|----------|-------------|
| productionOrigins | string[] | [] | No | Allowed production origins |developmentOrigins
| | string[] | Common localhost ports | No | Allowed development origins |allowedSubdomains
| | string[] | [] | No | Subdomain patterns (e.g., .myapp.com) |originPatterns
| | RegExp[] | [] | No | Regex patterns for origin matching |allowNoOrigin
| | boolean | true | No | Allow requests with no origin |allowAllInDev
| | boolean | true | No | Allow all origins in development |customValidator
| | (origin: string) => boolean | - | No | Custom origin validator |credentials
| | boolean | true | No | Include credentials |methods
| | string[] | All standard methods | No | Allowed HTTP methods |allowedHeaders
| | string[] | Common headers | No | Allowed request headers |exposedHeaders
| | string[] | Common response headers | No | Exposed response headers |maxAge
| | number | 86400 | No | Preflight cache duration (seconds) |
##### Rate Limiter Configuration
`typescript
import {
// Factory functions
createRateLimiter,
createStandardRateLimiter,
createStrictRateLimiter,
createDdosRateLimiter,
createApiRateLimiter,
// Builder
rateLimiter,
RateLimiterBuilder,
// Key generators
defaultKeyGenerator,
createPrefixedKeyGenerator,
createUserKeyGenerator,
createApiKeyGenerator,
// Constants
DEFAULT_RATE_LIMIT_TIERS,
// Types
type RateLimitTierConfig,
} from '@exyconn/common/server/configs';
// Method 1: Factory functions with defaults
const standardLimiter = createStandardRateLimiter(); // 100 req/15min
const strictLimiter = createStrictRateLimiter(); // 20 req/15min
const ddosLimiter = createDdosRateLimiter(); // 60 req/1min
const apiLimiter = createApiRateLimiter(); // 30 req/1min (by API key)
// Method 2: Custom configuration
const customLimiter = createRateLimiter({
windowMs: 5 60 1000, // 5 minutes
maxRequests: 50,
message: 'Too many requests, please slow down.',
skipSuccessfulRequests: false,
skipFailedRequests: false,
}, {
standardHeaders: true,
legacyHeaders: false,
keyGenerator: defaultKeyGenerator,
skip: (req) => req.headers['x-skip-rate-limit'] === 'true',
});
// Method 3: Builder pattern (most flexible)
const uploadLimiter = rateLimiter('STRICT')
.windowMinutes(5)
.max(10)
.message('Upload limit exceeded. Please wait.')
.keyByIp()
.skipWhen((req) => req.user?.isPremium)
.build();
// User-based rate limiting
const userLimiter = rateLimiter()
.windowHours(1)
.max(1000)
.keyBy((req) => req.userId || defaultKeyGenerator(req))
.build();
// API key based rate limiting
const apiKeyLimiter = rateLimiter('API')
.keyByApiKey('x-api-key')
.build();
// Usage with Express
app.use('/api/', ddosLimiter);
app.use('/api/', standardLimiter);
app.post('/api/auth/login', strictLimiter);
app.post('/api/upload', uploadLimiter);
`
RateLimiterBuilder Methods:
| Method | Parameters | Description |
|--------|------------|-------------|
| windowMs(ms) | number | Set window in milliseconds |windowMinutes(minutes)
| | number | Set window in minutes |windowHours(hours)
| | number | Set window in hours |max(requests)
| | number | Set max requests in window |message(msg)
| | string | Set error message |skipSuccessful(skip?)
| | boolean | Skip successful requests (default: true) |skipFailed(skip?)
| | boolean | Skip failed requests (default: true) |keyBy(generator)
| | (req) => string | Custom key generator |keyByIp()
| | - | Key by IP address (default) |keyByApiKey(headerName?)
| | string | Key by API key header |skipWhen(predicate)
| | (req) => boolean | Skip when condition is true |build()
| | - | Build rate limiter middleware |
Available Rate Limit Tiers:
| Tier | Window | Max Requests | Use Case |
|------|--------|--------------|----------|
| STANDARD | 15 min | 100 | General API endpoints |STRICT
| | 15 min | 20 | Auth endpoints (login, signup) |DDOS
| | 1 min | 60 | Global DDoS protection |VERY_STRICT
| | 1 hour | 5 | Password reset, sensitive actions |RELAXED
| | 15 min | 500 | High-traffic endpoints |API
| | 1 min | 30 | Third-party API access |
#### Response Helpers (@exyconn/common/server/response)
`typescript
import {
// Success responses
successResponse, // 200 OK
successResponseArr, // 200 OK with pagination
createdResponse, // 201 Created
noContentResponse, // 204 No Content
// Error responses
badRequestResponse, // 400 Bad Request
unauthorizedResponse, // 401 Unauthorized
forbiddenResponse, // 403 Forbidden
notFoundResponse, // 404 Not Found
conflictResponse, // 409 Conflict
serverErrorResponse, // 500 Internal Server Error
// Utilities
extractColumns,
// Types
type ApiResponse,
type PaginationData,
type ColumnMetadata,
} from '@exyconn/common/server/response';
// Basic success response
app.get('/api/user/:id', async (req, res) => {
const user = await User.findById(req.params.id);
return successResponse(res, user, 'User retrieved successfully');
});
// Response: { status: 'success', statusCode: 200, message: '...', data: {...} }
// Paginated array response (auto-extracts columns for table rendering)
app.get('/api/users', async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const users = await User.find().skip((page - 1) * limit).limit(limit);
const total = await User.countDocuments();
return successResponseArr(res, users, {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
hasNextPage: page < Math.ceil(total / limit),
hasPrevPage: page > 1,
}, 'Users retrieved');
});
// Error responses
return badRequestResponse(res, 'Invalid email format', { field: 'email' });
return unauthorizedResponse(res, 'Token expired');
return forbiddenResponse(res, 'Insufficient permissions');
return notFoundResponse(res, 'User not found');
return serverErrorResponse(res, 'Database connection failed');
`
Response Functions:
| Function | Status | Parameters | Description |
|----------|--------|------------|-------------|
| successResponse | 200 | (res, data, message?) | Standard success |successResponseArr
| | 200 | (res, data[], pagination?, message?) | Paginated array |createdResponse
| | 201 | (res, data, message?) | Resource created |noContentResponse
| | 200* | (res, data?, message?) | No data found |badRequestResponse
| | 400 | (res, message?, errors?) | Invalid request |unauthorizedResponse
| | 401 | (res, message?) | Authentication required |forbiddenResponse
| | 403 | (res, message?) | Access denied |notFoundResponse
| | 404 | (res, message?) | Resource not found |conflictResponse
| | 409 | (res, message?) | Resource conflict |serverErrorResponse
| | 500 | (res, message?, error?) | Server error |
#### Logger (@exyconn/common/server/logger)
`typescript
import {
createLogger,
logger, // Default instance
simpleLogger, // Console-only logger
createMorganStream,
stream, // Morgan stream for default logger
type LoggerConfig,
} from '@exyconn/common/server/logger';
// Default logger (writes to console + files)
logger.info('Server started', { port: 3000 });
logger.error('Database error', { error: err.message, stack: err.stack });
logger.warn('Rate limit approaching');
logger.debug('Request received', { method: 'GET', path: '/api/users' });
// Custom logger
const customLogger = createLogger({
level: 'debug', // 'error' | 'warn' | 'info' | 'http' | 'debug'
logsDir: 'logs', // Directory for log files
maxSize: '20m', // Max file size before rotation
maxFiles: '14d', // Keep logs for 14 days
errorMaxFiles: '30d', // Keep error logs longer
});
// Simple logger (no files, just console)
simpleLogger.info('Quick debug message');
// With Morgan HTTP logger
import morgan from 'morgan';
app.use(morgan('combined', { stream }));
`
#### Database (@exyconn/common/server/db)
`typescript
import {
connectDB,
disconnectDB,
getConnectionStatus,
type DbConnectionOptions,
} from '@exyconn/common/server/db';
// Connect with default options
await connectDB(process.env.MONGODB_URI!, {}, logger);
// Connect with custom options
await connectDB(process.env.MONGODB_URI!, {
maxPoolSize: 50,
minPoolSize: 10,
socketTimeoutMS: 45000,
retryWrites: true,
}, logger);
// Check status
const status = getConnectionStatus(); // 'connected' | 'disconnected' | ...
// Disconnect gracefully
await disconnectDB(logger);
`
#### Middleware (@exyconn/common/server/middleware)
##### CRUD Controller Factory
`typescript
import { createCrudControllers } from '@exyconn/common/server/middleware';
import { z } from 'zod';
import { User } from './models/User';
const userController = createCrudControllers({
model: User,
resourceName: 'User',
createSchema: z.object({ name: z.string(), email: z.string().email() }),
updateSchema: z.object({ name: z.string().optional() }),
searchFields: ['name', 'email'],
withOrganization: true,
});
// Auto-generated routes
router.get('/users', userController.getAll);
router.get('/users/:id', userController.getById);
router.post('/users', userController.create);
router.put('/users/:id', userController.update);
router.delete('/users/:id', userController.deleteOne);
router.delete('/users', userController.bulkDelete);
`
##### Query Parser & Pagination
`typescript
import { queryParser, queryPagination } from '@exyconn/common/server/middleware';
router.get('/users', queryParser, async (req, res) => {
const { page, limit, sort, search, filter } = req.parsedQuery;
// page: 1, limit: 10, sort: { createdAt: 'desc' }, search: 'john'
await queryPagination(User, {
searchFields: ['name', 'email'],
populate: ['department'],
}, true)(req, res, () => {});
res.json(res.paginatedResult);
// { data: [...], meta: { total, page, limit, totalPages }, columns: [...] }
});
`
##### Authentication Middleware
`typescript
import {
authenticateJWT,
optionalAuthenticateJWT,
authenticateApiKey,
extractOrganization,
type AuthRequest,
} from '@exyconn/common/server/middleware';
// JWT Authentication (required)
app.use('/api/protected', authenticateJWT(process.env.JWT_SECRET!));
// JWT Authentication (optional)
app.use('/api/public', optionalAuthenticateJWT(process.env.JWT_SECRET!));
// API Key Authentication
app.use('/api/external', authenticateApiKey(async (key) => {
const apiKey = await ApiKey.findOne({ key, isActive: true });
return { valid: !!apiKey, organizationId: apiKey?.organizationId };
}));
// Access auth data in routes
app.get('/api/profile', authenticateJWT(secret), (req: AuthRequest, res) => {
const userId = req.userId;
const orgId = req.organizationId;
});
`
#### Server Utils (@exyconn/common/server/utils)
`typescript
import {
buildFilter,
buildPagination,
buildPaginationMeta,
} from '@exyconn/common/server/utils';
// Build MongoDB filter dynamically
const filter = buildFilter({
organizationId: req.organizationId,
search: req.query.q,
searchFields: ['name', 'email', 'phone'],
status: req.query.status,
startDate: req.query.from,
endDate: req.query.to,
});
// Calculate pagination
const pagination = buildPagination({
page: parseInt(req.query.page) || 1,
limit: parseInt(req.query.limit),
defaultLimit: 10,
maxLimit: 100,
});
// Build pagination meta
const meta = buildPaginationMeta(totalCount, pagination.page, pagination.limit);
`
---
Import: @exyconn/common/client or specific submodules
#### React Hooks (@exyconn/common/client/hooks)
50+ production-ready React hooks organized by category.
##### State & Logic Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useToggle | Boolean toggle | const [isOpen, toggle] = useToggle(false) |useCounter
| | Numeric counter | const { count, increment, decrement } = useCounter(0) |usePrevious
| | Track previous value | const prevValue = usePrevious(value) |useObjectState
| | Partial state updates | const [state, setState] = useObjectState({ a: 1 }) |useHistoryState
| | State with undo/redo | const [value, setValue, { undo, redo }] = useHistoryState('') |useList
| | Array operations | const [list, { push, removeAt }] = useList([]) |useMap
| | Map operations | const [map, { set, remove }] = useMap() |useSet
| | Set operations | const [set, { add, toggle }] = useSet() |
##### Side Effects & Timing Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useDebounce | Debounce value | const debouncedSearch = useDebounce(search, 500) |useThrottle
| | Throttle value | const throttled = useThrottle(value, 100) |useTimeout
| | Execute after delay | const { reset, clear } = useTimeout(fn, 3000) |useInterval
| | Repeated execution | const { start, stop } = useInterval(fn, 1000) |useCountdown
| | Countdown timer | const { count, start, stop } = useCountdown({ seconds: 60 }) |
##### Browser & Document Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useWindowSize | Track dimensions | const { width, height } = useWindowSize() |useWindowScroll
| | Track scroll | const { y, scrollToTop } = useWindowScroll() |useDocumentTitle
| | Set title | useDocumentTitle('My Page') |useVisibilityChange
| | Tab visibility | const isVisible = useVisibilityChange() |useLockBodyScroll
| | Prevent scroll | useLockBodyScroll(isModalOpen) |useIsClient
| | Client check | const isClient = useIsClient() |
##### Events & Interaction Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useEventListener | Attach events | useEventListener('keydown', handler) |useKeyPress
| | Detect key | const isEnter = useKeyPress('Enter') |useHover
| | Track hover | const [ref, isHovered] = useHover() |useClickAway
| | Click outside | useClickAway(ref, closeHandler) |useLongPress
| | Long press | const props = useLongPress(handler, { delay: 500 }) |useCopyToClipboard
| | Copy text | const [copied, copy] = useCopyToClipboard() |
##### Media & Device Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useMediaQuery | Responsive | const isMobile = useMediaQuery('(max-width: 768px)') |useBattery
| | Battery status | const { level, charging } = useBattery() |useNetworkState
| | Network info | const { online, effectiveType } = useNetworkState() |useIdle
| | User inactivity | const isIdle = useIdle(30000) |useGeolocation
| | User location | const { latitude, longitude } = useGeolocation() |useThemeDetector
| | System theme | const isDark = useThemeDetector() |
##### Storage Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useLocalStorage | Persist in localStorage | const [value, setValue] = useLocalStorage('key', initial) |useSessionStorage
| | Session storage | const [value, setValue] = useSessionStorage('key', initial) |
##### Data Fetching Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useFetch | Data fetching | const { data, loading, error } = useFetch('/api/data') |useScript
| | Load scripts | const { loaded, error } = useScript('https://...') |
##### Performance Hooks
| Hook | Description | Example |
|------|-------------|---------|
| useRenderCount | Count renders | const count = useRenderCount() |useMeasure
| | Element dimensions | const [ref, { width, height }] = useMeasure() |useIntersectionObserver
| | Visibility detection | const [ref, entry] = useIntersectionObserver() |
#### HTTP Client (@exyconn/common/client/http)
`typescript
import {
getRequest,
postRequest,
putRequest,
patchRequest,
deleteRequest,
uploadFile,
extractData,
extractPaginatedData,
isSuccess,
parseError,
} from '@exyconn/common/client/http';
// GET request
const response = await getRequest('/api/users');
const users = extractData(response);
// POST request
const createResponse = await postRequest('/api/users', { name: 'John' });
if (isSuccess(createResponse)) {
console.log('User created!');
}
// Paginated response
const paginatedResponse = await getRequest('/api/users', { page: 1, limit: 10 });
const { items, total, page, totalPages } = extractPaginatedData(paginatedResponse);
// File upload
const uploadResponse = await uploadFile('/api/upload', file, { folder: 'avatars' });
// Error handling
try {
await postRequest('/api/users', invalidData);
} catch (error) {
const { message, statusCode } = parseError(error);
}
`
HTTP Functions:
| Function | Parameters | Returns | Description |
|----------|------------|---------|-------------|
| getRequest | url, params?, headers? | Promise | GET request |postRequest
| | url, data?, headers? | Promise | POST request |putRequest
| | url, data?, headers? | Promise | PUT request |patchRequest
| | url, data?, headers? | Promise | PATCH request |deleteRequest
| | url, params?, headers? | Promise | DELETE request |uploadFile
| | url, file, data? | Promise | File upload |
Response Utilities:
| Function | Description |
|----------|-------------|
| extractData | Extract data from axios response |extractMessage
| | Extract message from response |extractPaginatedData
| | Extract paginated data with meta |isSuccess
| | Check if response status is 2xx |isSuccessResponse
| | Check response data for success |parseError
| | Parse error to standardized format |parseResponseData
| | Parse data from raw response |parsePaginatedResponse
| | Parse paginated response data |safeJsonParse
| | Safe JSON parsing with fallback |
Slug Utilities:
`typescript
import { generateSlug, generateUrlSlug, generateSnakeSlug } from '@exyconn/common/client/http';
generateSlug('Hello World'); // 'helloWorld'
generateUrlSlug('Hello World'); // 'hello-world'
generateSnakeSlug('Hello World'); // 'hello_world'
`
---
Import: @exyconn/common/shared or specific submodules
#### Validation (@exyconn/common/shared/validation)
`typescript
import {
VALIDATION_PATTERNS,
isValidEmail,
isValidPassword,
isValidPhone,
isValidUrl,
} from '@exyconn/common/shared/validation';
isValidEmail('test@example.com'); // true
isValidPassword('Password1!', 'strong'); // true
isValidPhone('+1 234 567 8901'); // true
// Use patterns directly
VALIDATION_PATTERNS.EMAIL.test('test@example.com');
VALIDATION_PATTERNS.SLUG.test('my-blog-post');
`
Available Patterns:
| Pattern | Description |
|---------|-------------|
| EMAIL | Email validation |PASSWORD_STRONG
| | Strong password (8+ chars, upper, lower, number, special) |PASSWORD_MEDIUM
| | Medium password (8+ chars, upper, lower, number) |PHONE_INTERNATIONAL
| | International phone |URL
| / URL_STRICT | URL validation |SLUG
| | Kebab-case slug |USERNAME
| | Username (3-30 chars, alphanumeric, _, -) |IPV4
| | IPv4 address |HEX_COLOR
| | Hex color code |DATE_ISO
| | ISO date format |
#### Environment Config (@exyconn/common/shared/config)
`typescript
import { getEnv, getEnvOptional, isProd, isDev, validateEnv } from '@exyconn/common/shared/config';
const port = getEnv('PORT', 3000); // Returns number
const secret = getEnv('JWT_SECRET'); // Required, throws if missing
const optional = getEnvOptional('OPTIONAL'); // Returns undefined if missing
if (isProd()) { / Production only / }
if (isDev()) { / Development only / }
validateEnv(['DATABASE_URL', 'JWT_SECRET']); // Throws with missing vars
`
---
Import: @exyconn/common/data or specific files
#### Countries (@exyconn/common/data/countries)
`typescript
import {
default as countries,
getCountryByCode,
getStatesByCountry,
searchCountries,
getFlag,
} from '@exyconn/common/data/countries';
const usa = getCountryByCode('US');
const states = getStatesByCountry('US');
const matches = searchCountries('united');
const flag = getFlag('US'); // 'πΊπΈ'
`
#### Currencies (@exyconn/common/data/currencies)
`typescript
import { getCurrencyByCode, formatCurrency } from '@exyconn/common/data/currencies';
const usd = getCurrencyByCode('USD');
formatCurrency(1234.56, 'USD'); // '$1,234.56'
`
#### Phone Codes (@exyconn/common/data/phone-codes)
`typescript
import { getPhoneCodeByCountry } from '@exyconn/common/data/phone-codes';
const usCode = getPhoneCodeByCountry('US');
// { country: 'United States', dial_code: '+1', flag: 'πΊπΈ' }
`
#### Timezones (@exyconn/common/data/timezones)
`typescript
import { getTimezoneByCode, searchTimezones } from '@exyconn/common/data/timezones';
const pst = getTimezoneByCode('America/Los_Angeles');
const matches = searchTimezones('india');
`
#### Regex Patterns (@exyconn/common/data/regex)
`typescript
import REGEX from '@exyconn/common/data/regex';
REGEX.EMAIL.BASIC.test('test@example.com');
REGEX.PASSWORD.STRONG.test('Test@123');
REGEX.CREDIT_CARD.VISA.test('4111111111111111');
`
#### Brand Identity (@exyconn/common/data/brand-identity)
`typescript
import { BRANDS, getBrandById, getBrandByDomain, getThemedLogo } from '@exyconn/common/data/brand-identity';
const exyconn = getBrandById('exyconn');
const brand = getBrandByDomain('botify.life');
const logo = getThemedLogo('exyconn', 'dark');
`
---
``
@exyconn/common
βββ /server # Server utilities
β βββ /response # Response helpers
β βββ /enums # Status codes/messages
β βββ /logger # Winston logger
β βββ /db # Database connections
β βββ /middleware # Auth middleware
β βββ /utils # Server utilities
β βββ /configs # Dynamic configurations
βββ /client # Client utilities
β βββ /http # Axios HTTP client
β βββ /hooks # React hooks (50+)
β βββ /logger # Browser logger
β βββ /utils # Client utilities
β βββ /web # Web utilities
βββ /shared # Shared between server/client
β βββ /types # TypeScript types
β βββ /validation # Validation patterns
β βββ /constants # Enums, breakpoints
β βββ /config # Environment config
β βββ /utils # Date-time utilities
βββ /data # Static data modules
βββ /countries # Countries with states/cities
βββ /currencies # World currencies
βββ /phone-codes # Phone codes
βββ /timezones # Timezones
βββ /regex # Regex patterns
βββ /brand-identity # Brand configs
---
All peer dependencies are optional. Install only what you need:
| Package | Version | Required For |
|---------|---------|--------------|
| react | ^17.0.0 \|\| ^18.0.0 \|\| ^19.0.0 | Client hooks |express
| | ^4.18.0 \|\| ^5.0.0 | Server utilities |express-rate-limit
| | ^7.0.0 | Rate limiter configs |cors
| | ^2.8.5 | CORS configs |mongoose
| | ^8.0.0 | Database utilities |winston
| | ^3.11.0 | Server logger |jsonwebtoken
| | ^9.0.0 | Auth middleware |axios
| | ^1.6.0 | HTTP client |date-fns
| | ^3.0.0 | Date utilities |
---
Full TypeScript support with exported types:
`typescript`
import type {
ApiResponse,
CorsConfig,
RateLimitTierConfig,
AuthRequest,
} from '@exyconn/common/server';
---
Requires Node.js >= 18.0.0
---
`bashClone the repository
git clone https://github.com/exyconn/common.git
cd common
$3
| Command | Description |
|---------|-------------|
|
npm run build | Build the package with tsup |
| npm run dev | Watch mode for development |
| npm run test | Run tests with Vitest |
| npm run test:coverage | Run tests with coverage report |
| npm run lint | Lint the codebase |
| npm run type-check | TypeScript type checking |
| npm run docs:dev | Start docs dev server (port 4006) |
| npm run docs:build | Build documentation |
| npm run docs:start | Start production docs server |$3
`bash
Install docs dependencies
npm run docs:installStart documentation site locally
npm run docs:devOpen http://localhost:4006
`$3
`bash
Build the docs Docker image
cd docs
docker build -t common-docs .Run the container
docker run -p 4006:4006 common-docs
`---
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
1. Fork the repository
2. Create your feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'feat: add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature)
5. Open a Pull Request$3
We use Conventional Commits:
-
feat: - New features
- fix: - Bug fixes
- docs: - Documentation changes
- test: - Adding or updating tests
- refactor: - Code refactoring
- chore:` - Maintenance tasks---
MIT Β© Exyconn