@mks2508/better-logger 0.0.2-alpha.2 → 0.3.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.
Files changed (326) hide show
  1. package/.claude/settings.local.json +18 -2
  2. package/.github/workflows/ci-quality.yml +357 -0
  3. package/.github/workflows/docs-demo.yml +119 -0
  4. package/.github/workflows/releases-core.yml +512 -0
  5. package/.github/workflows/releases-full.yml +582 -0
  6. package/.github/{workflows → workflows-backup}/ci.yml +12 -110
  7. package/.github/workflows-backup/nightly.yml +196 -0
  8. package/.github/workflows-backup/release-optimized.yml +373 -0
  9. package/.release-notes-0.2.0.md +129 -0
  10. package/.yamllint +28 -0
  11. package/CHANGELOG.json +783 -0
  12. package/CLAUDE.md +92 -1
  13. package/README.md +198 -490
  14. package/bun.lock +323 -0
  15. package/demo.html +9 -2
  16. package/dist/.tsbuildinfo +1 -0
  17. package/dist/Logger.d.ts +272 -0
  18. package/dist/Logger.d.ts.map +1 -0
  19. package/dist/Logger.js +680 -0
  20. package/dist/ScopedLogger.d.ts +126 -0
  21. package/dist/ScopedLogger.d.ts.map +1 -0
  22. package/dist/ScopedLogger.js +234 -0
  23. package/dist/chunks/Logger-BpeuYIxR.js +2 -0
  24. package/dist/chunks/Logger-BpeuYIxR.js.map +1 -0
  25. package/dist/chunks/Logger-Didxtr23.js +3275 -0
  26. package/dist/chunks/Logger-Didxtr23.js.map +1 -0
  27. package/dist/chunks/RemoteLogHandler-CjWpWZGl.js +33 -0
  28. package/dist/chunks/RemoteLogHandler-CjWpWZGl.js.map +1 -0
  29. package/dist/chunks/RemoteLogHandler-ymkQ97xl.js +2 -0
  30. package/dist/chunks/RemoteLogHandler-ymkQ97xl.js.map +1 -0
  31. package/dist/chunks/environment-Bxbnw4pd.js +544 -0
  32. package/dist/chunks/environment-Bxbnw4pd.js.map +1 -0
  33. package/dist/chunks/environment-Dm66zOML.js +4 -0
  34. package/dist/chunks/environment-Dm66zOML.js.map +1 -0
  35. package/dist/chunks/formatting-C0riPC5_.js +95 -0
  36. package/dist/chunks/formatting-C0riPC5_.js.map +1 -0
  37. package/dist/chunks/formatting-DNCAV66-.js +2 -0
  38. package/dist/chunks/formatting-DNCAV66-.js.map +1 -0
  39. package/dist/cli/CommandProcessor.d.ts +103 -0
  40. package/dist/cli/CommandProcessor.d.ts.map +1 -0
  41. package/dist/cli/CommandProcessor.js +184 -0
  42. package/dist/cli/commands/ConfigCommand.d.ts +17 -0
  43. package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
  44. package/dist/cli/commands/ConfigCommand.js +88 -0
  45. package/dist/cli/commands/ExportCommand.d.ts +51 -0
  46. package/dist/cli/commands/ExportCommand.d.ts.map +1 -0
  47. package/dist/cli/commands/ExportCommand.js +243 -0
  48. package/dist/cli/commands/HistoryCommand.d.ts +50 -0
  49. package/dist/cli/commands/HistoryCommand.d.ts.map +1 -0
  50. package/dist/cli/commands/HistoryCommand.js +96 -0
  51. package/dist/cli/commands/StatusCommand.d.ts +33 -0
  52. package/dist/cli/commands/StatusCommand.d.ts.map +1 -0
  53. package/dist/cli/commands/StatusCommand.js +93 -0
  54. package/dist/cli/commands/ThemeCommand.d.ts +33 -0
  55. package/dist/cli/commands/ThemeCommand.d.ts.map +1 -0
  56. package/dist/cli/commands/ThemeCommand.js +79 -0
  57. package/dist/cli/help.d.ts +15 -0
  58. package/dist/cli/help.d.ts.map +1 -0
  59. package/dist/cli/help.js +118 -0
  60. package/dist/cli/index.d.ts +15 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +46 -0
  63. package/dist/constants.d.ts +122 -0
  64. package/dist/constants.d.ts.map +1 -0
  65. package/dist/constants.js +159 -0
  66. package/dist/core.cjs +1 -1
  67. package/dist/core.cjs.map +1 -1
  68. package/dist/core.d.ts +134 -0
  69. package/dist/core.d.ts.map +1 -0
  70. package/dist/core.js +315 -242
  71. package/dist/core.js.map +1 -1
  72. package/dist/example.d.ts +18 -0
  73. package/dist/example.d.ts.map +1 -0
  74. package/dist/example.js +154 -0
  75. package/dist/exports-module.d.ts +203 -0
  76. package/dist/exports-module.d.ts.map +1 -0
  77. package/dist/exports-module.js +260 -0
  78. package/dist/exports.cjs +1 -1
  79. package/dist/exports.cjs.map +1 -1
  80. package/dist/exports.js +5 -4
  81. package/dist/exports.js.map +1 -1
  82. package/dist/handlers/AnalyticsLogHandler.d.ts +11 -0
  83. package/dist/handlers/AnalyticsLogHandler.d.ts.map +1 -0
  84. package/dist/handlers/AnalyticsLogHandler.js +19 -0
  85. package/dist/handlers/ExportLogHandler.d.ts +103 -0
  86. package/dist/handlers/ExportLogHandler.d.ts.map +1 -0
  87. package/dist/handlers/ExportLogHandler.js +516 -0
  88. package/dist/handlers/FileLogHandler.d.ts +36 -0
  89. package/dist/handlers/FileLogHandler.d.ts.map +1 -0
  90. package/dist/handlers/FileLogHandler.js +156 -0
  91. package/dist/handlers/RemoteLogHandler.d.ts +14 -0
  92. package/dist/handlers/RemoteLogHandler.d.ts.map +1 -0
  93. package/dist/handlers/RemoteLogHandler.js +37 -0
  94. package/dist/handlers/index.d.ts +8 -0
  95. package/dist/handlers/index.d.ts.map +1 -0
  96. package/dist/handlers/index.js +7 -0
  97. package/dist/index.cjs +1 -1
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.d.ts +86 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +89 -103
  102. package/dist/index.js.map +1 -1
  103. package/dist/main.d.ts +2 -0
  104. package/dist/main.d.ts.map +1 -0
  105. package/dist/main.js +139 -0
  106. package/dist/styling/LogStyleBuilder.d.ts +149 -0
  107. package/dist/styling/LogStyleBuilder.d.ts.map +1 -0
  108. package/dist/styling/LogStyleBuilder.js +289 -0
  109. package/dist/styling/SemanticStyles.d.ts +181 -0
  110. package/dist/styling/SemanticStyles.d.ts.map +1 -0
  111. package/dist/styling/SemanticStyles.js +339 -0
  112. package/dist/styling/SmartPresets.d.ts +25 -0
  113. package/dist/styling/SmartPresets.d.ts.map +1 -0
  114. package/dist/styling/SmartPresets.js +276 -0
  115. package/dist/styling/StyleBuilder.d.ts +140 -0
  116. package/dist/styling/StyleBuilder.d.ts.map +1 -0
  117. package/dist/styling/StyleBuilder.js +280 -0
  118. package/dist/styling/banners.d.ts +45 -0
  119. package/dist/styling/banners.d.ts.map +1 -0
  120. package/dist/styling/banners.js +155 -0
  121. package/dist/styling/index.d.ts +10 -0
  122. package/dist/styling/index.d.ts.map +1 -0
  123. package/dist/styling/index.js +9 -0
  124. package/dist/styling/themes.d.ts +10 -0
  125. package/dist/styling/themes.d.ts.map +1 -0
  126. package/dist/styling/themes.js +231 -0
  127. package/dist/styling-module.d.ts +185 -0
  128. package/dist/styling-module.d.ts.map +1 -0
  129. package/dist/styling-module.js +199 -0
  130. package/dist/styling.cjs +1 -1
  131. package/dist/styling.cjs.map +1 -1
  132. package/dist/styling.js +6 -6
  133. package/dist/styling.js.map +1 -1
  134. package/dist/types/Logger.d.ts +268 -0
  135. package/dist/types/Logger.d.ts.map +1 -0
  136. package/dist/types/ScopedLogger.d.ts +123 -0
  137. package/dist/types/ScopedLogger.d.ts.map +1 -0
  138. package/dist/types/cli/CommandProcessor.d.ts +100 -0
  139. package/dist/types/cli/CommandProcessor.d.ts.map +1 -0
  140. package/dist/types/cli/commands/ConfigCommand.d.ts +14 -0
  141. package/dist/types/cli/commands/ConfigCommand.d.ts.map +1 -0
  142. package/dist/types/cli/commands/ExportCommand.d.ts +48 -0
  143. package/dist/types/cli/commands/ExportCommand.d.ts.map +1 -0
  144. package/dist/types/cli/commands/HistoryCommand.d.ts +47 -0
  145. package/dist/types/cli/commands/HistoryCommand.d.ts.map +1 -0
  146. package/dist/types/cli/commands/StatusCommand.d.ts +30 -0
  147. package/dist/types/cli/commands/StatusCommand.d.ts.map +1 -0
  148. package/dist/types/cli/commands/ThemeCommand.d.ts +30 -0
  149. package/dist/types/cli/commands/ThemeCommand.d.ts.map +1 -0
  150. package/dist/types/cli/help.d.ts +12 -0
  151. package/dist/types/cli/help.d.ts.map +1 -0
  152. package/dist/types/cli/index.d.ts +15 -0
  153. package/dist/types/cli/index.d.ts.map +1 -0
  154. package/dist/types/constants.d.ts +119 -0
  155. package/dist/types/constants.d.ts.map +1 -0
  156. package/dist/types/core.d.ts +125 -211
  157. package/dist/types/core.d.ts.map +1 -0
  158. package/dist/types/core.js +13 -0
  159. package/dist/types/example.d.ts +18 -0
  160. package/dist/types/example.d.ts.map +1 -0
  161. package/dist/types/exports-module.d.ts +196 -0
  162. package/dist/types/exports-module.d.ts.map +1 -0
  163. package/dist/types/handlers/AnalyticsLogHandler.d.ts +8 -0
  164. package/dist/types/handlers/AnalyticsLogHandler.d.ts.map +1 -0
  165. package/dist/types/handlers/ExportLogHandler.d.ts +100 -0
  166. package/dist/types/handlers/ExportLogHandler.d.ts.map +1 -0
  167. package/dist/types/handlers/FileLogHandler.d.ts +33 -0
  168. package/dist/types/handlers/FileLogHandler.d.ts.map +1 -0
  169. package/dist/types/handlers/RemoteLogHandler.d.ts +11 -0
  170. package/dist/types/handlers/RemoteLogHandler.d.ts.map +1 -0
  171. package/dist/types/handlers/index.d.ts +8 -0
  172. package/dist/types/handlers/index.d.ts.map +1 -0
  173. package/dist/types/handlers.d.ts +88 -0
  174. package/dist/types/handlers.d.ts.map +1 -0
  175. package/dist/types/handlers.js +4 -0
  176. package/dist/types/index.d.ts +7 -675
  177. package/dist/types/index.d.ts.map +1 -0
  178. package/dist/types/index.js +4 -0
  179. package/dist/types/main.d.ts +2 -0
  180. package/dist/types/main.d.ts.map +1 -0
  181. package/dist/types/styling/LogStyleBuilder.d.ts +146 -0
  182. package/dist/types/styling/LogStyleBuilder.d.ts.map +1 -0
  183. package/dist/types/styling/SemanticStyles.d.ts +178 -0
  184. package/dist/types/styling/SemanticStyles.d.ts.map +1 -0
  185. package/dist/types/styling/SmartPresets.d.ts +22 -0
  186. package/dist/types/styling/SmartPresets.d.ts.map +1 -0
  187. package/dist/types/styling/StyleBuilder.d.ts +140 -0
  188. package/dist/types/styling/StyleBuilder.d.ts.map +1 -0
  189. package/dist/types/styling/banners.d.ts +42 -0
  190. package/dist/types/styling/banners.d.ts.map +1 -0
  191. package/dist/types/styling/index.d.ts +10 -0
  192. package/dist/types/styling/index.d.ts.map +1 -0
  193. package/dist/types/styling/themes.d.ts +7 -0
  194. package/dist/types/styling/themes.d.ts.map +1 -0
  195. package/dist/types/styling-module.d.ts +178 -0
  196. package/dist/types/styling-module.d.ts.map +1 -0
  197. package/dist/types/types/core.d.ts +125 -0
  198. package/dist/types/types/core.d.ts.map +1 -0
  199. package/dist/types/types/handlers.d.ts +85 -0
  200. package/dist/types/types/handlers.d.ts.map +1 -0
  201. package/dist/types/types/index.d.ts +7 -0
  202. package/dist/types/types/index.d.ts.map +1 -0
  203. package/dist/types/utils/environment.d.ts +47 -0
  204. package/dist/types/utils/environment.d.ts.map +1 -0
  205. package/dist/types/utils/formatting.d.ts +37 -0
  206. package/dist/types/utils/formatting.d.ts.map +1 -0
  207. package/dist/types/utils/index.d.ts +7 -0
  208. package/dist/types/utils/index.d.ts.map +1 -0
  209. package/dist/types/utils/opentui-detection.d.ts +34 -0
  210. package/dist/types/utils/opentui-detection.d.ts.map +1 -0
  211. package/dist/types/utils/output.d.ts +41 -0
  212. package/dist/types/utils/output.d.ts.map +1 -0
  213. package/dist/types/utils/stackTrace.d.ts +6 -0
  214. package/dist/types/utils/stackTrace.d.ts.map +1 -0
  215. package/dist/types/utils/timestamps.d.ts +20 -0
  216. package/dist/types/utils/timestamps.d.ts.map +1 -0
  217. package/dist/utils/environment.d.ts +47 -0
  218. package/dist/utils/environment.d.ts.map +1 -0
  219. package/dist/utils/environment.js +85 -0
  220. package/dist/utils/formatting.d.ts +40 -0
  221. package/dist/utils/formatting.d.ts.map +1 -0
  222. package/dist/utils/formatting.js +117 -0
  223. package/dist/utils/index.d.ts +7 -0
  224. package/dist/utils/index.d.ts.map +1 -0
  225. package/dist/utils/index.js +6 -0
  226. package/dist/utils/opentui-detection.d.ts +34 -0
  227. package/dist/utils/opentui-detection.d.ts.map +1 -0
  228. package/dist/utils/opentui-detection.js +116 -0
  229. package/dist/utils/output.d.ts +44 -0
  230. package/dist/utils/output.d.ts.map +1 -0
  231. package/dist/utils/output.js +150 -0
  232. package/dist/utils/stackTrace.d.ts +9 -0
  233. package/dist/utils/stackTrace.d.ts.map +1 -0
  234. package/dist/utils/stackTrace.js +81 -0
  235. package/dist/utils/timestamps.d.ts +20 -0
  236. package/dist/utils/timestamps.d.ts.map +1 -0
  237. package/dist/utils/timestamps.js +71 -0
  238. package/docs/API.md +691 -0
  239. package/docs/CORE.md +10 -10
  240. package/docs/DEVELOPMENT.md +731 -0
  241. package/docs/PACKAGES.md +244 -0
  242. package/docs/_config.yml +36 -0
  243. package/docs/index.md +179 -0
  244. package/examples/README.md +208 -0
  245. package/examples/basic-logging.js +75 -0
  246. package/examples/data-export.js +234 -0
  247. package/examples/package.json +16 -0
  248. package/examples/performance-timing.js +170 -0
  249. package/examples/simplified-api.js +124 -0
  250. package/examples/styling-themes.js +207 -0
  251. package/package.json +37 -21
  252. package/packages/core/package.json +57 -0
  253. package/packages/exports/package.json +41 -0
  254. package/packages/nodejs-opentui/README.md +232 -0
  255. package/packages/nodejs-opentui/package.json +72 -0
  256. package/packages/nodejs-opentui/src/LogRenderer.ts +171 -0
  257. package/packages/nodejs-opentui/src/OpenTUILogHandler.ts +178 -0
  258. package/packages/nodejs-opentui/src/components/LogBadge.tsx +131 -0
  259. package/packages/nodejs-opentui/src/index.ts +133 -0
  260. package/packages/nodejs-opentui/src/types.ts +158 -0
  261. package/packages/nodejs-opentui/tsconfig.json +20 -0
  262. package/packages/styling/package.json +41 -0
  263. package/project-utils/README.md +172 -0
  264. package/project-utils/auto-release-gemini.ts +1193 -0
  265. package/project-utils/auto-release-ui.ts +1329 -0
  266. package/project-utils/commit-generator.ts +1385 -0
  267. package/project-utils/commit-ui.ts +264 -0
  268. package/project-utils/git-utils.ts +199 -0
  269. package/project-utils/github-release-manager.ts +466 -0
  270. package/project-utils/project-config.ts +260 -0
  271. package/project-utils/prompt-templates.js +345 -0
  272. package/project-utils/prompt-templates.ts +422 -0
  273. package/project-utils/version-manager.ts +1078 -0
  274. package/src/Logger.ts +251 -16
  275. package/src/ScopedLogger.ts +262 -0
  276. package/src/cli/CommandProcessor.ts +173 -2
  277. package/src/cli/commands/HistoryCommand.ts +117 -0
  278. package/src/cli/index.ts +12 -0
  279. package/src/constants.ts +84 -2
  280. package/src/core.ts +119 -46
  281. package/src/example.ts +4 -4
  282. package/src/exports-module.ts +1 -1
  283. package/src/handlers/ExportLogHandler.ts +141 -0
  284. package/src/handlers/FileLogHandler.ts +144 -5
  285. package/src/index.ts +1 -1
  286. package/src/main.ts +3 -3
  287. package/src/styling/LogStyleBuilder.ts +355 -0
  288. package/src/styling/SemanticStyles.ts +380 -0
  289. package/src/styling/SmartPresets.ts +288 -0
  290. package/src/styling/StyleBuilder.ts +14 -0
  291. package/src/styling/index.ts +16 -1
  292. package/src/styling-module.ts +1 -1
  293. package/src/types/core.ts +60 -0
  294. package/src/types/index.ts +6 -0
  295. package/src/utils/environment.ts +94 -0
  296. package/src/utils/formatting.ts +142 -0
  297. package/src/utils/index.ts +3 -0
  298. package/src/utils/opentui-detection.ts +138 -0
  299. package/src/utils/output.ts +66 -8
  300. package/test-core-browser.html +237 -0
  301. package/test-core-node.js +63 -0
  302. package/tests/test-conflict-resolution.ts +391 -0
  303. package/tests/validate-workflows.sh +131 -0
  304. package/tests/yaml-autofix.sh +146 -0
  305. package/vite.config.ts +189 -50
  306. package/.npmrc.bak +0 -1
  307. package/dist/chunks/Logger-BQhMKy_T.js +0 -2
  308. package/dist/chunks/Logger-BQhMKy_T.js.map +0 -1
  309. package/dist/chunks/Logger-BrFKFZcD.js +0 -978
  310. package/dist/chunks/Logger-BrFKFZcD.js.map +0 -1
  311. package/dist/chunks/core-2opW4Pi3.js +0 -194
  312. package/dist/chunks/core-2opW4Pi3.js.map +0 -1
  313. package/dist/chunks/core-DyugwSYZ.js +0 -4
  314. package/dist/chunks/core-DyugwSYZ.js.map +0 -1
  315. package/dist/chunks/exports-BNP3R7dp.js +0 -421
  316. package/dist/chunks/exports-BNP3R7dp.js.map +0 -1
  317. package/dist/chunks/exports-U1xLBXrY.js +0 -2
  318. package/dist/chunks/exports-U1xLBXrY.js.map +0 -1
  319. package/dist/chunks/styling-DhUDzwlE.js +0 -654
  320. package/dist/chunks/styling-DhUDzwlE.js.map +0 -1
  321. package/dist/chunks/styling-tmRDI28D.js +0 -2
  322. package/dist/chunks/styling-tmRDI28D.js.map +0 -1
  323. package/dist/types/exports.d.ts +0 -600
  324. package/dist/types/styling.d.ts +0 -751
  325. package/src/Logger.ts.backup +0 -1684
  326. /package/.github/{workflows → workflows-backup}/release.yml +0 -0
package/src/core.ts CHANGED
@@ -16,14 +16,33 @@ import type {
16
16
  TimerEntry
17
17
  } from './types/index.js';
18
18
 
19
- // Core utilities only
19
+ // Core utilities
20
20
  import {
21
21
  parseStackTrace,
22
22
  formatTimestamp
23
23
  } from './utils/index.js';
24
24
 
25
- // Minimal constants
26
- import { DEFAULT_CONFIG } from './constants.js';
25
+ // Universal formatting and environment detection
26
+ import {
27
+ createPlainOutput,
28
+ getConsoleMethod,
29
+ createLogEntry,
30
+ formatTablePlain,
31
+ safeSerialize
32
+ } from './utils/formatting.js';
33
+
34
+ import {
35
+ isBrowser,
36
+ isNode,
37
+ getRuntimeEnvironment,
38
+ supportsColors,
39
+ supportsCSSColors
40
+ } from './utils/environment.js';
41
+
42
+ import { createStyledOutput } from './utils/output.js';
43
+
44
+ // Constants
45
+ import { DEFAULT_CONFIG, LEVEL_STYLES } from './constants.js';
27
46
 
28
47
  /**
29
48
  * Minimal Logger class with core functionality only
@@ -52,10 +71,11 @@ export class CoreLogger {
52
71
  this.config = {
53
72
  ...DEFAULT_CONFIG,
54
73
  ...config,
55
- // Force disable advanced features for core module
56
- enableColors: false,
74
+ // Auto-configure based on environment
75
+ enableColors: config.enableColors ?? (isBrowser ? true : false),
57
76
  enableTimestamps: config.enableTimestamps ?? true,
58
77
  enableStackTrace: config.enableStackTrace ?? false,
78
+ autoDetectTheme: config.autoDetectTheme ?? true
59
79
  };
60
80
  }
61
81
 
@@ -85,7 +105,7 @@ export class CoreLogger {
85
105
  /**
86
106
  * Creates a scoped logger with a specific prefix
87
107
  */
88
- createScopedLogger(prefix: string): CoreLogger {
108
+ scope(prefix: string): CoreLogger {
89
109
  const scopedLogger = new CoreLogger(this.config);
90
110
  scopedLogger.scopedPrefix = prefix;
91
111
  scopedLogger.handlers = [...this.handlers];
@@ -119,49 +139,53 @@ export class CoreLogger {
119
139
  }
120
140
 
121
141
  /**
122
- * Core logging method with minimal formatting
142
+ * Core logging method with universal formatting
123
143
  */
124
144
  private log(level: LogLevel, ...args: any[]): void {
125
145
  if (!this.shouldLog(level)) return;
126
146
 
127
147
  const prefix = this.getEffectivePrefix();
128
- const timestamp = this.config.enableTimestamps ? formatTimestamp() : null;
148
+ const message = String(args[0] || '');
129
149
  const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
150
+ const consoleMethod = getConsoleMethod(level);
130
151
 
131
- // Simple formatting without CSS styling
132
- const parts: string[] = [];
133
-
134
- if (timestamp) {
135
- parts.push(`[${timestamp.slice(11, 23)}]`);
136
- }
137
-
138
- parts.push(`[${level.toUpperCase()}]`);
139
-
140
- if (prefix) {
141
- parts.push(`[${prefix}]`);
142
- }
143
-
144
- const groupIndent = ' '.repeat(this.groupDepth);
145
- const logPrefix = groupIndent + parts.join(' ');
146
-
147
- // Output to console with simple formatting
148
- console.log(logPrefix, ...args);
149
-
150
- if (stackInfo && this.config.enableStackTrace) {
151
- console.log(` at ${stackInfo.file}:${stackInfo.line}:${stackInfo.column}`);
152
+ // Choose formatting based on environment and configuration
153
+ if (isBrowser && this.config.enableColors && supportsCSSColors()) {
154
+ // Browser with CSS styling
155
+ try {
156
+ const [format, ...styles] = createStyledOutput(
157
+ level,
158
+ LEVEL_STYLES,
159
+ prefix,
160
+ message,
161
+ stackInfo,
162
+ this.config.autoDetectTheme
163
+ );
164
+ console[consoleMethod](format, ...styles, ...args.slice(1));
165
+ } catch (error) {
166
+ // Fallback to plain text if CSS fails
167
+ const output = createPlainOutput(level, message, prefix, stackInfo);
168
+ console[consoleMethod](output, ...args.slice(1));
169
+ }
170
+ } else {
171
+ // Node.js or browser without colors - use plain text
172
+ const groupIndent = ' '.repeat(this.groupDepth);
173
+ const output = groupIndent + createPlainOutput(level, message, prefix, stackInfo);
174
+ console[consoleMethod](output, ...args.slice(1));
152
175
  }
153
176
 
154
- // Call custom handlers
177
+ // Call custom handlers with structured metadata
178
+ const logEntry = createLogEntry(level, message, args, prefix, stackInfo);
155
179
  const metadata: LogMetadata = {
156
- timestamp: timestamp || formatTimestamp(),
180
+ timestamp: logEntry.timestamp,
157
181
  level,
158
182
  prefix,
159
- stackInfo: stackInfo || undefined,
183
+ stackInfo: logEntry.location,
160
184
  };
161
185
 
162
186
  this.handlers.forEach(handler => {
163
187
  try {
164
- handler.handle(level, String(args[0] || ''), args, metadata);
188
+ handler.handle(level, message, args, metadata);
165
189
  } catch (error) {
166
190
  console.error('Log handler failed:', error);
167
191
  }
@@ -222,12 +246,21 @@ export class CoreLogger {
222
246
  if (!this.shouldLog('info')) return;
223
247
 
224
248
  const prefix = this.getEffectivePrefix();
225
- console.log(`[TABLE]${prefix ? ` [${prefix}]` : ''}:`);
249
+ const tableHeader = `[TABLE]${prefix ? ` [${prefix}]` : ''}:`;
226
250
 
227
- if (columns) {
228
- console.table(data, columns);
251
+ if (isBrowser && typeof console.table === 'function') {
252
+ // Browser has native table support
253
+ console.log(tableHeader);
254
+ if (columns) {
255
+ console.table(data, columns);
256
+ } else {
257
+ console.table(data);
258
+ }
229
259
  } else {
230
- console.table(data);
260
+ // Node.js or fallback - use plain text formatting
261
+ console.log(tableHeader);
262
+ const tableText = formatTablePlain(data, columns);
263
+ console.log(tableText);
231
264
  }
232
265
  }
233
266
 
@@ -238,10 +271,17 @@ export class CoreLogger {
238
271
  const prefix = this.getEffectivePrefix();
239
272
  const fullLabel = `${prefix ? `[${prefix}] ` : ''}${label}`;
240
273
 
241
- if (collapsed) {
242
- console.groupCollapsed(fullLabel);
274
+ if (isBrowser && console.group && console.groupCollapsed) {
275
+ // Browser supports native grouping
276
+ if (collapsed) {
277
+ console.groupCollapsed(fullLabel);
278
+ } else {
279
+ console.group(fullLabel);
280
+ }
243
281
  } else {
244
- console.group(fullLabel);
282
+ // Node.js fallback - just log the group label
283
+ const groupStart = '┌─ ' + fullLabel;
284
+ console.log(groupStart);
245
285
  }
246
286
 
247
287
  this.groupDepth++;
@@ -252,7 +292,13 @@ export class CoreLogger {
252
292
  */
253
293
  groupEnd(): void {
254
294
  if (this.groupDepth > 0) {
255
- console.groupEnd();
295
+ if (isBrowser && console.groupEnd) {
296
+ console.groupEnd();
297
+ } else {
298
+ // Node.js fallback - log group end
299
+ const groupEnd = '└─ (end group)';
300
+ console.log(groupEnd);
301
+ }
256
302
  this.groupDepth--;
257
303
  }
258
304
  }
@@ -261,12 +307,17 @@ export class CoreLogger {
261
307
  * Starts a timer with the given label
262
308
  */
263
309
  time(label: string): void {
310
+ const startTime = this.getPerformanceNow();
264
311
  const timer: TimerEntry = {
265
312
  label,
266
- startTime: performance.now(),
313
+ startTime,
267
314
  };
268
315
  this.timers.set(label, timer);
269
- console.log(`[TIMER] Started: ${label}`);
316
+
317
+ const prefix = this.getEffectivePrefix();
318
+ const message = `Timer started: ${label}`;
319
+ const output = createPlainOutput('info', message, prefix);
320
+ console.log(output);
270
321
  }
271
322
 
272
323
  /**
@@ -279,9 +330,31 @@ export class CoreLogger {
279
330
  return;
280
331
  }
281
332
 
282
- const elapsed = performance.now() - timer.startTime;
333
+ const elapsed = this.getPerformanceNow() - timer.startTime;
283
334
  this.timers.delete(label);
284
- console.log(`[TIMER] ${label}: ${elapsed.toFixed(2)}ms`);
335
+
336
+ const prefix = this.getEffectivePrefix();
337
+ const message = `Timer ended: ${label} - ${elapsed.toFixed(2)}ms`;
338
+ const output = createPlainOutput('info', message, prefix);
339
+ console.log(output);
340
+ }
341
+
342
+ /**
343
+ * Gets performance.now() or fallback for older environments
344
+ */
345
+ private getPerformanceNow(): number {
346
+ if (typeof performance !== 'undefined' && performance.now) {
347
+ return performance.now();
348
+ }
349
+
350
+ if (isNode && process.hrtime) {
351
+ // Node.js high-resolution time
352
+ const [seconds, nanoseconds] = process.hrtime();
353
+ return seconds * 1000 + nanoseconds / 1000000;
354
+ }
355
+
356
+ // Fallback to Date.now()
357
+ return Date.now();
285
358
  }
286
359
  }
287
360
 
@@ -303,7 +376,7 @@ export const groupEnd = () => coreLogger.groupEnd();
303
376
  export const time = (label: string) => coreLogger.time(label);
304
377
  export const timeEnd = (label: string) => coreLogger.timeEnd(label);
305
378
  export const setGlobalPrefix = (prefix: string) => coreLogger.setGlobalPrefix(prefix);
306
- export const createScopedLogger = (prefix: string) => coreLogger.createScopedLogger(prefix);
379
+ export const scope = (prefix: string) => coreLogger.scope(prefix);
307
380
  export const setVerbosity = (level: Verbosity) => coreLogger.setVerbosity(level);
308
381
  export const addHandler = (handler: ILogHandler) => coreLogger.addHandler(handler);
309
382
 
package/src/example.ts CHANGED
@@ -10,7 +10,7 @@ import logger, {
10
10
  error,
11
11
  success,
12
12
  critical,
13
- createScopedLogger,
13
+ scope,
14
14
  setGlobalPrefix,
15
15
  setVerbosity,
16
16
  setTheme,
@@ -151,9 +151,9 @@ export function demonstrateTiming() {
151
151
  }
152
152
 
153
153
  export function demonstrateScopedLogger() {
154
- const apiLogger = createScopedLogger('API');
155
- const dbLogger = createScopedLogger('DB');
156
- const authLogger = createScopedLogger('AUTH');
154
+ const apiLogger = scope('API');
155
+ const dbLogger = scope('DB');
156
+ const authLogger = scope('AUTH');
157
157
 
158
158
  apiLogger.info('🌐 Fetching user data', { endpoint: '/api/users' });
159
159
  dbLogger.debug('💾 Database query executed', { query: 'SELECT * FROM users LIMIT 10' });
@@ -280,7 +280,7 @@ export const groupEnd = () => exportLogger.groupEnd();
280
280
  export const time = (label: string) => exportLogger.time(label);
281
281
  export const timeEnd = (label: string) => exportLogger.timeEnd(label);
282
282
  export const setGlobalPrefix = (prefix: string) => exportLogger.setGlobalPrefix(prefix);
283
- export const createScopedLogger = (prefix: string) => exportLogger.createScopedLogger(prefix);
283
+ export const scope = (prefix: string) => exportLogger.scope(prefix);
284
284
  export const setVerbosity = (level: Verbosity) => exportLogger.setVerbosity(level);
285
285
  export const addHandler = (handler: ILogHandler) => exportLogger.addHandler(handler);
286
286
 
@@ -465,4 +465,145 @@ export class ExportLogHandler implements ILogHandler {
465
465
  getAllLogs(): LogEntry[] {
466
466
  return [...this.buffer];
467
467
  }
468
+
469
+ /**
470
+ * Save export to file with download prompt
471
+ */
472
+ async saveToFile(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): Promise<boolean> {
473
+ try {
474
+ const result = this.export(format, filters, options);
475
+ const blob = new Blob([result.data], {
476
+ type: this.getMimeType(format)
477
+ });
478
+
479
+ const url = URL.createObjectURL(blob);
480
+ const a = document.createElement('a');
481
+ const filename = this.generateFilename(format, filters);
482
+
483
+ a.href = url;
484
+ a.download = filename;
485
+ a.style.display = 'none';
486
+ document.body.appendChild(a);
487
+ a.click();
488
+ document.body.removeChild(a);
489
+ URL.revokeObjectURL(url);
490
+
491
+ return true;
492
+ } catch (error) {
493
+ console.error('Failed to save file:', error);
494
+ return false;
495
+ }
496
+ }
497
+
498
+ /**
499
+ * Get MIME type for format
500
+ */
501
+ private getMimeType(format: ExportFormat): string {
502
+ const mimeTypes = {
503
+ json: 'application/json',
504
+ csv: 'text/csv',
505
+ markdown: 'text/markdown',
506
+ plain: 'text/plain',
507
+ html: 'text/html'
508
+ };
509
+ return mimeTypes[format] || 'text/plain';
510
+ }
511
+
512
+ /**
513
+ * Generate filename with timestamp
514
+ */
515
+ private generateFilename(format: ExportFormat, filters: ExportFilters): string {
516
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
517
+ const prefix = filters.prefixes?.length === 1 ? `_${filters.prefixes[0]}` : '';
518
+ const extension = format === 'markdown' ? 'md' : format;
519
+ return `logs${prefix}_${timestamp}.${extension}`;
520
+ }
521
+
522
+ /**
523
+ * Stream logs to external endpoint
524
+ */
525
+ async streamToEndpoint(endpoint: string, filters: ExportFilters = {}, options: {
526
+ apiKey?: string;
527
+ batchSize?: number;
528
+ format?: ExportFormat
529
+ } = {}): Promise<boolean> {
530
+ try {
531
+ const logs = this.filterLogs(filters);
532
+ const batchSize = options.batchSize || 100;
533
+ const format = options.format || 'json';
534
+
535
+ // Send in batches
536
+ for (let i = 0; i < logs.length; i += batchSize) {
537
+ const batch = logs.slice(i, i + batchSize);
538
+ const result = this.export(format, {}, { compact: true });
539
+
540
+ const headers: Record<string, string> = {
541
+ 'Content-Type': 'application/json'
542
+ };
543
+
544
+ if (options.apiKey) {
545
+ headers['Authorization'] = `Bearer ${options.apiKey}`;
546
+ }
547
+
548
+ const response = await fetch(endpoint, {
549
+ method: 'POST',
550
+ headers,
551
+ body: JSON.stringify({
552
+ logs: batch,
553
+ metadata: {
554
+ batchIndex: Math.floor(i / batchSize),
555
+ totalBatches: Math.ceil(logs.length / batchSize),
556
+ timestamp: new Date().toISOString()
557
+ }
558
+ })
559
+ });
560
+
561
+ if (!response.ok) {
562
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
563
+ }
564
+ }
565
+
566
+ return true;
567
+ } catch (error) {
568
+ console.error('Failed to stream logs:', error);
569
+ return false;
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Create export with metadata enrichment
575
+ */
576
+ exportWithMetadata(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): ExportResult {
577
+ const baseResult = this.export(format, filters, options);
578
+ const bufferStats = this.getBufferStats();
579
+
580
+ return {
581
+ ...baseResult,
582
+ metadata: {
583
+ ...baseResult.metadata,
584
+ bufferStats: bufferStats,
585
+ performance: {
586
+ memoryUsage: this.estimateMemoryUsage(),
587
+ processingTime: Date.now()
588
+ },
589
+ system: {
590
+ userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'Node.js',
591
+ timestamp: new Date().toISOString(),
592
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
593
+ }
594
+ } as any
595
+ };
596
+ }
597
+
598
+ /**
599
+ * Estimate memory usage of buffer
600
+ */
601
+ private estimateMemoryUsage(): number {
602
+ const sampleEntry = this.buffer[0];
603
+ if (!sampleEntry) return 0;
604
+
605
+ // Rough estimation: JSON.stringify size * buffer length
606
+ const sampleSize = JSON.stringify(sampleEntry).length;
607
+ return sampleSize * this.buffer.length;
608
+ }
468
609
  }
@@ -3,28 +3,167 @@
3
3
  */
4
4
 
5
5
  import type { ILogHandler, LogLevel, LogMetadata } from '../types/index.js';
6
+ import { isNode, isBrowser } from '../utils/environment.js';
7
+ import { safeSerialize } from '../utils/formatting.js';
6
8
 
7
9
  /**
8
- * File-based log handler for persistent logging
10
+ * Universal file-based log handler for persistent logging
11
+ * Works in both Node.js (real files) and browser (localStorage)
9
12
  */
10
13
  export class FileLogHandler implements ILogHandler {
11
14
  private filename: string;
15
+ private maxSize?: number;
16
+ private fs?: any; // Node.js fs module
12
17
 
13
- constructor(filename: string = 'app.log') {
18
+ constructor(filename: string = 'app.log', maxSize?: number) {
14
19
  this.filename = filename;
20
+ this.maxSize = maxSize;
21
+
22
+ // Load Node.js fs module if in Node environment
23
+ if (isNode) {
24
+ try {
25
+ this.fs = require('fs');
26
+ } catch (error) {
27
+ console.warn('FileLogHandler: Unable to load fs module:', error);
28
+ }
29
+ }
15
30
  }
16
31
 
17
32
  handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void {
18
33
  const logEntry = {
19
- filename: this.filename,
20
34
  timestamp: metadata.timestamp,
21
35
  level: level.toUpperCase(),
22
36
  prefix: metadata.prefix,
23
37
  message,
24
- args: args.slice(1),
38
+ args: args.slice(1).map(arg => safeSerialize(arg)),
25
39
  location: metadata.stackInfo,
26
40
  };
27
41
 
28
- console.debug('File log entry:', logEntry);
42
+ const logLine = this.formatLogLine(logEntry);
43
+
44
+ if (isNode && this.fs) {
45
+ this.writeToFileNode(logLine);
46
+ } else if (isBrowser) {
47
+ this.writeToFileLocal(logLine);
48
+ } else {
49
+ // Fallback - just console output
50
+ console.log(`[FILE-LOG] ${logLine}`);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Format log entry as a single line for file storage
56
+ */
57
+ private formatLogLine(entry: any): string {
58
+ const parts = [
59
+ entry.timestamp,
60
+ `[${entry.level}]`,
61
+ entry.prefix ? `[${entry.prefix}]` : '',
62
+ entry.message
63
+ ].filter(Boolean);
64
+
65
+ let line = parts.join(' ');
66
+
67
+ if (entry.args && entry.args.length > 0) {
68
+ line += ' ' + entry.args.map((arg: any) =>
69
+ typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
70
+ ).join(' ');
71
+ }
72
+
73
+ if (entry.location) {
74
+ line += ` (${entry.location.file}:${entry.location.line}:${entry.location.column})`;
75
+ }
76
+
77
+ return line;
78
+ }
79
+
80
+ /**
81
+ * Write to actual file in Node.js environment
82
+ */
83
+ private writeToFileNode(logLine: string): void {
84
+ if (!this.fs) return;
85
+
86
+ try {
87
+ // Check file size if maxSize is set
88
+ if (this.maxSize) {
89
+ try {
90
+ const stats = this.fs.statSync(this.filename);
91
+ if (stats.size > this.maxSize) {
92
+ // Simple rotation: backup current file and start fresh
93
+ const backupName = `${this.filename}.bak`;
94
+ this.fs.renameSync(this.filename, backupName);
95
+ }
96
+ } catch (error) {
97
+ // File doesn't exist yet, that's fine
98
+ }
99
+ }
100
+
101
+ // Append to file
102
+ this.fs.appendFileSync(this.filename, logLine + '\n', 'utf8');
103
+ } catch (error) {
104
+ console.error('FileLogHandler: Failed to write to file:', error);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Write to localStorage in browser environment
110
+ */
111
+ private writeToFileLocal(logLine: string): void {
112
+ try {
113
+ const storageKey = `logger_${this.filename}`;
114
+ const existingLogs = localStorage.getItem(storageKey) || '';
115
+
116
+ let newLogs = existingLogs;
117
+ if (existingLogs.length > 0) {
118
+ newLogs += '\n';
119
+ }
120
+ newLogs += logLine;
121
+
122
+ // Simple size management - keep last 1000 lines max
123
+ if (this.maxSize || newLogs.split('\n').length > 1000) {
124
+ const lines = newLogs.split('\n');
125
+ const maxLines = this.maxSize ? Math.floor(this.maxSize / 100) : 1000;
126
+ if (lines.length > maxLines) {
127
+ newLogs = lines.slice(-maxLines).join('\n');
128
+ }
129
+ }
130
+
131
+ localStorage.setItem(storageKey, newLogs);
132
+ } catch (error) {
133
+ console.error('FileLogHandler: Failed to write to localStorage:', error);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Get stored logs (useful for debugging)
139
+ */
140
+ getLogs(): string | null {
141
+ if (isNode && this.fs) {
142
+ try {
143
+ return this.fs.readFileSync(this.filename, 'utf8');
144
+ } catch (error) {
145
+ return null;
146
+ }
147
+ } else if (isBrowser) {
148
+ const storageKey = `logger_${this.filename}`;
149
+ return localStorage.getItem(storageKey);
150
+ }
151
+ return null;
152
+ }
153
+
154
+ /**
155
+ * Clear stored logs
156
+ */
157
+ clearLogs(): void {
158
+ if (isNode && this.fs) {
159
+ try {
160
+ this.fs.writeFileSync(this.filename, '', 'utf8');
161
+ } catch (error) {
162
+ console.error('FileLogHandler: Failed to clear file:', error);
163
+ }
164
+ } else if (isBrowser) {
165
+ const storageKey = `logger_${this.filename}`;
166
+ localStorage.removeItem(storageKey);
167
+ }
29
168
  }
30
169
  }
package/src/index.ts CHANGED
@@ -53,7 +53,7 @@ export const groupEnd = () => logger.groupEnd();
53
53
  export const time = (label: string) => logger.time(label);
54
54
  export const timeEnd = (label: string) => logger.timeEnd(label);
55
55
  export const setGlobalPrefix = (prefix: string) => logger.setGlobalPrefix(prefix);
56
- export const createScopedLogger = (prefix: string) => logger.createScopedLogger(prefix);
56
+ export const scope = (prefix: string) => logger.scope(prefix);
57
57
  export const setVerbosity = (level: Verbosity) => logger.setVerbosity(level);
58
58
  export const addHandler = (handler: ILogHandler) => logger.addHandler(handler);
59
59
  export const setTheme = (theme: ThemeVariant) => logger.setTheme(theme);
package/src/main.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  time,
12
12
  timeEnd,
13
13
  trace,
14
- createScopedLogger,
14
+ scope,
15
15
  cli,
16
16
  setTheme,
17
17
  setBannerType,
@@ -87,9 +87,9 @@ function testTiming() {
87
87
 
88
88
  function testScopedLogger() {
89
89
  info("Demonstrating scoped loggers...");
90
- const apiLogger = createScopedLogger("API");
90
+ const apiLogger = scope("API");
91
91
  apiLogger.info("Fetching user data...");
92
- const uiLogger = createScopedLogger("UI");
92
+ const uiLogger = scope("UI");
93
93
  uiLogger.debug("Rendering user profile component.");
94
94
  apiLogger.success("User data fetched successfully.");
95
95
  apiLogger.error("Failed to update settings.");