@crimsonsunset/jsg-logger 1.7.13 → 1.7.15

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 (82) hide show
  1. package/README.md +116 -0
  2. package/config/config-manager.js +26 -0
  3. package/config/default-config.json +4 -0
  4. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  5. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  6. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  7. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  8. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  9. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  10. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  11. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  12. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  13. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  14. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  15. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  16. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  17. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  18. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  19. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  20. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  21. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  22. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  23. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  24. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  25. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  26. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  27. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  28. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  29. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  30. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  31. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  32. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js +5 -0
  33. package/devtools/dist/__vite-browser-external-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW-2Ng8QIWW.js.map +1 -0
  34. package/devtools/dist/panel-entry-Ahej53sX-y0jCvGoU-BGR5GUGQ-B-CAdAd3-DipRT8xT-CSMwqnO2-CHbiC4Qe-BRrWXqYD-CNcJekPg.js +13932 -0
  35. package/devtools/dist/panel-entry-Ahej53sX-y0jCvGoU-BGR5GUGQ-B-CAdAd3-DipRT8xT-CSMwqnO2-CHbiC4Qe-BRrWXqYD-CNcJekPg.js.map +1 -0
  36. package/devtools/dist/panel-entry-B0ZWdsQY-DQdquw3J-TpkzsLdj-BGvdXuoS-CzTJvBQl-CAQNpg2W-Bo2gswMb-CAJV5DP5-DZe7oWlk-tDqwNUld.js +13923 -0
  37. package/devtools/dist/panel-entry-B0ZWdsQY-DQdquw3J-TpkzsLdj-BGvdXuoS-CzTJvBQl-CAQNpg2W-Bo2gswMb-CAJV5DP5-DZe7oWlk-tDqwNUld.js.map +1 -0
  38. package/devtools/dist/panel-entry-B2MhZ3gp.js +13975 -0
  39. package/devtools/dist/panel-entry-B2MhZ3gp.js.map +1 -0
  40. package/devtools/dist/{panel-entry-BZ56bT66-BMyD75mc.js → panel-entry-BZ56bT66-BMyD75mc-BxfcB-io-DLFBg4X2-By57n0cz-DG9FfyPU-tsDeIORn-Brgs9eZh-C1aTwqVx-Bga5v200-D2uIBkta-DFygDOuM-CiZQ0ZWs-CK-Qy7JF-3_LfnrLj-C80pq-Ox-BAydVBl9.js} +4 -4
  41. package/devtools/dist/panel-entry-BZ56bT66-BMyD75mc-BxfcB-io-DLFBg4X2-By57n0cz-DG9FfyPU-tsDeIORn-Brgs9eZh-C1aTwqVx-Bga5v200-D2uIBkta-DFygDOuM-CiZQ0ZWs-CK-Qy7JF-3_LfnrLj-C80pq-Ox-BAydVBl9.js.map +1 -0
  42. package/devtools/dist/panel-entry-BaBkkSt6-B3YoMX06.js +13963 -0
  43. package/devtools/dist/panel-entry-BaBkkSt6-B3YoMX06.js.map +1 -0
  44. package/devtools/dist/panel-entry-CIG0rw0G-C23A9sjU-CbDuened.js +13957 -0
  45. package/devtools/dist/panel-entry-CIG0rw0G-C23A9sjU-CbDuened.js.map +1 -0
  46. package/devtools/dist/panel-entry-CLDrp2P4-BhE5Qmzo-DsrDp_aW-D77vFWhc-D0p-3Vck-juqy3fYf-Dla5eR-o-CER385zO-3kQqst6Z-mEXeTQxs-XDwY_WUW-CcUUttdq-BGVY77mY.js +13914 -0
  47. package/devtools/dist/panel-entry-CLDrp2P4-BhE5Qmzo-DsrDp_aW-D77vFWhc-D0p-3Vck-juqy3fYf-Dla5eR-o-CER385zO-3kQqst6Z-mEXeTQxs-XDwY_WUW-CcUUttdq-BGVY77mY.js.map +1 -0
  48. package/devtools/dist/panel-entry-Car1txqz-Cujk5tL9-pWwzfz1C-C_CMO2R5-BtcT6buV-DEh6fx7S-BLtwgJNF-CUuTgFqX.js +13935 -0
  49. package/devtools/dist/panel-entry-Car1txqz-Cujk5tL9-pWwzfz1C-C_CMO2R5-BtcT6buV-DEh6fx7S-BLtwgJNF-CUuTgFqX.js.map +1 -0
  50. package/devtools/dist/panel-entry-CdHyTMuR-wqllnGMv-CJFeg7x9-C7mRB5fj-DOgQaLRi-yYTqC4-t-DKUNOg6F-BtruRO_z-BDQrJIZQ-ByHq-LMj-Drlk5OTb-BAoinR4q-CD8VtN79-Cidg8klg-CA9PGoiF.js +13728 -0
  51. package/devtools/dist/panel-entry-CdHyTMuR-wqllnGMv-CJFeg7x9-C7mRB5fj-DOgQaLRi-yYTqC4-t-DKUNOg6F-BtruRO_z-BDQrJIZQ-ByHq-LMj-Drlk5OTb-BAoinR4q-CD8VtN79-Cidg8klg-CA9PGoiF.js.map +1 -0
  52. package/devtools/dist/panel-entry-D53S3yYJ-DlRCzJ0q-BnWPSxII-B57eqdYD-CRpb7P7m-CSgQ8r4U-DqjeAoTP.js +13935 -0
  53. package/devtools/dist/panel-entry-D53S3yYJ-DlRCzJ0q-BnWPSxII-B57eqdYD-CRpb7P7m-CSgQ8r4U-DqjeAoTP.js.map +1 -0
  54. package/devtools/dist/panel-entry-D6EQIVsV-51lrmGiZ-D4qVD_00-DVb1ySI7-C_RfHhv5-D78tUI3_.js +13945 -0
  55. package/devtools/dist/panel-entry-D6EQIVsV-51lrmGiZ-D4qVD_00-DVb1ySI7-C_RfHhv5-D78tUI3_.js.map +1 -0
  56. package/devtools/dist/{panel-entry-Da9n85v7.js → panel-entry-Da9n85v7-C6Gam2oq-DDT89MNj-D9ZqvcEM-Dx--LZLa-BJiXK46m-DGuagWzT-B6evq62k-Cj2QcsAJ-BnxooYWW-D4oxy7MQ-g1-2DT91-DESRmxtW-BFO1EmVx-MWoTuzW5-Cg1SKwet.js} +4 -4
  57. package/devtools/dist/panel-entry-Da9n85v7-C6Gam2oq-DDT89MNj-D9ZqvcEM-Dx--LZLa-BJiXK46m-DGuagWzT-B6evq62k-Cj2QcsAJ-BnxooYWW-D4oxy7MQ-g1-2DT91-DESRmxtW-BFO1EmVx-MWoTuzW5-Cg1SKwet.js.map +1 -0
  58. package/devtools/dist/panel-entry-DeLbn5Tk-P7ZtOAd3-DXeez3fy-BFxPfyZi-9L9N-1f8-DzO_hXuM-CWXuuiJk-CI3ufk2T-CP3JflFQ-CLoczwy1-D7ukr7PE-DbMhopxi-BHxIMhDL-CDnadrxB.js +13999 -0
  59. package/devtools/dist/panel-entry-DeLbn5Tk-P7ZtOAd3-DXeez3fy-BFxPfyZi-9L9N-1f8-DzO_hXuM-CWXuuiJk-CI3ufk2T-CP3JflFQ-CLoczwy1-D7ukr7PE-DbMhopxi-BHxIMhDL-CDnadrxB.js.map +1 -0
  60. package/devtools/dist/panel-entry-Dh3i-JKj-D7NSYIQS-C_qI61WQ-BfJ0BW16-Df40_Q6R.js +13951 -0
  61. package/devtools/dist/panel-entry-Dh3i-JKj-D7NSYIQS-C_qI61WQ-BfJ0BW16-Df40_Q6R.js.map +1 -0
  62. package/devtools/dist/panel-entry-DlWAx2yA-vZadFV4d-DTvClgVl-zRu1NJo4-rPRjTg0e-dbBSWeq3-jx9KYEq_-B3sEcSWI-DeCc9Glr-02P4pIqr-CrzX1Fsr-C1C-Fcde.js +13921 -0
  63. package/devtools/dist/panel-entry-DlWAx2yA-vZadFV4d-DTvClgVl-zRu1NJo4-rPRjTg0e-dbBSWeq3-jx9KYEq_-B3sEcSWI-DeCc9Glr-02P4pIqr-CrzX1Fsr-C1C-Fcde.js.map +1 -0
  64. package/devtools/dist/{panel-entry-DzGsV7s--DzGsV7s--DzGsV7s-.js → panel-entry-DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s-.js} +1 -1
  65. package/devtools/dist/panel-entry-DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s--DzGsV7s-.js.map +1 -0
  66. package/devtools/dist/panel-entry-F_czAD5m-haMWJ-NK-CsYx2f8v-CczoKRsz-CIS8-qZe-F0ks_Y8t-CZBpt1gj-QmZJhT5N-460P-RpL-CKLofb-z-DEXl2ipp.js +13921 -0
  67. package/devtools/dist/panel-entry-F_czAD5m-haMWJ-NK-CsYx2f8v-CczoKRsz-CIS8-qZe-F0ks_Y8t-CZBpt1gj-QmZJhT5N-460P-RpL-CKLofb-z-DEXl2ipp.js.map +1 -0
  68. package/devtools/dist/panel-entry-L66PnOAy-CSPip1Cq-DIdgAB36-D56Z9rwz.js +13957 -0
  69. package/devtools/dist/panel-entry-L66PnOAy-CSPip1Cq-DIdgAB36-D56Z9rwz.js.map +1 -0
  70. package/devtools/dist/panel-entry.js +449 -193
  71. package/devtools/dist/panel-entry.js.map +1 -1
  72. package/docs/roadmap.md +5 -0
  73. package/examples/advanced-config.json +8 -0
  74. package/formatters/browser-formatter.js +5 -1
  75. package/formatters/cli-formatter.js +7 -1
  76. package/formatters/server-formatter.js +7 -3
  77. package/index.js +112 -7
  78. package/package.json +1 -1
  79. package/utils/redaction.js +51 -0
  80. package/devtools/dist/panel-entry-BZ56bT66-BMyD75mc.js.map +0 -1
  81. package/devtools/dist/panel-entry-Da9n85v7.js.map +0 -1
  82. package/devtools/dist/panel-entry-DzGsV7s--DzGsV7s--DzGsV7s-.js.map +0 -1
package/docs/roadmap.md CHANGED
@@ -435,6 +435,11 @@ Console filtering updates
435
435
 
436
436
  ### **🔮 Future Enhancements**
437
437
 
438
+ #### **Component Management** (Future)
439
+ - [ ] **Component removal notification** - Subscribe to component removal events (currently only addition events are supported)
440
+ - [ ] **Component lifecycle tracking** - Track when components are created and removed
441
+ - **Note:** Component addition notifications are implemented. Removal notifications are deferred for future enhancement (see TODO in `index.js:817`)
442
+
438
443
  #### **Advanced Filtering** (Phase 6.5)
439
444
  - [ ] **File-level controls** - Panel interface for file overrides
440
445
  - [ ] **Search filtering** - Filter logs by message content
@@ -16,6 +16,10 @@
16
16
  "jsonPayload": true,
17
17
  "stackTrace": true
18
18
  },
19
+ "redact": {
20
+ "paths": ["password", "token", "*key", "*secret", "*apiKey", "*api_key"],
21
+ "censor": "[REDACTED]"
22
+ },
19
23
  "components": {
20
24
  "core": {
21
25
  "emoji": "🎯",
@@ -116,6 +120,10 @@
116
120
  "display": {
117
121
  "jsonPayload": true,
118
122
  "stackTrace": true
123
+ },
124
+ "redact": {
125
+ "paths": ["password", "token", "*key", "*secret"],
126
+ "censor": "***"
119
127
  }
120
128
  },
121
129
  "src/database/*.js": {
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import {configManager} from '../config/config-manager.js';
8
+ import {redactValue} from '../utils/redaction.js';
8
9
 
9
10
  /**
10
11
  * Create browser console formatter for a specific component
@@ -28,6 +29,7 @@ export const createBrowserFormatter = (componentName, logStore = null) => {
28
29
  const component = configManager.getComponentConfig(componentName, filePath);
29
30
  const level = configManager.getLevelConfig(logData.level);
30
31
  const displayConfig = configManager.getDisplayConfig(filePath);
32
+ const redactConfig = configManager.getRedactConfig(filePath);
31
33
 
32
34
  // Check if this log should be displayed based on effective level
33
35
  const effectiveLevel = configManager.getEffectiveLevel(componentName, filePath);
@@ -84,7 +86,8 @@ export const createBrowserFormatter = (componentName, logStore = null) => {
84
86
  if (displayConfig.jsonPayload) {
85
87
  const contextData = extractContextData(logData);
86
88
  if (Object.keys(contextData).length > 0) {
87
- displayContextData(contextData);
89
+ const redactedData = redactValue(contextData, redactConfig);
90
+ displayContextData(redactedData);
88
91
  }
89
92
  }
90
93
 
@@ -158,6 +161,7 @@ function extractContextData(logData) {
158
161
  return contextData;
159
162
  }
160
163
 
164
+
161
165
  /**
162
166
  * Display context data with tree-like structure
163
167
  * @param {Object} contextData - Context data to display
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { LEVEL_SCHEME } from '../config/component-schemes.js';
7
7
  import { configManager } from '../config/config-manager.js';
8
+ import { redactValue } from '../utils/redaction.js';
8
9
 
9
10
  /**
10
11
  * Format a value for display in context tree
@@ -27,6 +28,7 @@ const formatValue = (value) => {
27
28
  return String(value);
28
29
  };
29
30
 
31
+
30
32
  /**
31
33
  * Create CLI formatter with context data support
32
34
  * @returns {Object} Stream-like object for Pino
@@ -62,10 +64,14 @@ export const createCLIFormatter = () => {
62
64
  const contextKeys = Object.keys(log).filter(key => !internalFields.includes(key));
63
65
 
64
66
  if (contextKeys.length > 0) {
67
+ // Get redact config and apply redaction
68
+ const redactConfig = configManager.getRedactConfig();
69
+ const redactedLog = redactValue(log, redactConfig);
70
+
65
71
  contextKeys.forEach((key, index) => {
66
72
  const isLast = index === contextKeys.length - 1;
67
73
  const prefix = isLast ? ' └─' : ' ├─';
68
- const value = formatValue(log[key]);
74
+ const value = formatValue(redactedLog[key]);
69
75
  console.log(`${prefix} ${key}: ${value}`);
70
76
  });
71
77
  }
@@ -3,6 +3,8 @@
3
3
  * Structured JSON output for production logging and log aggregation
4
4
  */
5
5
 
6
+ import {configManager} from '../config/config-manager.js';
7
+
6
8
  /**
7
9
  * Create server formatter (structured JSON)
8
10
  * @returns {null} No custom formatter - uses Pino's default JSON output
@@ -18,6 +20,8 @@ export const createServerFormatter = () => {
18
20
  * @returns {Object} Pino configuration for server environments
19
21
  */
20
22
  export const getServerConfig = () => {
23
+ const redactConfig = configManager.getRedactConfig();
24
+
21
25
  return {
22
26
  level: 'info', // More conservative logging in production
23
27
  formatters: {
@@ -33,10 +37,10 @@ export const getServerConfig = () => {
33
37
  };
34
38
  }
35
39
  },
36
- // Redact sensitive information in production
40
+ // Redact sensitive information in production (configurable)
37
41
  redact: {
38
- paths: ['password', 'token', 'key', 'secret'],
39
- censor: '[REDACTED]'
42
+ paths: redactConfig.paths.length > 0 ? redactConfig.paths : ['password', 'token', 'key', 'secret'],
43
+ censor: redactConfig.censor || '[REDACTED]'
40
44
  }
41
45
  };
42
46
  };
package/index.js CHANGED
@@ -57,6 +57,7 @@ class JSGLogger {
57
57
  this.environment = null; // Will be set after config loads
58
58
  this.initialized = false;
59
59
  this.components = {}; // Auto-discovery getters
60
+ this.componentSubscribers = []; // Subscribers for component change events
60
61
  }
61
62
 
62
63
  /**
@@ -75,6 +76,7 @@ class JSGLogger {
75
76
  // Make runtime controls available globally in browser for debugging
76
77
  if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
77
78
  window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
79
+ window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
78
80
  }
79
81
  } else if (hasOptions) {
80
82
  // Instance exists but new options provided - reinitialize
@@ -84,6 +86,7 @@ class JSGLogger {
84
86
  // (same as getInstanceSync behavior)
85
87
  if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
86
88
  window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
89
+ window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
87
90
  }
88
91
  }
89
92
 
@@ -92,21 +95,62 @@ class JSGLogger {
92
95
 
93
96
  /**
94
97
  * Get singleton instance synchronously (for environments without async support)
98
+ * Checks window.JSG_Logger first to ensure singleton works across separate bundles
95
99
  * @param {Object} options - Initialization options (only used on first call)
96
100
  * @returns {Object} Enhanced logger exports with controls API
97
101
  */
98
102
  static getInstanceSync(options = {}) {
99
103
  const hasOptions = options && Object.keys(options).length > 0;
104
+
105
+ // If options are provided, we need to reinitialize even if global instance exists
106
+ // This ensures config changes (like devtools.enabled) are applied
107
+ if (hasOptions) {
108
+ // Reinitialize with new options - this will update the global references
109
+ if (!JSGLogger._instance) {
110
+ JSGLogger._instance = new JSGLogger();
111
+ }
112
+ JSGLogger._enhancedLoggers = JSGLogger._instance.initSync(options);
113
+
114
+ // Update global references after reinitialization
115
+ if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
116
+ window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
117
+ window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
118
+ }
119
+
120
+ return JSGLogger._enhancedLoggers;
121
+ }
122
+
123
+ // No options provided - check window.JSG_Logger first to ensure singleton works across bundles
124
+ // This is critical when devtools bundle loads separately from main app
125
+ if (isBrowser() && typeof window !== 'undefined' && window.JSG_Logger) {
126
+ // If enhanced loggers reference exists, use it (preferred)
127
+ if (window.__JSG_Logger_Enhanced__) {
128
+ return window.__JSG_Logger_Enhanced__;
129
+ }
130
+ // Fallback: reconstruct enhanced loggers from controls object
131
+ // This handles cases where __JSG_Logger_Enhanced__ wasn't set
132
+ const globalControls = window.JSG_Logger;
133
+ return {
134
+ ...globalControls,
135
+ controls: globalControls,
136
+ getComponent: globalControls.getComponent,
137
+ getInstanceSync: JSGLogger.getInstanceSync.bind(JSGLogger),
138
+ getInstance: JSGLogger.getInstance.bind(JSGLogger),
139
+ logPerformance: JSGLogger.logPerformance.bind(JSGLogger),
140
+ JSGLogger: JSGLogger
141
+ };
142
+ }
100
143
 
144
+ // No options and no global instance - first time initialization
101
145
  if (!JSGLogger._instance) {
102
- // First time initialization
103
146
  JSGLogger._instance = new JSGLogger();
104
147
  JSGLogger._enhancedLoggers = JSGLogger._instance.initSync(options);
105
- } else if (hasOptions) {
106
- // Instance exists but new options provided - reinitialize
107
- // Only reinit if flag is not set (first init hasn't completed yet)
108
- // or if we need to apply new config
109
- JSGLogger._enhancedLoggers = JSGLogger._instance.initSync(options);
148
+
149
+ // Make runtime controls available globally in browser for debugging
150
+ if (isBrowser() && typeof window !== 'undefined' && JSGLogger._enhancedLoggers?.controls) {
151
+ window.JSG_Logger = JSGLogger._enhancedLoggers.controls;
152
+ window.__JSG_Logger_Enhanced__ = JSGLogger._enhancedLoggers;
153
+ }
110
154
  }
111
155
 
112
156
  return JSGLogger._enhancedLoggers;
@@ -282,6 +326,19 @@ class JSGLogger {
282
326
 
283
327
  // Create auto-discovery getters (eager)
284
328
  this._createAutoDiscoveryGetters();
329
+
330
+ // Notify component subscribers after reinitialization (if components changed)
331
+ // This ensures panel sees components created during reinit, not just via getComponent()
332
+ if (this.componentSubscribers.length > 0) {
333
+ const currentComponents = Object.keys(this.loggers);
334
+ this.componentSubscribers.forEach(callback => {
335
+ try {
336
+ callback(currentComponents);
337
+ } catch (error) {
338
+ metaError('Component subscriber error:', error);
339
+ }
340
+ });
341
+ }
285
342
 
286
343
  this.initialized = true;
287
344
 
@@ -494,7 +551,25 @@ class JSGLogger {
494
551
  },
495
552
 
496
553
  // Component controls
497
- listComponents: () => configManager.getAvailableComponents(),
554
+ listComponents: () => Object.keys(this.loggers),
555
+ subscribeToComponents: (callback) => {
556
+ this.componentSubscribers.push(callback);
557
+ // Immediately call callback with current component list (don't wait for changes)
558
+ // This ensures panel sees components created during reinitialization
559
+ const currentComponents = Object.keys(this.loggers);
560
+ try {
561
+ callback(currentComponents);
562
+ } catch (error) {
563
+ this.loggers.core?.error('Component subscriber error (initial call):', error);
564
+ }
565
+ // Return unsubscribe function
566
+ return () => {
567
+ const index = this.componentSubscribers.indexOf(callback);
568
+ if (index > -1) {
569
+ this.componentSubscribers.splice(index, 1);
570
+ }
571
+ };
572
+ },
498
573
  enableDebugMode: () => {
499
574
  Object.keys(this.loggers).forEach(component => {
500
575
  if (this.loggers[component]) {
@@ -801,6 +876,26 @@ class JSGLogger {
801
876
  if (camelName !== componentName) {
802
877
  this.components[camelName] = () => this.getComponent(componentName);
803
878
  }
879
+
880
+ // Notify component subscribers of the change (emit on every add, not deduplicated)
881
+ // TODO: Add component removal notification support (future enhancement)
882
+ const currentComponents = Object.keys(this.loggers);
883
+ if (this.componentSubscribers.length > 0) {
884
+ if (this.loggers.core) {
885
+ this.loggers.core.debug(`Notifying ${this.componentSubscribers.length} subscribers of component change`, {
886
+ component: componentName,
887
+ totalComponents: currentComponents.length,
888
+ components: currentComponents
889
+ });
890
+ }
891
+ }
892
+ this.componentSubscribers.forEach(callback => {
893
+ try {
894
+ callback(currentComponents);
895
+ } catch (error) {
896
+ this.loggers.core?.error('Component subscriber error:', error);
897
+ }
898
+ });
804
899
  }
805
900
 
806
901
  return this.loggers[componentName];
@@ -858,10 +953,18 @@ class JSGLogger {
858
953
 
859
954
  /**
860
955
  * Get singleton controls without triggering initialization
956
+ * Checks window.JSG_Logger first to ensure singleton works across separate bundles
861
957
  * Returns the controls object from the current singleton instance if it exists
862
958
  * @returns {Object|null} Controls object or null if singleton not initialized
863
959
  */
864
960
  static getControls() {
961
+ // Check window.JSG_Logger first to ensure singleton works across bundles
962
+ // This is critical when devtools bundle loads separately from main app
963
+ if (isBrowser() && typeof window !== 'undefined' && window.JSG_Logger) {
964
+ return window.JSG_Logger;
965
+ }
966
+
967
+ // Fall back to local instance if global doesn't exist
865
968
  return JSGLogger._enhancedLoggers?.controls || null;
866
969
  }
867
970
  }
@@ -871,8 +974,10 @@ class JSGLogger {
871
974
  const enhancedLoggers = JSGLogger.getInstanceSync();
872
975
 
873
976
  // Make runtime controls available globally in browser for debugging
977
+ // Also store enhanced loggers reference so getInstanceSync() can access it across bundles
874
978
  if (isBrowser() && typeof window !== 'undefined') {
875
979
  window.JSG_Logger = enhancedLoggers.controls;
980
+ window.__JSG_Logger_Enhanced__ = enhancedLoggers;
876
981
  }
877
982
 
878
983
  // Add static methods to the enhanced loggers for convenience
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimsonsunset/jsg-logger",
3
- "version": "1.7.13",
3
+ "version": "1.7.15",
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",
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Redaction utility functions for sensitive data protection
3
+ * Used by browser, CLI, and server formatters
4
+ */
5
+
6
+ /**
7
+ * Check if a key should be redacted based on patterns
8
+ * @param {string} key - Key to check
9
+ * @param {string[]} paths - Array of patterns (exact match or wildcard like *key)
10
+ * @returns {boolean} Whether key should be redacted
11
+ */
12
+ export function shouldRedactKey(key, paths) {
13
+ return paths.some(pattern => {
14
+ if (pattern.startsWith('*')) {
15
+ const suffix = pattern.slice(1).toLowerCase();
16
+ return key.toLowerCase().endsWith(suffix);
17
+ }
18
+ return key.toLowerCase() === pattern.toLowerCase();
19
+ });
20
+ }
21
+
22
+ /**
23
+ * Redact sensitive values from an object based on key patterns
24
+ * @param {*} value - Value to redact (object, array, or primitive)
25
+ * @param {Object} redactConfig - Redaction configuration with paths and censor
26
+ * @returns {*} Redacted value
27
+ */
28
+ export function redactValue(value, redactConfig) {
29
+ if (!redactConfig || !redactConfig.paths || redactConfig.paths.length === 0) {
30
+ return value;
31
+ }
32
+
33
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
34
+ const redacted = {};
35
+ for (const [key, val] of Object.entries(value)) {
36
+ if (shouldRedactKey(key, redactConfig.paths)) {
37
+ redacted[key] = redactConfig.censor || '[REDACTED]';
38
+ } else {
39
+ redacted[key] = redactValue(val, redactConfig);
40
+ }
41
+ }
42
+ return redacted;
43
+ }
44
+
45
+ if (Array.isArray(value)) {
46
+ return value.map(item => redactValue(item, redactConfig));
47
+ }
48
+
49
+ return value;
50
+ }
51
+