@crimsonsunset/jsg-logger 1.8.0 → 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 +62 -12
  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,21 +107,29 @@ 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
 
129
+ if (!isBrowser()) {
130
+ globalThis.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
131
+ }
132
+
122
133
  return JSGLogger._enhancedLoggers;
123
134
  }
124
135
 
@@ -143,6 +154,13 @@ class JSGLogger {
143
154
  };
144
155
  }
145
156
 
157
+ // Server equivalent: check globalThis for cross-module-instance singleton
158
+ // Same problem as browser cross-bundle, but for Node.js bundled server chunks
159
+ if (!isBrowser() && globalThis.__JSG_Logger_Enhanced__) {
160
+ JSGLogger._enhancedLoggers = globalThis.__JSG_Logger_Enhanced__;
161
+ return JSGLogger._enhancedLoggers;
162
+ }
163
+
146
164
  // No options and no global instance - first time initialization
147
165
  if (!JSGLogger._instance) {
148
166
  JSGLogger._instance = new JSGLogger();
@@ -153,6 +171,11 @@ class JSGLogger {
153
171
  window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
154
172
  window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
155
173
  }
174
+
175
+ // Server equivalent: persist to globalThis for cross-bundle access
176
+ if (!isBrowser()) {
177
+ globalThis.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
178
+ }
156
179
  }
157
180
 
158
181
  return JSGLogger._enhancedLoggers;
@@ -542,6 +565,9 @@ class JSGLogger {
542
565
  // Expose config manager for runtime configuration
543
566
  configManager: configManager,
544
567
 
568
+ // Post-init config updater — safe to call after transports are registered
569
+ configure: (partialConfig) => JSGLogger.configure(partialConfig),
570
+
545
571
  // Log store for popup/debugging
546
572
  logStore: this.logStore,
547
573
 
@@ -970,6 +996,29 @@ class JSGLogger {
970
996
  }
971
997
  }
972
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
+
973
1022
  /**
974
1023
  * Get singleton controls without triggering initialization
975
1024
  * Checks window.JSG_Logger first to ensure singleton works across separate bundles
@@ -1002,6 +1051,7 @@ if (isBrowser() && typeof window !== 'undefined') {
1002
1051
  // Add static methods to the enhanced loggers for convenience
1003
1052
  enhancedLoggers.getInstance = JSGLogger.getInstance;
1004
1053
  enhancedLoggers.getInstanceSync = JSGLogger.getInstanceSync;
1054
+ enhancedLoggers.configure = JSGLogger.configure.bind(JSGLogger);
1005
1055
  enhancedLoggers.logPerformance = JSGLogger.logPerformance;
1006
1056
  enhancedLoggers.JSGLogger = JSGLogger;
1007
1057
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimsonsunset/jsg-logger",
3
- "version": "1.8.0",
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",