@crimsonsunset/jsg-logger 1.8.1 → 1.8.2

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/index.js +46 -13
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to the JSG Logger project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.8.2] - 2026-03-27 🛡️ **Reinit Guard + configure() API**
9
+
10
+ ### Added
11
+ - **`JSGLogger.configure(partialConfig)`** — new static method for post-init config updates. Merges partial config into the running instance without reinitializing or touching registered transports. Exposed on the default export as `logger.configure()` and on the class as `JSGLogger.configure()`. If called before any initialization has occurred, delegates to `getInstanceSync(partialConfig)`.
12
+
13
+ ### Fixed
14
+ - **Reinit guard in `getInstanceSync()`** — calling `getInstanceSync(options)` on an already-initialized instance now returns the existing singleton instead of reinitializing. Previously this would wipe registered transports (e.g. PostHog transport) on every call. A `console.warn` is emitted to surface the misuse at call sites.
15
+ - **Reinit guard in `getInstance()`** — same protection applied to the async path.
16
+
17
+ ### Changed
18
+ - Removed stale comment "// If options are provided, we need to reinitialize even if global instance exists" — the behavior is now the opposite by design.
19
+
20
+ ### Migration
21
+ No breaking changes. If you were relying on calling `getInstanceSync(options)` multiple times to update config post-init (which was always a footgun), switch to `logger.configure(partialConfig)` instead.
22
+
8
23
  ## [1.7.7] - 2025-11-07 🔧 **DevTools Import Path Fix**
9
24
 
10
25
  ### Fixed
package/index.js CHANGED
@@ -81,14 +81,17 @@ class JSGLogger {
81
81
  window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
82
82
  }
83
83
  } else if (hasOptions) {
84
- // Instance exists but new options provided - reinitialize
85
- JSGLogger._enhancedLoggers = await JSGLogger._instance.init(options);
86
-
87
- // Make runtime controls available globally in browser for debugging
88
- // (same as getInstanceSync behavior)
89
- if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
90
- window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
91
- window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
84
+ if (JSGLogger._instance.initialized) {
85
+ console.warn(
86
+ '[JSGLogger] getInstance() called with options on an already-initialized instance — ' +
87
+ 'options were ignored to preserve registered transports. Use configure() to update settings post-init.'
88
+ );
89
+ } else {
90
+ JSGLogger._enhancedLoggers = await JSGLogger._instance.init(options);
91
+ if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
92
+ window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
93
+ window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
94
+ }
92
95
  }
93
96
  }
94
97
 
@@ -104,22 +107,25 @@ class JSGLogger {
104
107
  static getInstanceSync(options = {}) {
105
108
  const hasOptions = options && Object.keys(options).length > 0;
106
109
 
107
- // If options are provided, we need to reinitialize even if global instance exists
108
- // This ensures config changes (like devtools.enabled) are applied
109
110
  if (hasOptions) {
110
- // Reinitialize with new options - this will update the global references
111
+ if (JSGLogger._instance?.initialized) {
112
+ console.warn(
113
+ '[JSGLogger] getInstanceSync() called with options on an already-initialized instance — ' +
114
+ 'options were ignored to preserve registered transports. Use configure() to update settings post-init.'
115
+ );
116
+ return JSGLogger._enhancedLoggers;
117
+ }
118
+
111
119
  if (!JSGLogger._instance) {
112
120
  JSGLogger._instance = new JSGLogger();
113
121
  }
114
122
  JSGLogger._enhancedLoggers = JSGLogger._instance.initSync(options);
115
123
 
116
- // Update global references after reinitialization
117
124
  if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
118
125
  window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
119
126
  window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
120
127
  }
121
128
 
122
- // Server equivalent: persist to globalThis so cross-bundle module instances can find it
123
129
  if (!isBrowser()) {
124
130
  globalThis.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
125
131
  }
@@ -559,6 +565,9 @@ class JSGLogger {
559
565
  // Expose config manager for runtime configuration
560
566
  configManager: configManager,
561
567
 
568
+ // Post-init config updater — safe to call after transports are registered
569
+ configure: (partialConfig) => JSGLogger.configure(partialConfig),
570
+
562
571
  // Log store for popup/debugging
563
572
  logStore: this.logStore,
564
573
 
@@ -987,6 +996,29 @@ class JSGLogger {
987
996
  }
988
997
  }
989
998
 
999
+ /**
1000
+ * Update logger configuration post-initialization without reinitializing.
1001
+ * Merges partialConfig into the current config without touching registered transports.
1002
+ * Transports can only be registered at init time — this method cannot add or remove them.
1003
+ * If called before any initialization has occurred, delegates to getInstanceSync(partialConfig).
1004
+ * @param {Object} partialConfig - Partial config to merge into the current config
1005
+ * @returns {Object} Enhanced logger exports
1006
+ */
1007
+ static configure(partialConfig = {}) {
1008
+ if (!JSGLogger._instance?.initialized) {
1009
+ return JSGLogger.getInstanceSync(partialConfig);
1010
+ }
1011
+
1012
+ const currentTransports = JSGLogger._instance.transports;
1013
+ const normalized = configManager._normalizeConfigStructure(partialConfig);
1014
+ configManager.config = configManager.mergeConfigs(configManager.config, normalized);
1015
+ configManager.config.transports = currentTransports;
1016
+ JSGLogger._instance.transports = currentTransports;
1017
+ JSGLogger._instance.refreshLoggers();
1018
+
1019
+ return JSGLogger._enhancedLoggers;
1020
+ }
1021
+
990
1022
  /**
991
1023
  * Get singleton controls without triggering initialization
992
1024
  * Checks window.JSG_Logger first to ensure singleton works across separate bundles
@@ -1019,6 +1051,7 @@ if (isBrowser() && typeof window !== 'undefined') {
1019
1051
  // Add static methods to the enhanced loggers for convenience
1020
1052
  enhancedLoggers.getInstance = JSGLogger.getInstance;
1021
1053
  enhancedLoggers.getInstanceSync = JSGLogger.getInstanceSync;
1054
+ enhancedLoggers.configure = JSGLogger.configure.bind(JSGLogger);
1022
1055
  enhancedLoggers.logPerformance = JSGLogger.logPerformance;
1023
1056
  enhancedLoggers.JSGLogger = JSGLogger;
1024
1057
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimsonsunset/jsg-logger",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "type": "module",
5
5
  "description": "Multi-environment logger with smart detection, file-level overrides, and beautiful console formatting. Test it live: https://logger.joesangiorgio.com/",
6
6
  "main": "index.js",