@revealui/utils 0.2.0 → 0.3.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/README.md +66 -33
- package/dist/{chunk-KUMOGLHP.js → chunk-U7HNVJST.js} +1 -1
- package/dist/chunk-U7HNVJST.js.map +1 -0
- package/dist/{chunk-QR4YAGWO.js → chunk-X2ZL53G2.js} +9 -4
- package/dist/chunk-X2ZL53G2.js.map +1 -0
- package/dist/{chunk-UEYUNTMY.js → chunk-XSQPIKCD.js} +1 -1
- package/dist/chunk-XSQPIKCD.js.map +1 -0
- package/dist/database/index.js +1 -1
- package/dist/index.js +5 -5
- package/dist/logger/index.d.ts +4 -1
- package/dist/logger/index.js +1 -1
- package/dist/validation/index.js +1 -1
- package/package.json +22 -16
- package/dist/chunk-KUMOGLHP.js.map +0 -1
- package/dist/chunk-QR4YAGWO.js.map +0 -1
- package/dist/chunk-UEYUNTMY.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,60 +1,85 @@
|
|
|
1
1
|
# @revealui/utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Zero-dependency shared utilities for RevealUI — structured logging, database helpers, and validation.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Why This Package Exists
|
|
6
|
+
|
|
7
|
+
Extracting shared utilities to `@revealui/utils` breaks circular dependencies between core, db, and contracts:
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
```
|
|
10
|
+
utils (no dependencies)
|
|
11
|
+
^
|
|
12
|
+
|-- contracts
|
|
13
|
+
|-- db
|
|
14
|
+
|-- core
|
|
15
|
+
```
|
|
8
16
|
|
|
9
|
-
##
|
|
17
|
+
## Features
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- Context propagation
|
|
15
|
-
- Pretty printing for development
|
|
16
|
-
- Remote logging support
|
|
17
|
-
- Child loggers with inherited context
|
|
19
|
+
- **Structured Logger** — Multiple log levels, context propagation, child loggers, pluggable handlers
|
|
20
|
+
- **Database Utilities** — SSL configuration, connection string parsing, security validation
|
|
21
|
+
- **Validation** — Common validation helpers
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @revealui/utils
|
|
27
|
+
```
|
|
23
28
|
|
|
24
29
|
## Usage
|
|
25
30
|
|
|
31
|
+
### Logger
|
|
32
|
+
|
|
26
33
|
```typescript
|
|
27
|
-
// Import logger
|
|
28
34
|
import { logger, createLogger } from '@revealui/utils'
|
|
29
35
|
|
|
30
|
-
//
|
|
36
|
+
// Default logger
|
|
31
37
|
logger.info('Application started')
|
|
38
|
+
logger.error('Something failed', { error })
|
|
32
39
|
|
|
33
|
-
//
|
|
34
|
-
const requestLogger = createLogger({ requestId: '123' })
|
|
40
|
+
// Child logger with context
|
|
41
|
+
const requestLogger = createLogger({ requestId: '123', service: 'api' })
|
|
35
42
|
requestLogger.info('Processing request')
|
|
36
43
|
|
|
37
|
-
//
|
|
44
|
+
// Add custom log handler (e.g. database transport)
|
|
45
|
+
logger.addLogHandler(async (entry) => {
|
|
46
|
+
await db.insert(appLogs).values(entry)
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Database Utilities
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
38
53
|
import { getSSLConfig } from '@revealui/utils/database'
|
|
39
54
|
|
|
40
|
-
|
|
55
|
+
// Auto-configure SSL for PostgreSQL connections
|
|
56
|
+
const sslConfig = getSSLConfig(process.env.POSTGRES_URL)
|
|
41
57
|
```
|
|
42
58
|
|
|
43
|
-
|
|
59
|
+
### Validation
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
contracts → db → core → contracts (circular!)
|
|
61
|
+
```typescript
|
|
62
|
+
import { validate } from '@revealui/utils/validation'
|
|
48
63
|
```
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
## Exports
|
|
66
|
+
|
|
67
|
+
| Subpath | Contents |
|
|
68
|
+
|---------|----------|
|
|
69
|
+
| `@revealui/utils` | Logger and main utilities |
|
|
70
|
+
| `@revealui/utils/logger` | Logger module (createLogger, log levels, handlers) |
|
|
71
|
+
| `@revealui/utils/database` | SSL config, connection string parsing |
|
|
72
|
+
| `@revealui/utils/validation` | Validation helpers |
|
|
73
|
+
|
|
74
|
+
## Logger Features
|
|
75
|
+
|
|
76
|
+
| Feature | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| Log levels | `debug`, `info`, `warn`, `error`, `fatal` |
|
|
79
|
+
| Context propagation | Attach metadata (requestId, service, etc.) |
|
|
80
|
+
| Child loggers | Inherit parent context with additional fields |
|
|
81
|
+
| Log handlers | Pluggable async handlers (DB, remote, etc.) |
|
|
82
|
+
| Pretty printing | Colored output in development |
|
|
58
83
|
|
|
59
84
|
## Development
|
|
60
85
|
|
|
@@ -65,10 +90,18 @@ pnpm build
|
|
|
65
90
|
# Type check
|
|
66
91
|
pnpm typecheck
|
|
67
92
|
|
|
93
|
+
# Run tests
|
|
94
|
+
pnpm test
|
|
95
|
+
|
|
68
96
|
# Watch mode
|
|
69
97
|
pnpm dev
|
|
70
98
|
```
|
|
71
99
|
|
|
100
|
+
## Related
|
|
101
|
+
|
|
102
|
+
- [Core Package](../core/README.md) — Uses utils for logging and error handling
|
|
103
|
+
- [DB Package](../db/README.md) — Uses utils for SSL and connection helpers
|
|
104
|
+
|
|
72
105
|
## License
|
|
73
106
|
|
|
74
107
|
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/database/ssl-config.ts"],"sourcesContent":["/**\n * SSL Configuration Utility\n *\n * Provides centralized SSL configuration for PostgreSQL connections based on\n * connection string sslmode parameter and environment variables.\n *\n * Extracted from @revealui/core to break circular dependencies\n *\n * @module ssl-config\n */\n\n/**\n * PostgreSQL SSL configuration options\n */\nexport interface SSLConfig {\n rejectUnauthorized: boolean;\n}\n\n/**\n * Determines the appropriate SSL configuration for a PostgreSQL connection\n * based on the connection string's sslmode parameter.\n *\n * SSL Modes (aligned with PostgreSQL libpq standards):\n * - `disable`: No SSL connection\n * - `require`: SSL connection with certificate verification\n * - `verify-full`: Full SSL verification (same as require in pg v9+)\n * - `verify-ca`: CA verification (treated as verify-full)\n *\n * Environment Override:\n * - `DATABASE_SSL_REJECT_UNAUTHORIZED=false`: Force skip certificate verification\n * (DEVELOPMENT ONLY - use for self-signed certificates in local environments)\n *\n * @param connectionString - PostgreSQL connection string (may include sslmode parameter)\n * @returns SSL configuration object or false to disable SSL\n *\n * @example\n * ```typescript\n * // Connection string with SSL required\n * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')\n * // Returns: { rejectUnauthorized: true }\n *\n * // Connection string without SSL\n * const ssl = getSSLConfig('postgresql://localhost:5432/db')\n * // Returns: false\n *\n * // Development override for self-signed certificates\n * process.env.DATABASE_SSL_REJECT_UNAUTHORIZED = 'false'\n * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')\n * // Returns: { rejectUnauthorized: false }\n * ```\n */\nexport function getSSLConfig(connectionString: string): SSLConfig | false {\n try {\n // Parse connection string\n const url = new URL(connectionString);\n const sslmode = url.searchParams.get('sslmode');\n\n // No SSL if sslmode is explicitly disabled or not specified\n if (!sslmode || sslmode === 'disable') {\n return false;\n }\n\n // Environment override for local development with self-signed certificates\n // This should ONLY be used in development environments\n // SECURITY: Explicitly check NODE_ENV to prevent accidental use in production\n if (\n process.env.NODE_ENV !== 'production' &&\n process.env.DATABASE_SSL_REJECT_UNAUTHORIZED === 'false'\n ) {\n return { rejectUnauthorized: false };\n }\n\n // Default: verify certificates (security best practice)\n // Handles: require, verify-full, verify-ca, prefer\n // In pg v9+, these will all use verify-full semantics\n return { rejectUnauthorized: true };\n } catch (_error) {\n // If URL parsing fails, assume local connection without SSL\n // Silently fallback to no SSL for invalid connection strings\n return false;\n }\n}\n\n/**\n * Validates SSL configuration for production environments.\n * Warns if insecure SSL settings are detected in production.\n *\n * @param connectionString - PostgreSQL connection string\n * @param environment - Current environment (e.g., 'production', 'development')\n */\nexport function validateSSLConfig(\n connectionString: string,\n environment: string = process.env.NODE_ENV || 'development',\n): boolean {\n const sslConfig = getSSLConfig(connectionString);\n\n // Check if SSL is disabled in production\n if (environment === 'production' && sslConfig === false) {\n return false; // SSL disabled in production - security risk\n }\n\n // Check if certificate verification is disabled in production\n if (environment === 'production' && sslConfig && !sslConfig.rejectUnauthorized) {\n return false; // Certificate verification disabled - security risk\n }\n\n return true; // SSL configuration is valid\n}\n"],"mappings":";AAmDO,SAAS,aAAa,kBAA6C;AACxE,MAAI;AAEF,UAAM,MAAM,IAAI,IAAI,gBAAgB;AACpC,UAAM,UAAU,IAAI,aAAa,IAAI,SAAS;AAG9C,QAAI,CAAC,WAAW,YAAY,WAAW;AACrC,aAAO;AAAA,IACT;AAKA,QACE,QAAQ,IAAI,aAAa,gBACzB,QAAQ,IAAI,qCAAqC,SACjD;AACA,aAAO,EAAE,oBAAoB,MAAM;AAAA,IACrC;AAKA,WAAO,EAAE,oBAAoB,KAAK;AAAA,EACpC,SAAS,QAAQ;AAGf,WAAO;AAAA,EACT;AACF;AASO,SAAS,kBACd,kBACA,cAAsB,QAAQ,IAAI,YAAY,eACrC;AACT,QAAM,YAAY,aAAa,gBAAgB;AAG/C,MAAI,gBAAgB,gBAAgB,cAAc,OAAO;AACvD,WAAO;AAAA,EACT;AAGA,MAAI,gBAAgB,gBAAgB,aAAa,CAAC,UAAU,oBAAoB;AAC9E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -155,11 +155,15 @@ var Logger = class _Logger {
|
|
|
155
155
|
outputFile(entry) {
|
|
156
156
|
console.log(JSON.stringify(entry));
|
|
157
157
|
}
|
|
158
|
+
/** Circuit breaker state for remote transport */
|
|
159
|
+
remoteFailures = 0;
|
|
160
|
+
maxRemoteFailures = 5;
|
|
158
161
|
/**
|
|
159
|
-
* Output to remote service
|
|
162
|
+
* Output to remote service (with circuit breaker)
|
|
160
163
|
*/
|
|
161
164
|
async outputRemote(entry) {
|
|
162
165
|
if (!this.config.remoteUrl) return;
|
|
166
|
+
if (this.remoteFailures >= this.maxRemoteFailures) return;
|
|
163
167
|
try {
|
|
164
168
|
await fetch(this.config.remoteUrl, {
|
|
165
169
|
method: "POST",
|
|
@@ -168,8 +172,9 @@ var Logger = class _Logger {
|
|
|
168
172
|
},
|
|
169
173
|
body: JSON.stringify(entry)
|
|
170
174
|
});
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
this.remoteFailures = 0;
|
|
176
|
+
} catch {
|
|
177
|
+
this.remoteFailures++;
|
|
173
178
|
this.outputConsole(entry);
|
|
174
179
|
}
|
|
175
180
|
}
|
|
@@ -261,4 +266,4 @@ export {
|
|
|
261
266
|
logAudit,
|
|
262
267
|
logQuery
|
|
263
268
|
};
|
|
264
|
-
//# sourceMappingURL=chunk-
|
|
269
|
+
//# sourceMappingURL=chunk-X2ZL53G2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logger/index.ts"],"sourcesContent":["/**\n * Structured Logging Infrastructure\n *\n * Provides consistent, structured logging across the application\n * Extracted from @revealui/core to break circular dependencies\n */\n\n/* console-allowed */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport interface LogContext {\n [key: string]: unknown;\n userId?: string;\n requestId?: string;\n sessionId?: string;\n traceId?: string;\n spanId?: string;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n context?: LogContext;\n error?: {\n name: string;\n message: string;\n stack?: string;\n cause?: unknown;\n };\n metadata?: Record<string, unknown>;\n}\n\nexport interface LoggerConfig {\n level?: LogLevel;\n enabled?: boolean;\n pretty?: boolean;\n includeTimestamp?: boolean;\n includeStack?: boolean;\n destination?: 'console' | 'file' | 'remote';\n remoteUrl?: string;\n onLog?: (entry: LogEntry) => void;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n};\n\nexport class Logger {\n private config: Required<LoggerConfig>;\n private context: LogContext = {};\n private extraHandlers: Array<(entry: LogEntry) => void> = [];\n\n constructor(config: LoggerConfig = {}) {\n this.config = {\n level: config.level || 'info',\n enabled: config.enabled !== undefined ? config.enabled : true,\n pretty: config.pretty !== undefined ? config.pretty : process.env.NODE_ENV !== 'production',\n includeTimestamp: config.includeTimestamp !== undefined ? config.includeTimestamp : true,\n includeStack: config.includeStack !== undefined ? config.includeStack : true,\n destination: config.destination || 'console',\n remoteUrl: config.remoteUrl || '',\n onLog:\n config.onLog ||\n (() => {\n // No-op function\n }),\n };\n }\n\n /**\n * Register an additional log handler (e.g. DB transport).\n * Called after every log entry, fire-and-forget. Must not throw.\n */\n addLogHandler(handler: (entry: LogEntry) => void): void {\n this.extraHandlers.push(handler);\n }\n\n /**\n * Set global context\n */\n setContext(context: LogContext): void {\n this.context = { ...this.context, ...context };\n }\n\n /**\n * Clear global context\n */\n clearContext(): void {\n this.context = {};\n }\n\n /**\n * Debug log\n */\n debug(message: string, context?: LogContext): void {\n this.log('debug', message, context);\n }\n\n /**\n * Info log\n */\n info(message: string, context?: LogContext): void {\n this.log('info', message, context);\n }\n\n /**\n * Warning log\n */\n warn(message: string, context?: LogContext): void {\n this.log('warn', message, context);\n }\n\n /**\n * Error log\n */\n error(message: string, error?: Error, context?: LogContext): void {\n const errorContext = error\n ? {\n error: {\n name: error.name,\n message: error.message,\n stack: this.config.includeStack ? error.stack : undefined,\n cause: error.cause,\n },\n }\n : {};\n\n this.log('error', message, { ...context, ...errorContext });\n }\n\n /**\n * Fatal log\n */\n fatal(message: string, error?: Error, context?: LogContext): void {\n const errorContext = error\n ? {\n error: {\n name: error.name,\n message: error.message,\n stack: this.config.includeStack ? error.stack : undefined,\n cause: error.cause,\n },\n }\n : {};\n\n this.log('fatal', message, { ...context, ...errorContext });\n }\n\n /**\n * Core logging method\n */\n private log(level: LogLevel, message: string, context?: LogContext): void {\n if (!this.config.enabled) return;\n\n if (LOG_LEVELS[level] < LOG_LEVELS[this.config.level]) {\n return;\n }\n\n const entry: LogEntry = {\n timestamp: this.config.includeTimestamp ? new Date().toISOString() : '',\n level,\n message,\n context: { ...this.context, ...context },\n };\n\n // Extract error if in context\n if (entry.context?.error) {\n entry.error = entry.context.error as LogEntry['error'];\n entry.context.error = undefined;\n }\n\n // Call custom handler\n this.config.onLog(entry);\n\n // Call additional handlers (e.g. DB transport)\n for (const handler of this.extraHandlers) {\n handler(entry);\n }\n\n // Output log\n this.output(entry);\n }\n\n /**\n * Output log entry\n */\n private output(entry: LogEntry): void {\n switch (this.config.destination) {\n case 'console':\n this.outputConsole(entry);\n break;\n case 'file':\n this.outputFile(entry);\n break;\n case 'remote':\n this.outputRemote(entry);\n break;\n }\n }\n\n /**\n * Output to console\n */\n private outputConsole(entry: LogEntry): void {\n const output = this.config.pretty ? this.formatPretty(entry) : JSON.stringify(entry);\n\n switch (entry.level) {\n case 'debug':\n console.debug(output);\n break;\n case 'info':\n console.info(output);\n break;\n case 'warn':\n console.warn(output);\n break;\n case 'error':\n case 'fatal':\n console.error(output);\n break;\n }\n }\n\n /**\n * Output to file\n */\n private outputFile(entry: LogEntry): void {\n // Would require fs module - placeholder for server-side logging\n console.log(JSON.stringify(entry));\n }\n\n /** Circuit breaker state for remote transport */\n private remoteFailures = 0;\n private readonly maxRemoteFailures = 5;\n\n /**\n * Output to remote service (with circuit breaker)\n */\n private async outputRemote(entry: LogEntry): Promise<void> {\n if (!this.config.remoteUrl) return;\n if (this.remoteFailures >= this.maxRemoteFailures) return;\n\n try {\n await fetch(this.config.remoteUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(entry),\n });\n this.remoteFailures = 0;\n } catch {\n this.remoteFailures++;\n this.outputConsole(entry);\n }\n }\n\n /**\n * Format log entry for pretty printing\n */\n private formatPretty(entry: LogEntry): string {\n const levelColors: Record<LogLevel, string> = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n fatal: '\\x1b[35m', // Magenta\n };\n\n const reset = '\\x1b[0m';\n const color = levelColors[entry.level];\n\n const parts: string[] = [];\n\n if (entry.timestamp) {\n parts.push(`\\x1b[90m${entry.timestamp}${reset}`);\n }\n\n parts.push(`${color}${entry.level.toUpperCase()}${reset}`);\n parts.push(entry.message);\n\n if (entry.context && Object.keys(entry.context).length > 0) {\n parts.push(JSON.stringify(entry.context, null, 2));\n }\n\n if (entry.error) {\n parts.push(`\\n${color}Error: ${entry.error.message}${reset}`);\n if (entry.error.stack) {\n parts.push(entry.error.stack);\n }\n }\n\n return parts.join(' ');\n }\n\n /**\n * Create child logger with additional context\n */\n child(context: LogContext): Logger {\n const childLogger = new Logger(this.config);\n childLogger.setContext({ ...this.context, ...context });\n // Share parent's extra handlers so DB transport applies to child loggers too\n for (const handler of this.extraHandlers) {\n childLogger.addLogHandler(handler);\n }\n return childLogger;\n }\n}\n\n/**\n * Default logger instance\n */\nexport const logger = new Logger({\n level: (process.env.LOG_LEVEL as LogLevel) || 'info',\n pretty: process.env.NODE_ENV !== 'production',\n});\n\n/**\n * Create logger with context\n */\nexport function createLogger(context: LogContext): Logger {\n return logger.child(context);\n}\n\n/**\n * Error logger\n */\nexport function logError(error: Error, context?: LogContext): void {\n logger.error(error.message, error, context);\n}\n\n/**\n * Audit logger for security-sensitive operations\n */\nexport function logAudit(action: string, context?: LogContext): void {\n logger.info(`Audit: ${action}`, {\n ...context,\n audit: true,\n action,\n });\n}\n\n/**\n * Database query logger\n */\nexport function logQuery(query: string, duration: number, context?: LogContext): void {\n logger.debug('Database query', {\n ...context,\n query,\n duration,\n });\n\n if (duration > 1000) {\n logger.warn('Slow query detected', {\n ...context,\n query,\n duration,\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation/password-schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Password validation schema\n *\n * Requirements:\n * - Minimum 8 characters\n * - Maximum 128 characters\n * - At least one uppercase letter\n * - At least one lowercase letter\n * - At least one number\n */\nexport const passwordSchema = z\n .string()\n .min(8, 'Password must be at least 8 characters')\n .max(128, 'Password is too long')\n .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')\n .regex(/[a-z]/, 'Password must contain at least one lowercase letter')\n .regex(/[0-9]/, 'Password must contain at least one number');\n\nexport type Password = z.infer<typeof passwordSchema>;\n"],"mappings":";AAAA,SAAS,SAAS;AAYX,IAAM,iBAAiB,EAC3B,OAAO,EACP,IAAI,GAAG,wCAAwC,EAC/C,IAAI,KAAK,sBAAsB,EAC/B,MAAM,SAAS,qDAAqD,EACpE,MAAM,SAAS,qDAAqD,EACpE,MAAM,SAAS,2CAA2C;","names":[]}
|
package/dist/database/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSSLConfig,
|
|
3
3
|
validateSSLConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-U7HNVJST.js";
|
|
5
|
+
import {
|
|
6
|
+
passwordSchema
|
|
7
|
+
} from "./chunk-XSQPIKCD.js";
|
|
5
8
|
import {
|
|
6
9
|
Logger,
|
|
7
10
|
createLogger,
|
|
@@ -9,10 +12,7 @@ import {
|
|
|
9
12
|
logError,
|
|
10
13
|
logQuery,
|
|
11
14
|
logger
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
passwordSchema
|
|
15
|
-
} from "./chunk-UEYUNTMY.js";
|
|
15
|
+
} from "./chunk-X2ZL53G2.js";
|
|
16
16
|
export {
|
|
17
17
|
Logger,
|
|
18
18
|
createLogger,
|
package/dist/logger/index.d.ts
CHANGED
|
@@ -90,8 +90,11 @@ declare class Logger {
|
|
|
90
90
|
* Output to file
|
|
91
91
|
*/
|
|
92
92
|
private outputFile;
|
|
93
|
+
/** Circuit breaker state for remote transport */
|
|
94
|
+
private remoteFailures;
|
|
95
|
+
private readonly maxRemoteFailures;
|
|
93
96
|
/**
|
|
94
|
-
* Output to remote service
|
|
97
|
+
* Output to remote service (with circuit breaker)
|
|
95
98
|
*/
|
|
96
99
|
private outputRemote;
|
|
97
100
|
/**
|
package/dist/logger/index.js
CHANGED
package/dist/validation/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared utilities for RevealUI - logger, SSL config, and common helpers",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"zod": "^4.3.6"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@types/node": "^25.3.0",
|
|
11
|
+
"tsup": "^8.5.1",
|
|
12
|
+
"typescript": "^5.9.3",
|
|
13
|
+
"vitest": "^4.0.18",
|
|
14
|
+
"dev": "0.0.1"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=24.13.0"
|
|
18
|
+
},
|
|
7
19
|
"exports": {
|
|
8
20
|
".": {
|
|
9
21
|
"types": "./dist/index.d.ts",
|
|
@@ -25,28 +37,22 @@
|
|
|
25
37
|
"files": [
|
|
26
38
|
"dist"
|
|
27
39
|
],
|
|
40
|
+
"main": "./dist/index.js",
|
|
28
41
|
"publishConfig": {
|
|
29
42
|
"access": "public",
|
|
30
43
|
"registry": "https://registry.npmjs.org"
|
|
31
44
|
},
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@types/node": "^25.3.0",
|
|
37
|
-
"tsup": "^8.5.1",
|
|
38
|
-
"typescript": "^5.9.3",
|
|
39
|
-
"vitest": "^4.0.18",
|
|
40
|
-
"dev": "0.0.1"
|
|
41
|
-
},
|
|
45
|
+
"type": "module",
|
|
46
|
+
"types": "./dist/index.d.ts",
|
|
42
47
|
"scripts": {
|
|
43
48
|
"build": "tsup",
|
|
49
|
+
"clean": "rm -rf dist",
|
|
44
50
|
"dev": "tsup --watch",
|
|
45
|
-
"typecheck": "tsc --noEmit",
|
|
46
|
-
"test": "vitest run --passWithNoTests",
|
|
47
|
-
"test:watch": "vitest",
|
|
48
51
|
"lint": "biome check .",
|
|
49
52
|
"lint:fix": "biome check --write .",
|
|
50
|
-
"
|
|
53
|
+
"test": "vitest run --passWithNoTests",
|
|
54
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=json-summary --coverage.reporter=html --coverage.reporter=text --passWithNoTests",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --noEmit"
|
|
51
57
|
}
|
|
52
58
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/database/ssl-config.ts"],"sourcesContent":["/**\n * SSL Configuration Utility\n *\n * Provides centralized SSL configuration for PostgreSQL connections based on\n * connection string sslmode parameter and environment variables.\n *\n * Extracted from @revealui/core to break circular dependencies\n *\n * @module ssl-config\n */\n\n/**\n * PostgreSQL SSL configuration options\n */\nexport interface SSLConfig {\n rejectUnauthorized: boolean\n}\n\n/**\n * Determines the appropriate SSL configuration for a PostgreSQL connection\n * based on the connection string's sslmode parameter.\n *\n * SSL Modes (aligned with PostgreSQL libpq standards):\n * - `disable`: No SSL connection\n * - `require`: SSL connection with certificate verification\n * - `verify-full`: Full SSL verification (same as require in pg v9+)\n * - `verify-ca`: CA verification (treated as verify-full)\n *\n * Environment Override:\n * - `DATABASE_SSL_REJECT_UNAUTHORIZED=false`: Force skip certificate verification\n * (DEVELOPMENT ONLY - use for self-signed certificates in local environments)\n *\n * @param connectionString - PostgreSQL connection string (may include sslmode parameter)\n * @returns SSL configuration object or false to disable SSL\n *\n * @example\n * ```typescript\n * // Connection string with SSL required\n * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')\n * // Returns: { rejectUnauthorized: true }\n *\n * // Connection string without SSL\n * const ssl = getSSLConfig('postgresql://localhost:5432/db')\n * // Returns: false\n *\n * // Development override for self-signed certificates\n * process.env.DATABASE_SSL_REJECT_UNAUTHORIZED = 'false'\n * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')\n * // Returns: { rejectUnauthorized: false }\n * ```\n */\nexport function getSSLConfig(connectionString: string): SSLConfig | false {\n try {\n // Parse connection string\n const url = new URL(connectionString)\n const sslmode = url.searchParams.get('sslmode')\n\n // No SSL if sslmode is explicitly disabled or not specified\n if (!sslmode || sslmode === 'disable') {\n return false\n }\n\n // Environment override for local development with self-signed certificates\n // This should ONLY be used in development environments\n // SECURITY: Explicitly check NODE_ENV to prevent accidental use in production\n if (\n process.env.NODE_ENV !== 'production' &&\n process.env.DATABASE_SSL_REJECT_UNAUTHORIZED === 'false'\n ) {\n return { rejectUnauthorized: false }\n }\n\n // Default: verify certificates (security best practice)\n // Handles: require, verify-full, verify-ca, prefer\n // In pg v9+, these will all use verify-full semantics\n return { rejectUnauthorized: true }\n } catch (_error) {\n // If URL parsing fails, assume local connection without SSL\n // Silently fallback to no SSL for invalid connection strings\n return false\n }\n}\n\n/**\n * Validates SSL configuration for production environments.\n * Warns if insecure SSL settings are detected in production.\n *\n * @param connectionString - PostgreSQL connection string\n * @param environment - Current environment (e.g., 'production', 'development')\n */\nexport function validateSSLConfig(\n connectionString: string,\n environment: string = process.env.NODE_ENV || 'development',\n): boolean {\n const sslConfig = getSSLConfig(connectionString)\n\n // Check if SSL is disabled in production\n if (environment === 'production' && sslConfig === false) {\n return false // SSL disabled in production - security risk\n }\n\n // Check if certificate verification is disabled in production\n if (environment === 'production' && sslConfig && !sslConfig.rejectUnauthorized) {\n return false // Certificate verification disabled - security risk\n }\n\n return true // SSL configuration is valid\n}\n"],"mappings":";AAmDO,SAAS,aAAa,kBAA6C;AACxE,MAAI;AAEF,UAAM,MAAM,IAAI,IAAI,gBAAgB;AACpC,UAAM,UAAU,IAAI,aAAa,IAAI,SAAS;AAG9C,QAAI,CAAC,WAAW,YAAY,WAAW;AACrC,aAAO;AAAA,IACT;AAKA,QACE,QAAQ,IAAI,aAAa,gBACzB,QAAQ,IAAI,qCAAqC,SACjD;AACA,aAAO,EAAE,oBAAoB,MAAM;AAAA,IACrC;AAKA,WAAO,EAAE,oBAAoB,KAAK;AAAA,EACpC,SAAS,QAAQ;AAGf,WAAO;AAAA,EACT;AACF;AASO,SAAS,kBACd,kBACA,cAAsB,QAAQ,IAAI,YAAY,eACrC;AACT,QAAM,YAAY,aAAa,gBAAgB;AAG/C,MAAI,gBAAgB,gBAAgB,cAAc,OAAO;AACvD,WAAO;AAAA,EACT;AAGA,MAAI,gBAAgB,gBAAgB,aAAa,CAAC,UAAU,oBAAoB;AAC9E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logger/index.ts"],"sourcesContent":["/**\n * Structured Logging Infrastructure\n *\n * Provides consistent, structured logging across the application\n * Extracted from @revealui/core to break circular dependencies\n */\n\n/* eslint-disable no-console */\n/* console-allowed */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n\nexport interface LogContext {\n [key: string]: unknown\n userId?: string\n requestId?: string\n sessionId?: string\n traceId?: string\n spanId?: string\n}\n\nexport interface LogEntry {\n timestamp: string\n level: LogLevel\n message: string\n context?: LogContext\n error?: {\n name: string\n message: string\n stack?: string\n cause?: unknown\n }\n metadata?: Record<string, unknown>\n}\n\nexport interface LoggerConfig {\n level?: LogLevel\n enabled?: boolean\n pretty?: boolean\n includeTimestamp?: boolean\n includeStack?: boolean\n destination?: 'console' | 'file' | 'remote'\n remoteUrl?: string\n onLog?: (entry: LogEntry) => void\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n fatal: 4,\n}\n\nexport class Logger {\n private config: Required<LoggerConfig>\n private context: LogContext = {}\n private extraHandlers: Array<(entry: LogEntry) => void> = []\n\n constructor(config: LoggerConfig = {}) {\n this.config = {\n level: config.level || 'info',\n enabled: config.enabled !== undefined ? config.enabled : true,\n pretty: config.pretty !== undefined ? config.pretty : process.env.NODE_ENV !== 'production',\n includeTimestamp: config.includeTimestamp !== undefined ? config.includeTimestamp : true,\n includeStack: config.includeStack !== undefined ? config.includeStack : true,\n destination: config.destination || 'console',\n remoteUrl: config.remoteUrl || '',\n onLog:\n config.onLog ||\n (() => {\n // No-op function\n }),\n }\n }\n\n /**\n * Register an additional log handler (e.g. DB transport).\n * Called after every log entry, fire-and-forget. Must not throw.\n */\n addLogHandler(handler: (entry: LogEntry) => void): void {\n this.extraHandlers.push(handler)\n }\n\n /**\n * Set global context\n */\n setContext(context: LogContext): void {\n this.context = { ...this.context, ...context }\n }\n\n /**\n * Clear global context\n */\n clearContext(): void {\n this.context = {}\n }\n\n /**\n * Debug log\n */\n debug(message: string, context?: LogContext): void {\n this.log('debug', message, context)\n }\n\n /**\n * Info log\n */\n info(message: string, context?: LogContext): void {\n this.log('info', message, context)\n }\n\n /**\n * Warning log\n */\n warn(message: string, context?: LogContext): void {\n this.log('warn', message, context)\n }\n\n /**\n * Error log\n */\n error(message: string, error?: Error, context?: LogContext): void {\n const errorContext = error\n ? {\n error: {\n name: error.name,\n message: error.message,\n stack: this.config.includeStack ? error.stack : undefined,\n cause: error.cause,\n },\n }\n : {}\n\n this.log('error', message, { ...context, ...errorContext })\n }\n\n /**\n * Fatal log\n */\n fatal(message: string, error?: Error, context?: LogContext): void {\n const errorContext = error\n ? {\n error: {\n name: error.name,\n message: error.message,\n stack: this.config.includeStack ? error.stack : undefined,\n cause: error.cause,\n },\n }\n : {}\n\n this.log('fatal', message, { ...context, ...errorContext })\n }\n\n /**\n * Core logging method\n */\n private log(level: LogLevel, message: string, context?: LogContext): void {\n if (!this.config.enabled) return\n\n if (LOG_LEVELS[level] < LOG_LEVELS[this.config.level]) {\n return\n }\n\n const entry: LogEntry = {\n timestamp: this.config.includeTimestamp ? new Date().toISOString() : '',\n level,\n message,\n context: { ...this.context, ...context },\n }\n\n // Extract error if in context\n if (entry.context?.error) {\n entry.error = entry.context.error as LogEntry['error']\n entry.context.error = undefined\n }\n\n // Call custom handler\n this.config.onLog(entry)\n\n // Call additional handlers (e.g. DB transport)\n for (const handler of this.extraHandlers) {\n handler(entry)\n }\n\n // Output log\n this.output(entry)\n }\n\n /**\n * Output log entry\n */\n private output(entry: LogEntry): void {\n switch (this.config.destination) {\n case 'console':\n this.outputConsole(entry)\n break\n case 'file':\n this.outputFile(entry)\n break\n case 'remote':\n this.outputRemote(entry)\n break\n }\n }\n\n /**\n * Output to console\n */\n private outputConsole(entry: LogEntry): void {\n const output = this.config.pretty ? this.formatPretty(entry) : JSON.stringify(entry)\n\n switch (entry.level) {\n case 'debug':\n console.debug(output)\n break\n case 'info':\n console.info(output)\n break\n case 'warn':\n console.warn(output)\n break\n case 'error':\n case 'fatal':\n console.error(output)\n break\n }\n }\n\n /**\n * Output to file\n */\n private outputFile(entry: LogEntry): void {\n // Would require fs module - placeholder for server-side logging\n console.log(JSON.stringify(entry))\n }\n\n /**\n * Output to remote service\n */\n private async outputRemote(entry: LogEntry): Promise<void> {\n if (!this.config.remoteUrl) return\n\n try {\n await fetch(this.config.remoteUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(entry),\n })\n } catch (error) {\n // Fallback to console if remote fails\n console.error('Failed to send log to remote:', error)\n this.outputConsole(entry)\n }\n }\n\n /**\n * Format log entry for pretty printing\n */\n private formatPretty(entry: LogEntry): string {\n const levelColors: Record<LogLevel, string> = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n fatal: '\\x1b[35m', // Magenta\n }\n\n const reset = '\\x1b[0m'\n const color = levelColors[entry.level]\n\n const parts: string[] = []\n\n if (entry.timestamp) {\n parts.push(`\\x1b[90m${entry.timestamp}${reset}`)\n }\n\n parts.push(`${color}${entry.level.toUpperCase()}${reset}`)\n parts.push(entry.message)\n\n if (entry.context && Object.keys(entry.context).length > 0) {\n parts.push(JSON.stringify(entry.context, null, 2))\n }\n\n if (entry.error) {\n parts.push(`\\n${color}Error: ${entry.error.message}${reset}`)\n if (entry.error.stack) {\n parts.push(entry.error.stack)\n }\n }\n\n return parts.join(' ')\n }\n\n /**\n * Create child logger with additional context\n */\n child(context: LogContext): Logger {\n const childLogger = new Logger(this.config)\n childLogger.setContext({ ...this.context, ...context })\n // Share parent's extra handlers so DB transport applies to child loggers too\n for (const handler of this.extraHandlers) {\n childLogger.addLogHandler(handler)\n }\n return childLogger\n }\n}\n\n/**\n * Default logger instance\n */\nexport const logger = new Logger({\n level: (process.env.LOG_LEVEL as LogLevel) || 'info',\n pretty: process.env.NODE_ENV !== 'production',\n})\n\n/**\n * Create logger with context\n */\nexport function createLogger(context: LogContext): Logger {\n return logger.child(context)\n}\n\n/**\n * Error logger\n */\nexport function logError(error: Error, context?: LogContext): void {\n logger.error(error.message, error, context)\n}\n\n/**\n * Audit logger for security-sensitive operations\n */\nexport function logAudit(action: string, context?: LogContext): void {\n logger.info(`Audit: ${action}`, {\n ...context,\n audit: true,\n action,\n })\n}\n\n/**\n * Database query logger\n */\nexport function logQuery(query: string, duration: number, context?: LogContext): void {\n logger.debug('Database query', {\n ...context,\n query,\n duration,\n })\n\n if (duration > 1000) {\n logger.warn('Slow query detected', {\n ...context,\n query,\n duration,\n })\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/validation/password-schema.ts"],"sourcesContent":["import { z } from 'zod'\n\n/**\n * Password validation schema\n *\n * Requirements:\n * - Minimum 8 characters\n * - Maximum 128 characters\n * - At least one uppercase letter\n * - At least one lowercase letter\n * - At least one number\n */\nexport const passwordSchema = z\n .string()\n .min(8, 'Password must be at least 8 characters')\n .max(128, 'Password is too long')\n .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')\n .regex(/[a-z]/, 'Password must contain at least one lowercase letter')\n .regex(/[0-9]/, 'Password must contain at least one number')\n\nexport type Password = z.infer<typeof passwordSchema>\n"],"mappings":";AAAA,SAAS,SAAS;AAYX,IAAM,iBAAiB,EAC3B,OAAO,EACP,IAAI,GAAG,wCAAwC,EAC/C,IAAI,KAAK,sBAAsB,EAC/B,MAAM,SAAS,qDAAqD,EACpE,MAAM,SAAS,qDAAqD,EACpE,MAAM,SAAS,2CAA2C;","names":[]}
|