@revealui/utils 0.2.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RevealUI Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @revealui/utils
2
+
3
+ Shared utilities for RevealUI - logger, SSL config, and common helpers.
4
+
5
+ ## Purpose
6
+
7
+ This package contains zero-dependency utilities that are used across multiple packages in the RevealUI monorepo. It was created to break circular dependencies between `@revealui/core`, `@revealui/db`, and `@revealui/contracts`.
8
+
9
+ ## Contents
10
+
11
+ ### Logger
12
+ Structured logging infrastructure with support for:
13
+ - Multiple log levels (debug, info, warn, error, fatal)
14
+ - Context propagation
15
+ - Pretty printing for development
16
+ - Remote logging support
17
+ - Child loggers with inherited context
18
+
19
+ ### Database Utilities
20
+ - SSL configuration for PostgreSQL connections
21
+ - Connection string parsing
22
+ - Security validation for production environments
23
+
24
+ ## Usage
25
+
26
+ ```typescript
27
+ // Import logger
28
+ import { logger, createLogger } from '@revealui/utils'
29
+
30
+ // Use default logger
31
+ logger.info('Application started')
32
+
33
+ // Create child logger with context
34
+ const requestLogger = createLogger({ requestId: '123' })
35
+ requestLogger.info('Processing request')
36
+
37
+ // Import database utilities
38
+ import { getSSLConfig } from '@revealui/utils/database'
39
+
40
+ const sslConfig = getSSLConfig(process.env.DATABASE_URL)
41
+ ```
42
+
43
+ ## Why This Package Exists
44
+
45
+ Previously, these utilities lived in `@revealui/core`, which created a circular dependency:
46
+ ```
47
+ contracts → db → core → contracts (circular!)
48
+ ```
49
+
50
+ By extracting shared utilities to `@revealui/utils`, we get a clean dependency graph:
51
+ ```
52
+ utils (no dependencies)
53
+
54
+ ├── contracts
55
+ ├── db
56
+ └── core
57
+ ```
58
+
59
+ ## Development
60
+
61
+ ```bash
62
+ # Build
63
+ pnpm build
64
+
65
+ # Type check
66
+ pnpm typecheck
67
+
68
+ # Watch mode
69
+ pnpm dev
70
+ ```
71
+
72
+ ## License
73
+
74
+ MIT
@@ -0,0 +1,32 @@
1
+ // src/database/ssl-config.ts
2
+ function getSSLConfig(connectionString) {
3
+ try {
4
+ const url = new URL(connectionString);
5
+ const sslmode = url.searchParams.get("sslmode");
6
+ if (!sslmode || sslmode === "disable") {
7
+ return false;
8
+ }
9
+ if (process.env.NODE_ENV !== "production" && process.env.DATABASE_SSL_REJECT_UNAUTHORIZED === "false") {
10
+ return { rejectUnauthorized: false };
11
+ }
12
+ return { rejectUnauthorized: true };
13
+ } catch (_error) {
14
+ return false;
15
+ }
16
+ }
17
+ function validateSSLConfig(connectionString, environment = process.env.NODE_ENV || "development") {
18
+ const sslConfig = getSSLConfig(connectionString);
19
+ if (environment === "production" && sslConfig === false) {
20
+ return false;
21
+ }
22
+ if (environment === "production" && sslConfig && !sslConfig.rejectUnauthorized) {
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+
28
+ export {
29
+ getSSLConfig,
30
+ validateSSLConfig
31
+ };
32
+ //# sourceMappingURL=chunk-KUMOGLHP.js.map
@@ -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":[]}
@@ -0,0 +1,264 @@
1
+ // src/logger/index.ts
2
+ var LOG_LEVELS = {
3
+ debug: 0,
4
+ info: 1,
5
+ warn: 2,
6
+ error: 3,
7
+ fatal: 4
8
+ };
9
+ var Logger = class _Logger {
10
+ config;
11
+ context = {};
12
+ extraHandlers = [];
13
+ constructor(config = {}) {
14
+ this.config = {
15
+ level: config.level || "info",
16
+ enabled: config.enabled !== void 0 ? config.enabled : true,
17
+ pretty: config.pretty !== void 0 ? config.pretty : process.env.NODE_ENV !== "production",
18
+ includeTimestamp: config.includeTimestamp !== void 0 ? config.includeTimestamp : true,
19
+ includeStack: config.includeStack !== void 0 ? config.includeStack : true,
20
+ destination: config.destination || "console",
21
+ remoteUrl: config.remoteUrl || "",
22
+ onLog: config.onLog || (() => {
23
+ })
24
+ };
25
+ }
26
+ /**
27
+ * Register an additional log handler (e.g. DB transport).
28
+ * Called after every log entry, fire-and-forget. Must not throw.
29
+ */
30
+ addLogHandler(handler) {
31
+ this.extraHandlers.push(handler);
32
+ }
33
+ /**
34
+ * Set global context
35
+ */
36
+ setContext(context) {
37
+ this.context = { ...this.context, ...context };
38
+ }
39
+ /**
40
+ * Clear global context
41
+ */
42
+ clearContext() {
43
+ this.context = {};
44
+ }
45
+ /**
46
+ * Debug log
47
+ */
48
+ debug(message, context) {
49
+ this.log("debug", message, context);
50
+ }
51
+ /**
52
+ * Info log
53
+ */
54
+ info(message, context) {
55
+ this.log("info", message, context);
56
+ }
57
+ /**
58
+ * Warning log
59
+ */
60
+ warn(message, context) {
61
+ this.log("warn", message, context);
62
+ }
63
+ /**
64
+ * Error log
65
+ */
66
+ error(message, error, context) {
67
+ const errorContext = error ? {
68
+ error: {
69
+ name: error.name,
70
+ message: error.message,
71
+ stack: this.config.includeStack ? error.stack : void 0,
72
+ cause: error.cause
73
+ }
74
+ } : {};
75
+ this.log("error", message, { ...context, ...errorContext });
76
+ }
77
+ /**
78
+ * Fatal log
79
+ */
80
+ fatal(message, error, context) {
81
+ const errorContext = error ? {
82
+ error: {
83
+ name: error.name,
84
+ message: error.message,
85
+ stack: this.config.includeStack ? error.stack : void 0,
86
+ cause: error.cause
87
+ }
88
+ } : {};
89
+ this.log("fatal", message, { ...context, ...errorContext });
90
+ }
91
+ /**
92
+ * Core logging method
93
+ */
94
+ log(level, message, context) {
95
+ if (!this.config.enabled) return;
96
+ if (LOG_LEVELS[level] < LOG_LEVELS[this.config.level]) {
97
+ return;
98
+ }
99
+ const entry = {
100
+ timestamp: this.config.includeTimestamp ? (/* @__PURE__ */ new Date()).toISOString() : "",
101
+ level,
102
+ message,
103
+ context: { ...this.context, ...context }
104
+ };
105
+ if (entry.context?.error) {
106
+ entry.error = entry.context.error;
107
+ entry.context.error = void 0;
108
+ }
109
+ this.config.onLog(entry);
110
+ for (const handler of this.extraHandlers) {
111
+ handler(entry);
112
+ }
113
+ this.output(entry);
114
+ }
115
+ /**
116
+ * Output log entry
117
+ */
118
+ output(entry) {
119
+ switch (this.config.destination) {
120
+ case "console":
121
+ this.outputConsole(entry);
122
+ break;
123
+ case "file":
124
+ this.outputFile(entry);
125
+ break;
126
+ case "remote":
127
+ this.outputRemote(entry);
128
+ break;
129
+ }
130
+ }
131
+ /**
132
+ * Output to console
133
+ */
134
+ outputConsole(entry) {
135
+ const output = this.config.pretty ? this.formatPretty(entry) : JSON.stringify(entry);
136
+ switch (entry.level) {
137
+ case "debug":
138
+ console.debug(output);
139
+ break;
140
+ case "info":
141
+ console.info(output);
142
+ break;
143
+ case "warn":
144
+ console.warn(output);
145
+ break;
146
+ case "error":
147
+ case "fatal":
148
+ console.error(output);
149
+ break;
150
+ }
151
+ }
152
+ /**
153
+ * Output to file
154
+ */
155
+ outputFile(entry) {
156
+ console.log(JSON.stringify(entry));
157
+ }
158
+ /**
159
+ * Output to remote service
160
+ */
161
+ async outputRemote(entry) {
162
+ if (!this.config.remoteUrl) return;
163
+ try {
164
+ await fetch(this.config.remoteUrl, {
165
+ method: "POST",
166
+ headers: {
167
+ "Content-Type": "application/json"
168
+ },
169
+ body: JSON.stringify(entry)
170
+ });
171
+ } catch (error) {
172
+ console.error("Failed to send log to remote:", error);
173
+ this.outputConsole(entry);
174
+ }
175
+ }
176
+ /**
177
+ * Format log entry for pretty printing
178
+ */
179
+ formatPretty(entry) {
180
+ const levelColors = {
181
+ debug: "\x1B[36m",
182
+ // Cyan
183
+ info: "\x1B[32m",
184
+ // Green
185
+ warn: "\x1B[33m",
186
+ // Yellow
187
+ error: "\x1B[31m",
188
+ // Red
189
+ fatal: "\x1B[35m"
190
+ // Magenta
191
+ };
192
+ const reset = "\x1B[0m";
193
+ const color = levelColors[entry.level];
194
+ const parts = [];
195
+ if (entry.timestamp) {
196
+ parts.push(`\x1B[90m${entry.timestamp}${reset}`);
197
+ }
198
+ parts.push(`${color}${entry.level.toUpperCase()}${reset}`);
199
+ parts.push(entry.message);
200
+ if (entry.context && Object.keys(entry.context).length > 0) {
201
+ parts.push(JSON.stringify(entry.context, null, 2));
202
+ }
203
+ if (entry.error) {
204
+ parts.push(`
205
+ ${color}Error: ${entry.error.message}${reset}`);
206
+ if (entry.error.stack) {
207
+ parts.push(entry.error.stack);
208
+ }
209
+ }
210
+ return parts.join(" ");
211
+ }
212
+ /**
213
+ * Create child logger with additional context
214
+ */
215
+ child(context) {
216
+ const childLogger = new _Logger(this.config);
217
+ childLogger.setContext({ ...this.context, ...context });
218
+ for (const handler of this.extraHandlers) {
219
+ childLogger.addLogHandler(handler);
220
+ }
221
+ return childLogger;
222
+ }
223
+ };
224
+ var logger = new Logger({
225
+ level: process.env.LOG_LEVEL || "info",
226
+ pretty: process.env.NODE_ENV !== "production"
227
+ });
228
+ function createLogger(context) {
229
+ return logger.child(context);
230
+ }
231
+ function logError(error, context) {
232
+ logger.error(error.message, error, context);
233
+ }
234
+ function logAudit(action, context) {
235
+ logger.info(`Audit: ${action}`, {
236
+ ...context,
237
+ audit: true,
238
+ action
239
+ });
240
+ }
241
+ function logQuery(query, duration, context) {
242
+ logger.debug("Database query", {
243
+ ...context,
244
+ query,
245
+ duration
246
+ });
247
+ if (duration > 1e3) {
248
+ logger.warn("Slow query detected", {
249
+ ...context,
250
+ query,
251
+ duration
252
+ });
253
+ }
254
+ }
255
+
256
+ export {
257
+ Logger,
258
+ logger,
259
+ createLogger,
260
+ logError,
261
+ logAudit,
262
+ logQuery
263
+ };
264
+ //# sourceMappingURL=chunk-QR4YAGWO.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/* 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":[]}
@@ -0,0 +1,8 @@
1
+ // src/validation/password-schema.ts
2
+ import { z } from "zod";
3
+ var passwordSchema = z.string().min(8, "Password must be at least 8 characters").max(128, "Password is too long").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number");
4
+
5
+ export {
6
+ passwordSchema
7
+ };
8
+ //# sourceMappingURL=chunk-UEYUNTMY.js.map
@@ -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":[]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * SSL Configuration Utility
3
+ *
4
+ * Provides centralized SSL configuration for PostgreSQL connections based on
5
+ * connection string sslmode parameter and environment variables.
6
+ *
7
+ * Extracted from @revealui/core to break circular dependencies
8
+ *
9
+ * @module ssl-config
10
+ */
11
+ /**
12
+ * PostgreSQL SSL configuration options
13
+ */
14
+ interface SSLConfig {
15
+ rejectUnauthorized: boolean;
16
+ }
17
+ /**
18
+ * Determines the appropriate SSL configuration for a PostgreSQL connection
19
+ * based on the connection string's sslmode parameter.
20
+ *
21
+ * SSL Modes (aligned with PostgreSQL libpq standards):
22
+ * - `disable`: No SSL connection
23
+ * - `require`: SSL connection with certificate verification
24
+ * - `verify-full`: Full SSL verification (same as require in pg v9+)
25
+ * - `verify-ca`: CA verification (treated as verify-full)
26
+ *
27
+ * Environment Override:
28
+ * - `DATABASE_SSL_REJECT_UNAUTHORIZED=false`: Force skip certificate verification
29
+ * (DEVELOPMENT ONLY - use for self-signed certificates in local environments)
30
+ *
31
+ * @param connectionString - PostgreSQL connection string (may include sslmode parameter)
32
+ * @returns SSL configuration object or false to disable SSL
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // Connection string with SSL required
37
+ * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')
38
+ * // Returns: { rejectUnauthorized: true }
39
+ *
40
+ * // Connection string without SSL
41
+ * const ssl = getSSLConfig('postgresql://localhost:5432/db')
42
+ * // Returns: false
43
+ *
44
+ * // Development override for self-signed certificates
45
+ * process.env.DATABASE_SSL_REJECT_UNAUTHORIZED = 'false'
46
+ * const ssl = getSSLConfig('postgresql://user:pass@host/db?sslmode=require')
47
+ * // Returns: { rejectUnauthorized: false }
48
+ * ```
49
+ */
50
+ declare function getSSLConfig(connectionString: string): SSLConfig | false;
51
+ /**
52
+ * Validates SSL configuration for production environments.
53
+ * Warns if insecure SSL settings are detected in production.
54
+ *
55
+ * @param connectionString - PostgreSQL connection string
56
+ * @param environment - Current environment (e.g., 'production', 'development')
57
+ */
58
+ declare function validateSSLConfig(connectionString: string, environment?: string): boolean;
59
+
60
+ export { type SSLConfig, getSSLConfig, validateSSLConfig };
@@ -0,0 +1,9 @@
1
+ import {
2
+ getSSLConfig,
3
+ validateSSLConfig
4
+ } from "../chunk-KUMOGLHP.js";
5
+ export {
6
+ getSSLConfig,
7
+ validateSSLConfig
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,4 @@
1
+ export { SSLConfig, getSSLConfig, validateSSLConfig } from './database/index.js';
2
+ export { LogContext, LogEntry, LogLevel, Logger, LoggerConfig, createLogger, logAudit, logError, logQuery, logger } from './logger/index.js';
3
+ export { Password, passwordSchema } from './validation/index.js';
4
+ import 'zod';
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ import {
2
+ getSSLConfig,
3
+ validateSSLConfig
4
+ } from "./chunk-KUMOGLHP.js";
5
+ import {
6
+ Logger,
7
+ createLogger,
8
+ logAudit,
9
+ logError,
10
+ logQuery,
11
+ logger
12
+ } from "./chunk-QR4YAGWO.js";
13
+ import {
14
+ passwordSchema
15
+ } from "./chunk-UEYUNTMY.js";
16
+ export {
17
+ Logger,
18
+ createLogger,
19
+ getSSLConfig,
20
+ logAudit,
21
+ logError,
22
+ logQuery,
23
+ logger,
24
+ passwordSchema,
25
+ validateSSLConfig
26
+ };
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Structured Logging Infrastructure
3
+ *
4
+ * Provides consistent, structured logging across the application
5
+ * Extracted from @revealui/core to break circular dependencies
6
+ */
7
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';
8
+ interface LogContext {
9
+ [key: string]: unknown;
10
+ userId?: string;
11
+ requestId?: string;
12
+ sessionId?: string;
13
+ traceId?: string;
14
+ spanId?: string;
15
+ }
16
+ interface LogEntry {
17
+ timestamp: string;
18
+ level: LogLevel;
19
+ message: string;
20
+ context?: LogContext;
21
+ error?: {
22
+ name: string;
23
+ message: string;
24
+ stack?: string;
25
+ cause?: unknown;
26
+ };
27
+ metadata?: Record<string, unknown>;
28
+ }
29
+ interface LoggerConfig {
30
+ level?: LogLevel;
31
+ enabled?: boolean;
32
+ pretty?: boolean;
33
+ includeTimestamp?: boolean;
34
+ includeStack?: boolean;
35
+ destination?: 'console' | 'file' | 'remote';
36
+ remoteUrl?: string;
37
+ onLog?: (entry: LogEntry) => void;
38
+ }
39
+ declare class Logger {
40
+ private config;
41
+ private context;
42
+ private extraHandlers;
43
+ constructor(config?: LoggerConfig);
44
+ /**
45
+ * Register an additional log handler (e.g. DB transport).
46
+ * Called after every log entry, fire-and-forget. Must not throw.
47
+ */
48
+ addLogHandler(handler: (entry: LogEntry) => void): void;
49
+ /**
50
+ * Set global context
51
+ */
52
+ setContext(context: LogContext): void;
53
+ /**
54
+ * Clear global context
55
+ */
56
+ clearContext(): void;
57
+ /**
58
+ * Debug log
59
+ */
60
+ debug(message: string, context?: LogContext): void;
61
+ /**
62
+ * Info log
63
+ */
64
+ info(message: string, context?: LogContext): void;
65
+ /**
66
+ * Warning log
67
+ */
68
+ warn(message: string, context?: LogContext): void;
69
+ /**
70
+ * Error log
71
+ */
72
+ error(message: string, error?: Error, context?: LogContext): void;
73
+ /**
74
+ * Fatal log
75
+ */
76
+ fatal(message: string, error?: Error, context?: LogContext): void;
77
+ /**
78
+ * Core logging method
79
+ */
80
+ private log;
81
+ /**
82
+ * Output log entry
83
+ */
84
+ private output;
85
+ /**
86
+ * Output to console
87
+ */
88
+ private outputConsole;
89
+ /**
90
+ * Output to file
91
+ */
92
+ private outputFile;
93
+ /**
94
+ * Output to remote service
95
+ */
96
+ private outputRemote;
97
+ /**
98
+ * Format log entry for pretty printing
99
+ */
100
+ private formatPretty;
101
+ /**
102
+ * Create child logger with additional context
103
+ */
104
+ child(context: LogContext): Logger;
105
+ }
106
+ /**
107
+ * Default logger instance
108
+ */
109
+ declare const logger: Logger;
110
+ /**
111
+ * Create logger with context
112
+ */
113
+ declare function createLogger(context: LogContext): Logger;
114
+ /**
115
+ * Error logger
116
+ */
117
+ declare function logError(error: Error, context?: LogContext): void;
118
+ /**
119
+ * Audit logger for security-sensitive operations
120
+ */
121
+ declare function logAudit(action: string, context?: LogContext): void;
122
+ /**
123
+ * Database query logger
124
+ */
125
+ declare function logQuery(query: string, duration: number, context?: LogContext): void;
126
+
127
+ export { type LogContext, type LogEntry, type LogLevel, Logger, type LoggerConfig, createLogger, logAudit, logError, logQuery, logger };
@@ -0,0 +1,17 @@
1
+ import {
2
+ Logger,
3
+ createLogger,
4
+ logAudit,
5
+ logError,
6
+ logQuery,
7
+ logger
8
+ } from "../chunk-QR4YAGWO.js";
9
+ export {
10
+ Logger,
11
+ createLogger,
12
+ logAudit,
13
+ logError,
14
+ logQuery,
15
+ logger
16
+ };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Password validation schema
5
+ *
6
+ * Requirements:
7
+ * - Minimum 8 characters
8
+ * - Maximum 128 characters
9
+ * - At least one uppercase letter
10
+ * - At least one lowercase letter
11
+ * - At least one number
12
+ */
13
+ declare const passwordSchema: z.ZodString;
14
+ type Password = z.infer<typeof passwordSchema>;
15
+
16
+ export { type Password, passwordSchema };
@@ -0,0 +1,7 @@
1
+ import {
2
+ passwordSchema
3
+ } from "../chunk-UEYUNTMY.js";
4
+ export {
5
+ passwordSchema
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@revealui/utils",
3
+ "version": "0.2.0",
4
+ "description": "Shared utilities for RevealUI - logger, SSL config, and common helpers",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./logger": {
13
+ "types": "./dist/logger/index.d.ts",
14
+ "import": "./dist/logger/index.js"
15
+ },
16
+ "./database": {
17
+ "types": "./dist/database/index.d.ts",
18
+ "import": "./dist/database/index.js"
19
+ },
20
+ "./validation": {
21
+ "types": "./dist/validation/index.d.ts",
22
+ "import": "./dist/validation/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "registry": "https://registry.npmjs.org"
31
+ },
32
+ "dependencies": {
33
+ "zod": "^4.3.6"
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
+ },
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "typecheck": "tsc --noEmit",
46
+ "test": "vitest run --passWithNoTests",
47
+ "test:watch": "vitest",
48
+ "lint": "biome check .",
49
+ "lint:fix": "biome check --write .",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }