@crimsonsunset/jsg-logger 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Configuration Manager for CACP Logger
3
+ * Handles loading, merging, and validation of logger configurations
4
+ * Implements smart level resolution and file override system
5
+ */
6
+
7
+ import defaultConfig from './default-config.json';
8
+ import {COMPONENT_SCHEME, LEVEL_SCHEME} from './component-schemes.js';
9
+
10
+ export class ConfigManager {
11
+ constructor() {
12
+ this.config = {...defaultConfig};
13
+ this.loadedPaths = [];
14
+ this.currentFile = null; // Track current file for overrides
15
+ }
16
+
17
+ /**
18
+ * Load configuration from a file path or object
19
+ * @param {string|Object} configSource - File path or config object
20
+ * @returns {Promise<Object>} Merged configuration
21
+ */
22
+ async loadConfig(configSource) {
23
+ try {
24
+ let externalConfig = {};
25
+
26
+ if (typeof configSource === 'string') {
27
+ // Load from file path
28
+ if (configSource.startsWith('./') || configSource.startsWith('../')) {
29
+ // Relative path - attempt to load
30
+ try {
31
+ const response = await fetch(configSource);
32
+ if (response.ok) {
33
+ externalConfig = await response.json();
34
+ this.loadedPaths.push(configSource);
35
+ }
36
+ } catch (error) {
37
+ console.warn(`Failed to load config from ${configSource}:`, error.message);
38
+ }
39
+ }
40
+ } else if (typeof configSource === 'object') {
41
+ // Direct config object
42
+ externalConfig = configSource;
43
+ }
44
+
45
+ // Merge configurations
46
+ this.config = this.mergeConfigs(this.config, externalConfig);
47
+
48
+ return this.config;
49
+ } catch (error) {
50
+ console.error('ConfigManager: Error loading configuration:', error);
51
+ return this.config; // Return default config on error
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Set current file context for override resolution
57
+ * @param {string} filePath - Current file path being logged from
58
+ */
59
+ setCurrentFile(filePath) {
60
+ this.currentFile = filePath;
61
+ }
62
+
63
+ /**
64
+ * Deep merge two configuration objects
65
+ * @private
66
+ */
67
+ mergeConfigs(base, override) {
68
+ const merged = {...base};
69
+
70
+ for (const key in override) {
71
+ if (override.hasOwnProperty(key)) {
72
+ if (typeof override[key] === 'object' && !Array.isArray(override[key])) {
73
+ merged[key] = this.mergeConfigs(merged[key] || {}, override[key]);
74
+ } else {
75
+ merged[key] = override[key];
76
+ }
77
+ }
78
+ }
79
+
80
+ return merged;
81
+ }
82
+
83
+ /**
84
+ * Get effective log level using hierarchy: file override > component level > global level
85
+ * @param {string} componentName - Component identifier
86
+ * @param {string} filePath - Optional file path for override checking
87
+ * @returns {string} Effective log level
88
+ */
89
+ getEffectiveLevel(componentName, filePath = null) {
90
+ const checkFile = filePath || this.currentFile;
91
+
92
+ // 1. Check file overrides first (highest priority)
93
+ if (checkFile && this.config.fileOverrides) {
94
+ const fileOverride = this.getFileOverride(checkFile);
95
+ if (fileOverride && fileOverride.level) {
96
+ return fileOverride.level;
97
+ }
98
+ }
99
+
100
+ // 2. Check component-specific level
101
+ if (this.config.components && this.config.components[componentName] && this.config.components[componentName].level) {
102
+ return this.config.components[componentName].level;
103
+ }
104
+
105
+ // 3. Fall back to global level
106
+ return this.config.globalLevel || 'info';
107
+ }
108
+
109
+ /**
110
+ * Get file override configuration for a given file path
111
+ * @param {string} filePath - File path to check
112
+ * @returns {Object|null} File override config or null
113
+ */
114
+ getFileOverride(filePath) {
115
+ if (!this.config.fileOverrides || !filePath) {
116
+ return null;
117
+ }
118
+
119
+ // Normalize file path (remove leading ./ and ../)
120
+ const normalizedPath = filePath.replace(/^\.\.?\//g, '');
121
+
122
+ // Check exact matches first
123
+ if (this.config.fileOverrides[normalizedPath]) {
124
+ return this.config.fileOverrides[normalizedPath];
125
+ }
126
+
127
+ // Check pattern matches
128
+ for (const pattern in this.config.fileOverrides) {
129
+ if (this.matchFilePattern(normalizedPath, pattern)) {
130
+ return this.config.fileOverrides[pattern];
131
+ }
132
+ }
133
+
134
+ return null;
135
+ }
136
+
137
+ /**
138
+ * Match file path against a pattern (supports wildcards)
139
+ * @param {string} filePath - File path to test
140
+ * @param {string} pattern - Pattern to match against
141
+ * @returns {boolean} Whether the file matches the pattern
142
+ * @private
143
+ */
144
+ matchFilePattern(filePath, pattern) {
145
+ // Convert glob pattern to regex
146
+ const regexPattern = pattern
147
+ .replace(/\./g, '\\.') // Escape dots
148
+ .replace(/\*/g, '.*') // Convert * to .*
149
+ .replace(/\?/g, '.') // Convert ? to .
150
+ + '$'; // End of string
151
+
152
+ const regex = new RegExp(regexPattern);
153
+ return regex.test(filePath);
154
+ }
155
+
156
+ /**
157
+ * Get component configuration with file override support
158
+ * @param {string} componentName - Component identifier
159
+ * @param {string} filePath - Optional file path for override checking
160
+ * @returns {Object} Component configuration
161
+ */
162
+ getComponentConfig(componentName, filePath = null) {
163
+ const baseComponent = this.config.components?.[componentName] || COMPONENT_SCHEME[componentName] || COMPONENT_SCHEME['cacp'];
164
+
165
+ // Check for file-specific overrides
166
+ const checkFile = filePath || this.currentFile;
167
+ if (checkFile) {
168
+ const fileOverride = this.getFileOverride(checkFile);
169
+ if (fileOverride) {
170
+ return {
171
+ ...baseComponent,
172
+ ...fileOverride,
173
+ level: this.getEffectiveLevel(componentName, checkFile)
174
+ };
175
+ }
176
+ }
177
+
178
+ return {
179
+ ...baseComponent,
180
+ level: this.getEffectiveLevel(componentName, checkFile)
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Get level configuration
186
+ * @param {number} level - Log level number
187
+ * @returns {Object} Level configuration
188
+ */
189
+ getLevelConfig(level) {
190
+ // Check if level exists in loaded config
191
+ if (this.config.levels && this.config.levels[level]) {
192
+ return this.config.levels[level];
193
+ }
194
+
195
+ // Fallback to default schemes
196
+ return LEVEL_SCHEME[level] || LEVEL_SCHEME[30];
197
+ }
198
+
199
+ /**
200
+ * Get global log level
201
+ * @returns {string} Global log level
202
+ */
203
+ getGlobalLevel() {
204
+ return this.config.globalLevel || 'info';
205
+ }
206
+
207
+ /**
208
+ * Get timestamp mode
209
+ * @returns {string} Timestamp mode ('absolute', 'readable', 'relative', 'disable')
210
+ */
211
+ getTimestampMode() {
212
+ return this.config.timestampMode || 'absolute';
213
+ }
214
+
215
+ /**
216
+ * Get display configuration with file override support
217
+ * @param {string} filePath - Optional file path for override checking
218
+ * @returns {Object} Display configuration
219
+ */
220
+ getDisplayConfig(filePath = null) {
221
+ const baseDisplay = this.config.display || {
222
+ timestamp: true,
223
+ emoji: true,
224
+ component: true,
225
+ level: false,
226
+ message: true,
227
+ jsonPayload: true,
228
+ stackTrace: true
229
+ };
230
+
231
+ // Check for file-specific display overrides
232
+ const checkFile = filePath || this.currentFile;
233
+ if (checkFile) {
234
+ const fileOverride = this.getFileOverride(checkFile);
235
+ if (fileOverride && fileOverride.display) {
236
+ return {
237
+ ...baseDisplay,
238
+ ...fileOverride.display
239
+ };
240
+ }
241
+ }
242
+
243
+ return baseDisplay;
244
+ }
245
+
246
+ /**
247
+ * Get project name
248
+ * @returns {string} Project name
249
+ */
250
+ getProjectName() {
251
+ return this.config.projectName || 'CACP Logger';
252
+ }
253
+
254
+ /**
255
+ * Check if auto-registration is enabled
256
+ * @returns {boolean} Auto-registration flag
257
+ */
258
+ isAutoRegisterEnabled() {
259
+ return this.config.autoRegister !== false;
260
+ }
261
+
262
+ /**
263
+ * Get formatting configuration
264
+ * @returns {Object} Format configuration
265
+ */
266
+ getFormatConfig() {
267
+ return this.config.format || {
268
+ style: 'brackets',
269
+ componentCase: 'upper',
270
+ timestamp: 'HH:mm:ss.SSS'
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Get all available components
276
+ * @returns {Array} Array of component names
277
+ */
278
+ getAvailableComponents() {
279
+ const configComponents = Object.keys(this.config.components || {});
280
+ const schemeComponents = Object.keys(COMPONENT_SCHEME);
281
+
282
+ // Combine and deduplicate
283
+ return [...new Set([...configComponents, ...schemeComponents])];
284
+ }
285
+
286
+ /**
287
+ * Add or update a component configuration
288
+ * @param {string} componentName - Component identifier
289
+ * @param {Object} componentConfig - Component configuration
290
+ */
291
+ addComponent(componentName, componentConfig) {
292
+ if (!this.config.components) {
293
+ this.config.components = {};
294
+ }
295
+
296
+ this.config.components[componentName] = {
297
+ ...this.getComponentConfig(componentName),
298
+ ...componentConfig
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Add or update a file override
304
+ * @param {string} filePath - File path or pattern
305
+ * @param {Object} overrideConfig - Override configuration
306
+ */
307
+ addFileOverride(filePath, overrideConfig) {
308
+ if (!this.config.fileOverrides) {
309
+ this.config.fileOverrides = {};
310
+ }
311
+
312
+ this.config.fileOverrides[filePath] = overrideConfig;
313
+ }
314
+
315
+ /**
316
+ * Format timestamp based on mode
317
+ * @param {number} timestamp - Unix timestamp
318
+ * @param {string} mode - Timestamp mode
319
+ * @returns {string} Formatted timestamp
320
+ */
321
+ formatTimestamp(timestamp, mode = null) {
322
+ const timestampMode = mode || this.getTimestampMode();
323
+ const date = new Date(timestamp);
324
+
325
+ switch (timestampMode) {
326
+ case 'readable':
327
+ return date.toLocaleTimeString('en-US', {
328
+ hour: 'numeric',
329
+ minute: '2-digit',
330
+ hour12: true
331
+ });
332
+
333
+ case 'relative':
334
+ const now = Date.now();
335
+ const diff = now - timestamp;
336
+
337
+ if (diff < 1000) return 'now';
338
+ if (diff < 60000) return `${Math.floor(diff / 1000)}s ago`;
339
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
340
+ return `${Math.floor(diff / 3600000)}h ago`;
341
+
342
+ case 'disable':
343
+ return '';
344
+
345
+ case 'absolute':
346
+ default:
347
+ return date.toLocaleTimeString('en-US', {
348
+ hour12: false,
349
+ hour: '2-digit',
350
+ minute: '2-digit',
351
+ second: '2-digit',
352
+ fractionalSecondDigits: 3
353
+ });
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Get configuration summary for debugging
359
+ * @returns {Object} Configuration summary
360
+ */
361
+ getSummary() {
362
+ return {
363
+ projectName: this.getProjectName(),
364
+ globalLevel: this.getGlobalLevel(),
365
+ timestampMode: this.getTimestampMode(),
366
+ loadedPaths: this.loadedPaths,
367
+ componentCount: this.getAvailableComponents().length,
368
+ fileOverrideCount: Object.keys(this.config.fileOverrides || {}).length,
369
+ autoRegister: this.isAutoRegisterEnabled(),
370
+ format: this.getFormatConfig(),
371
+ display: this.getDisplayConfig(),
372
+ currentFile: this.currentFile
373
+ };
374
+ }
375
+ }
376
+
377
+ // Create singleton instance
378
+ export const configManager = new ConfigManager();
@@ -0,0 +1,92 @@
1
+ {
2
+ "projectName": "CACP Logger",
3
+ "globalLevel": "info",
4
+ "autoRegister": true,
5
+ "format": {
6
+ "style": "brackets",
7
+ "componentCase": "upper",
8
+ "timestamp": "HH:mm:ss.SSS"
9
+ },
10
+ "timestampMode": "absolute",
11
+ "display": {
12
+ "timestamp": true,
13
+ "emoji": true,
14
+ "component": true,
15
+ "level": false,
16
+ "message": true,
17
+ "jsonPayload": true,
18
+ "stackTrace": true
19
+ },
20
+ "levels": {
21
+ "10": { "name": "TRACE", "emoji": "πŸ”", "color": "#6C7B7F" },
22
+ "20": { "name": "DEBUG", "emoji": "πŸ›", "color": "#74B9FF" },
23
+ "30": { "name": "INFO", "emoji": "✨", "color": "#00B894" },
24
+ "40": { "name": "WARN", "emoji": "⚠️", "color": "#FDCB6E" },
25
+ "50": { "name": "ERROR", "emoji": "🚨", "color": "#E17055" },
26
+ "60": { "name": "FATAL", "emoji": "πŸ’€", "color": "#D63031" }
27
+ },
28
+ "components": {
29
+ "cacp": {
30
+ "emoji": "🎯",
31
+ "color": "#4A90E2",
32
+ "name": "CACP-Core",
33
+ "level": "info"
34
+ },
35
+ "soundcloud": {
36
+ "emoji": "🎡",
37
+ "color": "#FF5500",
38
+ "name": "SoundCloud",
39
+ "level": "info"
40
+ },
41
+ "youtube": {
42
+ "emoji": "πŸ“Ή",
43
+ "color": "#FF0000",
44
+ "name": "YouTube",
45
+ "level": "info"
46
+ },
47
+ "site-detector": {
48
+ "emoji": "πŸ”",
49
+ "color": "#00C896",
50
+ "name": "SiteDetector",
51
+ "level": "info"
52
+ },
53
+ "websocket": {
54
+ "emoji": "🌐",
55
+ "color": "#9B59B6",
56
+ "name": "WebSocket",
57
+ "level": "info"
58
+ },
59
+ "popup": {
60
+ "emoji": "πŸŽ›οΈ",
61
+ "color": "#FF6B6B",
62
+ "name": "Popup",
63
+ "level": "info"
64
+ },
65
+ "background": {
66
+ "emoji": "πŸ”§",
67
+ "color": "#4ECDC4",
68
+ "name": "Background",
69
+ "level": "info"
70
+ },
71
+ "priority-manager": {
72
+ "emoji": "βš–οΈ",
73
+ "color": "#45B7D1",
74
+ "name": "PriorityManager",
75
+ "level": "info"
76
+ },
77
+ "settings": {
78
+ "emoji": "βš™οΈ",
79
+ "color": "#96CEB4",
80
+ "name": "Settings",
81
+ "level": "info"
82
+ },
83
+ "test": {
84
+ "emoji": "πŸ§ͺ",
85
+ "color": "#FFEAA7",
86
+ "name": "Test",
87
+ "level": "debug"
88
+ }
89
+ },
90
+ "fileOverrides": {
91
+ }
92
+ }
@@ -0,0 +1,136 @@
1
+ # JSG Logger - Session Summary
2
+
3
+ ## πŸ“‹ How to Update This Doc
4
+
5
+ **This is a working document that gets refreshed each session:**
6
+ 1. **Wipe accomplished items** - Remove completed tasks and achievements
7
+ 2. **Keep undone items** - Leave incomplete tasks for tracking purposes
8
+ 3. **Add new priorities** - Include new tasks and blockers that emerge
9
+ 4. **Update current state** - Reflect what's working vs what needs attention
10
+
11
+ **Key difference from roadmap.md:**
12
+ - **This file:** Working session notes, gets refreshed as tasks complete
13
+ - **Roadmap.md:** Permanent historical record, accumulates progress over time
14
+
15
+ ---
16
+
17
+ **Date:** August 6, 2025
18
+ **Session Goal:** 🎯 **COMPLETE** - βœ… Logger extraction, NPM publication, and documentation structure
19
+ **Next Session Goal:** 🎨 **DevTools Panel Implementation** - Browser-based log filtering interface (optional enhancement)
20
+
21
+ ## πŸŽ‰ MAJOR ACCOMPLISHMENTS THIS SESSION
22
+
23
+ ### βœ… Logger Extraction & NPM Publication COMPLETE
24
+ - **πŸ“¦ NPM Package** - Published `@crimsonsunset/jsg-logger` v1.0.6 to registry
25
+ - **πŸ”§ Automated Scripts** - Added `npm run release` for easy version management
26
+ - **πŸ“‚ Repository Migration** - Successfully extracted from DeskThing-Apps to standalone repo
27
+ - **πŸ”„ Integration Success** - DeskThing-Apps now uses published package instead of local folder
28
+ - **🧹 Cleanup Complete** - Removed old logger folder from DeskThing-Apps
29
+
30
+ ### βœ… Documentation Structure COMPLETE
31
+ - **πŸ“„ LICENSE** - Added ISC license file with proper copyright
32
+ - **πŸ“‹ CHANGELOG.md** - Version history tracking with semantic versioning
33
+ - **🀝 CONTRIBUTING.md** - Guidelines for future contributors
34
+ - **πŸ“ docs/ Folder** - Professional documentation structure
35
+ - **πŸ—ΊοΈ roadmap.md** - Comprehensive project roadmap with phases
36
+ - **πŸ“ next-session.md** - Working session tracking template
37
+
38
+ ### βœ… Legal & Professional Polish COMPLETE
39
+ - **βš–οΈ ISC License** - "AS IS" liability protection with permissive usage
40
+ - **πŸ›‘οΈ Disclaimer** - Clear legal protection in README
41
+ - **πŸ“Š Package Metadata** - Professional NPM package with proper keywords
42
+ - **πŸ”— Repository Links** - GitHub links for issues, bugs, and homepage
43
+
44
+ ## 🎯 Current Status
45
+
46
+ ### **Project State: Feature Complete & Stable** βœ…
47
+ - **Core Logger**: Multi-environment logging working perfectly
48
+ - **NPM Package**: Published and successfully integrated
49
+ - **Documentation**: Comprehensive and professional
50
+ - **Legal Protection**: ISC license with "AS IS" disclaimer
51
+
52
+ ### **What's Working Well**
53
+ - βœ… **Beautiful Console Output** - Direct browser logger with perfect formatting
54
+ - βœ… **Runtime Controls** - Dynamic configuration without restarts
55
+ - βœ… **File-Level Precision** - Surgical debugging capabilities
56
+ - βœ… **Multi-Environment** - Seamless browser/CLI/server support
57
+ - βœ… **Professional Package** - NPM-ready with automated publishing
58
+
59
+ ### **No Known Issues**
60
+ - **Zero critical bugs** - Logger is stable and tested
61
+ - **Clean Integration** - DeskThing-Apps migration successful
62
+ - **Documentation Complete** - All standard files in place
63
+
64
+ ## πŸ“‹ CURRENT PRIORITIES
65
+
66
+ ### **No Immediate Tasks** βœ…
67
+ - **Logger is feature complete** - Core functionality fully implemented
68
+ - **Documentation structure complete** - All standard files added
69
+ - **NPM package stable** - Version 1.0.6 published and working
70
+
71
+ ### **Optional Future Enhancements** (Low Priority)
72
+ - [ ] **DevTools Panel** - Browser-based log filtering interface
73
+ - Runtime-injected widget with Preact
74
+ - Collapsible sidebar from left side
75
+ - Controls console filtering without displaying logs
76
+ - IndexedDB persistence for panel state
77
+ - [ ] **Performance Monitoring** - Track logging overhead metrics
78
+ - [ ] **Export Utilities** - Save logs to file formats
79
+ - [ ] **Framework Integration Guides** - React, Vue, Svelte examples
80
+
81
+ ## πŸ”§ Technical Notes
82
+
83
+ ### **NPM Publishing Lessons Learned**
84
+ - **Scoped Packages** - Need `--access public` for free publishing
85
+ - **Internal Imports** - Required multiple patch versions (1.0.1-1.0.4) to fix relative paths
86
+ - **Automated Scripts** - `npm run release` handles version bump + publish in one command
87
+
88
+ ### **Architecture Highlights**
89
+ - **Browser Logger Breakthrough** - Bypassing Pino for 100% console control
90
+ - **Hierarchical Config** - File > Component > Global precedence
91
+ - **Environment Detection** - Feature detection over user agent parsing
92
+ - **Runtime API** - Complete `logger.controls` interface for dynamic changes
93
+
94
+ ### **Integration Success**
95
+ - **Vite Alias Removal** - Cleanly replaced `@logger` alias with NPM import
96
+ - **Build Compatibility** - Extension builds successfully with published package
97
+ - **Zero Disruption** - Existing DeskThing functionality unchanged
98
+
99
+ ## 🎯 Next Session Possibilities
100
+
101
+ ### **If Continuing Development** (Optional)
102
+ 1. **DevTools Panel** - Browser interface for log filtering
103
+ - Use existing `logger.controls` API (no custom filter engine needed)
104
+ - Preact for minimal bundle size
105
+ - IndexedDB for persistence
106
+ - Runtime injection pattern
107
+
108
+ 2. **Community Features** (If demand exists)
109
+ - Framework integration examples
110
+ - Performance monitoring dashboard
111
+ - Export/import configuration utilities
112
+
113
+ ### **If Project Maintenance Mode**
114
+ - **Monitor NPM usage** - See if package gains adoption
115
+ - **Address issues** - Respond to bug reports or feature requests
116
+ - **Version updates** - Maintain dependencies and compatibility
117
+
118
+ ## πŸ“Š Session Metrics
119
+
120
+ ### **Documentation Added**
121
+ - LICENSE (ISC) - 15 lines
122
+ - CHANGELOG.md - 45 lines with full version history
123
+ - CONTRIBUTING.md - 65 lines with guidelines
124
+ - docs/roadmap.md - 280+ lines comprehensive roadmap
125
+ - docs/next-session.md - 130+ lines session template
126
+
127
+ ### **NPM Package Health**
128
+ - **Version**: 1.0.6 (stable)
129
+ - **Size**: 16.1 kB compressed, 65.0 kB unpacked
130
+ - **Files**: 12 files published
131
+ - **Dependencies**: Only pino for server environments
132
+ - **License**: ISC with "AS IS" protection
133
+
134
+ ### **Project Completion Status**: 100% Core Features βœ…
135
+
136
+ **The JSG Logger is now a complete, professional, reusable NPM package with comprehensive documentation and legal protection.**