@expressots/core 4.0.0-preview.1 → 4.0.0-preview.3

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 (134) hide show
  1. package/LICENSE.md +21 -21
  2. package/README.md +66 -66
  3. package/lib/CHANGELOG.md +774 -774
  4. package/lib/README.md +66 -66
  5. package/lib/cjs/application/application-factory.js +6 -0
  6. package/lib/cjs/application/bootstrap.js +117 -213
  7. package/lib/cjs/config/define-config.js +1 -1
  8. package/lib/cjs/config/env-field-builders.js +47 -0
  9. package/lib/cjs/config/index.js +7 -1
  10. package/lib/cjs/framework-version.js +10 -0
  11. package/lib/cjs/lazy-loading/index.js +5 -1
  12. package/lib/cjs/lazy-loading/lazy-module-helpers.js +49 -0
  13. package/lib/cjs/middleware/index.js +8 -9
  14. package/lib/cjs/middleware/middleware-service.js +68 -12
  15. package/lib/cjs/middleware/presets-standalone.js +93 -0
  16. package/lib/cjs/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
  17. package/lib/cjs/provider/db-in-memory/index.js +11 -1
  18. package/lib/cjs/provider/db-in-memory/query/query-engine.js +28 -0
  19. package/lib/cjs/provider/db-in-memory/schema/decorators.js +18 -0
  20. package/lib/cjs/provider/db-in-memory/storage/index.js +3 -1
  21. package/lib/cjs/provider/db-in-memory/storage/memory-store.js +72 -1
  22. package/lib/cjs/provider/logger/logger.banner.js +40 -31
  23. package/lib/cjs/provider/logger/logger.config.js +11 -1
  24. package/lib/cjs/provider/logger/logger.formatter.js +22 -1
  25. package/lib/cjs/provider/logger/logger.provider.js +59 -9
  26. package/lib/cjs/provider/logger/transports/console.transport.js +69 -6
  27. package/lib/cjs/provider/logger/transports/file.transport.js +27 -18
  28. package/lib/cjs/provider/logger/utils/log-levels.js +6 -5
  29. package/lib/cjs/provider/validation/adapters/index.js +12 -5
  30. package/lib/cjs/provider/validation/adapters/yup.adapter.js +118 -0
  31. package/lib/cjs/provider/validation/adapters/zod.adapter.js +137 -0
  32. package/lib/cjs/provider/validation/index.js +5 -1
  33. package/lib/cjs/render/adapters/react-adapter.js +14 -14
  34. package/lib/cjs/render/features/type-generator.js +30 -30
  35. package/lib/cjs/render/features/view-debugger.js +75 -55
  36. package/lib/cjs/testing/fluent-request.js +7 -0
  37. package/lib/cjs/testing/snapshot-request.js +2 -0
  38. package/lib/cjs/types/application/application-factory.d.ts +6 -0
  39. package/lib/cjs/types/application/bootstrap.d.ts +196 -24
  40. package/lib/cjs/types/config/config.interfaces.d.ts +7 -1
  41. package/lib/cjs/types/config/env-field-builders.d.ts +39 -0
  42. package/lib/cjs/types/config/index.d.ts +1 -1
  43. package/lib/cjs/types/framework-version.d.ts +7 -0
  44. package/lib/cjs/types/lazy-loading/index.d.ts +1 -0
  45. package/lib/cjs/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
  46. package/lib/cjs/types/middleware/index.d.ts +1 -1
  47. package/lib/cjs/types/middleware/middleware-service.d.ts +21 -0
  48. package/lib/cjs/types/middleware/presets-standalone.d.ts +75 -0
  49. package/lib/cjs/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
  50. package/lib/cjs/types/provider/db-in-memory/index.d.ts +9 -1
  51. package/lib/cjs/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
  52. package/lib/cjs/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
  53. package/lib/cjs/types/provider/db-in-memory/storage/index.d.ts +1 -1
  54. package/lib/cjs/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
  55. package/lib/cjs/types/provider/logger/logger.banner.d.ts +1 -1
  56. package/lib/cjs/types/provider/logger/logger.config.d.ts +7 -0
  57. package/lib/cjs/types/provider/logger/logger.formatter.d.ts +10 -0
  58. package/lib/cjs/types/provider/logger/logger.provider.d.ts +32 -1
  59. package/lib/cjs/types/provider/logger/transports/console.transport.d.ts +7 -0
  60. package/lib/cjs/types/provider/logger/utils/log-levels.d.ts +3 -3
  61. package/lib/cjs/types/provider/validation/adapters/index.d.ts +7 -4
  62. package/lib/cjs/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
  63. package/lib/cjs/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
  64. package/lib/cjs/types/provider/validation/index.d.ts +1 -1
  65. package/lib/cjs/types/render/features/view-debugger.d.ts +10 -0
  66. package/lib/cjs/types/testing/testing.interfaces.d.ts +31 -6
  67. package/lib/esm/application/application-factory.js +6 -0
  68. package/lib/esm/application/bootstrap.js +117 -213
  69. package/lib/esm/config/define-config.js +1 -1
  70. package/lib/esm/config/env-field-builders.js +48 -0
  71. package/lib/esm/config/index.js +6 -1
  72. package/lib/esm/framework-version.js +7 -0
  73. package/lib/esm/lazy-loading/index.js +2 -0
  74. package/lib/esm/lazy-loading/lazy-module-helpers.js +45 -0
  75. package/lib/esm/middleware/index.js +3 -2
  76. package/lib/esm/middleware/middleware-service.js +68 -12
  77. package/lib/esm/middleware/presets-standalone.js +87 -0
  78. package/lib/esm/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
  79. package/lib/esm/provider/db-in-memory/index.js +9 -1
  80. package/lib/esm/provider/db-in-memory/query/query-engine.js +28 -0
  81. package/lib/esm/provider/db-in-memory/schema/decorators.js +18 -0
  82. package/lib/esm/provider/db-in-memory/storage/index.js +1 -1
  83. package/lib/esm/provider/db-in-memory/storage/memory-store.js +75 -0
  84. package/lib/esm/provider/logger/logger.banner.js +40 -31
  85. package/lib/esm/provider/logger/logger.config.js +12 -2
  86. package/lib/esm/provider/logger/logger.formatter.js +22 -1
  87. package/lib/esm/provider/logger/logger.provider.js +61 -10
  88. package/lib/esm/provider/logger/transports/console.transport.js +69 -6
  89. package/lib/esm/provider/logger/transports/file.transport.js +27 -18
  90. package/lib/esm/provider/logger/utils/log-levels.js +6 -5
  91. package/lib/esm/provider/validation/adapters/index.js +7 -4
  92. package/lib/esm/provider/validation/adapters/yup.adapter.js +111 -0
  93. package/lib/esm/provider/validation/adapters/zod.adapter.js +130 -0
  94. package/lib/esm/provider/validation/index.js +1 -1
  95. package/lib/esm/render/adapters/react-adapter.js +14 -14
  96. package/lib/esm/render/features/type-generator.js +30 -30
  97. package/lib/esm/render/features/view-debugger.js +75 -55
  98. package/lib/esm/testing/fluent-request.js +7 -0
  99. package/lib/esm/testing/snapshot-request.js +2 -0
  100. package/lib/esm/types/application/application-factory.d.ts +6 -0
  101. package/lib/esm/types/application/bootstrap.d.ts +196 -24
  102. package/lib/esm/types/config/config.interfaces.d.ts +7 -1
  103. package/lib/esm/types/config/env-field-builders.d.ts +39 -0
  104. package/lib/esm/types/config/index.d.ts +1 -1
  105. package/lib/esm/types/framework-version.d.ts +7 -0
  106. package/lib/esm/types/lazy-loading/index.d.ts +1 -0
  107. package/lib/esm/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
  108. package/lib/esm/types/middleware/index.d.ts +1 -1
  109. package/lib/esm/types/middleware/middleware-service.d.ts +21 -0
  110. package/lib/esm/types/middleware/presets-standalone.d.ts +75 -0
  111. package/lib/esm/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
  112. package/lib/esm/types/provider/db-in-memory/index.d.ts +9 -1
  113. package/lib/esm/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
  114. package/lib/esm/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
  115. package/lib/esm/types/provider/db-in-memory/storage/index.d.ts +1 -1
  116. package/lib/esm/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
  117. package/lib/esm/types/provider/logger/logger.banner.d.ts +1 -1
  118. package/lib/esm/types/provider/logger/logger.config.d.ts +7 -0
  119. package/lib/esm/types/provider/logger/logger.formatter.d.ts +10 -0
  120. package/lib/esm/types/provider/logger/logger.provider.d.ts +32 -1
  121. package/lib/esm/types/provider/logger/transports/console.transport.d.ts +7 -0
  122. package/lib/esm/types/provider/logger/utils/log-levels.d.ts +3 -3
  123. package/lib/esm/types/provider/validation/adapters/index.d.ts +7 -4
  124. package/lib/esm/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
  125. package/lib/esm/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
  126. package/lib/esm/types/provider/validation/index.d.ts +1 -1
  127. package/lib/esm/types/render/features/view-debugger.d.ts +10 -0
  128. package/lib/esm/types/testing/testing.interfaces.d.ts +31 -6
  129. package/lib/package.json +23 -8
  130. package/package.json +23 -8
  131. package/lib/cjs/middleware/middleware-presets.js +0 -294
  132. package/lib/cjs/types/middleware/middleware-presets.d.ts +0 -90
  133. package/lib/esm/middleware/middleware-presets.js +0 -286
  134. package/lib/esm/types/middleware/middleware-presets.d.ts +0 -90
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BannerGenerator = void 0;
4
4
  const color_codes_js_1 = require("../../console/color-codes.js");
5
5
  const logger_metrics_js_1 = require("./logger.metrics.js");
6
+ const framework_version_js_1 = require("../../framework-version.js");
6
7
  // eslint-disable-next-line no-control-regex
7
8
  const ANSI_STRIP_REGEX = /\x1b\[[0-9;]*m/g;
8
9
  // Helper to write to stdout - uses process.stdout directly to allow runtime interception
@@ -56,22 +57,41 @@ function getExpressoTSLogo() {
56
57
  const ts = normalizedTs[i];
57
58
  return `║${padLeft}${green}${exp}${reset}${white}${ts}${reset}${padRight}║`;
58
59
  });
59
- return `
60
- ${topBorder}
61
- ${emptyLine}
62
- ${contentLines.join("\n")}
63
- ${emptyLine}
64
- ${bottomBorder}
60
+ return `
61
+ ${topBorder}
62
+ ${emptyLine}
63
+ ${contentLines.join("\n")}
64
+ ${emptyLine}
65
+ ${bottomBorder}
65
66
  `.trim();
66
67
  }
67
68
  /**
68
- * Compact ASCII art logo.
69
+ * Format a timestamp for structured log output.
69
70
  */
70
- const EXPRESSOTS_LOGO_COMPACT = `
71
- ╔════════════════════════════════════════════════════════════════╗
72
- ║ ExpressoTS - Enterprise TypeScript Framework ║
73
- ╚════════════════════════════════════════════════════════════════╝
74
- `.trim();
71
+ function formatBannerTimestamp() {
72
+ const options = {
73
+ year: "numeric",
74
+ month: "2-digit",
75
+ day: "2-digit",
76
+ hour: "2-digit",
77
+ minute: "2-digit",
78
+ second: "2-digit",
79
+ };
80
+ return new Date().toLocaleString(undefined, options).replace(",", "");
81
+ }
82
+ /**
83
+ * Build a structured log line matching the standard ExpressoTS log format.
84
+ */
85
+ function logLine(message) {
86
+ const timestamp = formatBannerTimestamp();
87
+ const pid = process.pid;
88
+ return (colorText("[ExpressoTS]", "green") +
89
+ ` ${timestamp}` +
90
+ ` ${colorText(`[PID:${pid}]`, "green")}` +
91
+ ` ${colorText("INFO ", "green")}` +
92
+ ` ${colorText("[app]", "green")}` +
93
+ ` ${message}\n`);
94
+ }
75
95
  // Removed unused EXPRESSOTS_LOGO_MINIMAL constant
76
96
  /**
77
97
  * Color text helper.
@@ -118,7 +138,6 @@ class BannerGenerator {
118
138
  const startupTime = Date.now() - this.startTime;
119
139
  const memoryUsage = process.memoryUsage().heapUsed;
120
140
  const memoryFormatted = (0, logger_metrics_js_1.formatMemory)(memoryUsage);
121
- writeStdout("\n");
122
141
  // Display logo based on style
123
142
  switch (this.config.style) {
124
143
  case "full":
@@ -131,7 +150,6 @@ class BannerGenerator {
131
150
  this.displayMinimalBanner(port, environment, appInfo);
132
151
  break;
133
152
  }
134
- writeStdout("\n");
135
153
  }
136
154
  /**
137
155
  * Display full banner with clean 3-column layout.
@@ -139,6 +157,7 @@ class BannerGenerator {
139
157
  * Optional (user-enabled): Features, Middleware Pipeline, Provider Registry, Resources
140
158
  */
141
159
  displayFullBanner(port, environment, appInfo, metrics, features, config, startupTime, memoryFormatted, bannerData) {
160
+ writeStdout("\n");
142
161
  // Banner box width is ~93 chars, calculate column widths for 3 columns
143
162
  const totalWidth = 93;
144
163
  const colWidth = 29;
@@ -149,7 +168,7 @@ class BannerGenerator {
149
168
  // ══════════════════════════════════════════════════════════════════════
150
169
  // Version info line (matches banner width)
151
170
  // ══════════════════════════════════════════════════════════════════════
152
- const frameworkVersion = "4.0.0-beta.1";
171
+ const frameworkVersion = framework_version_js_1.FRAMEWORK_VERSION;
153
172
  const nodeVersion = process.version;
154
173
  const platform = process.platform;
155
174
  const appName = appInfo?.appName || "App";
@@ -385,27 +404,17 @@ class BannerGenerator {
385
404
  writeStdout("\n");
386
405
  }
387
406
  /**
388
- * Display compact banner.
407
+ * Display compact banner using structured log format (cloud-friendly).
389
408
  */
390
- displayCompactBanner(port, environment, appInfo, metrics, features, startupTime, memoryFormatted) {
391
- writeStdout(colorText(EXPRESSOTS_LOGO_COMPACT, "green"));
392
- writeStdout("\n\n");
409
+ displayCompactBanner(port, environment, appInfo, metrics, _features, startupTime, memoryFormatted) {
393
410
  const appName = appInfo?.appName || "App";
394
411
  const appVersion = appInfo?.appVersion || "not provided";
395
- writeStdout(colorText(`ExpressoTS v4.0.0-beta.1`, "green") +
396
- colorText(` | ${appName} v${appVersion}`, "blue") +
397
- colorText(` | Node ${process.version}`, "blue") +
398
- "\n");
399
- writeStdout(colorText(`Environment: ${this.colorEnvironment(environment)}`, "yellow") +
400
- colorText(` | Port: ${port}`, "blue") +
401
- colorText(` | PID: ${process.pid}`, "blue") +
402
- "\n");
412
+ writeStdout(logLine(`ExpressoTS v${framework_version_js_1.FRAMEWORK_VERSION} | ${appName} v${appVersion} | Node ${process.version}`));
413
+ writeStdout(logLine(`Environment: ${environment} | Port: ${port} | PID: ${process.pid}`));
403
414
  if (metrics) {
404
- writeStdout(colorText(`Controllers: ${metrics.controllers} | Providers: ${metrics.providers} | Routes: ${metrics.routes}`, "white") + "\n");
415
+ writeStdout(logLine(`Controllers: ${metrics.controllers} | Providers: ${metrics.providers} | Routes: ${metrics.routes}`));
405
416
  }
406
- writeStdout(colorText(`Startup: ${startupTime.toFixed(2)}ms`, "yellow") +
407
- colorText(` | Memory: ${memoryFormatted}`, "yellow") +
408
- "\n");
417
+ writeStdout(logLine(`Startup: ${startupTime.toFixed(2)}ms | Memory: ${memoryFormatted}`));
409
418
  }
410
419
  /**
411
420
  * Display minimal banner.
@@ -4,12 +4,22 @@ exports.getDefaultLoggerConfig = getDefaultLoggerConfig;
4
4
  const log_levels_js_1 = require("./utils/log-levels.js");
5
5
  /**
6
6
  * Default logger configuration.
7
+ *
8
+ * Honours `process.env.LOG_LEVEL` when set so that any logs emitted
9
+ * BEFORE the application's `globalConfiguration()` runs (e.g. interceptor
10
+ * registration during container construction) are gated by the user's
11
+ * desired log level instead of the development default of `DEBUG`.
12
+ * Falls back to `DEBUG` in development and `INFO` in production.
13
+ *
7
14
  * @public API
8
15
  */
9
16
  function getDefaultLoggerConfig() {
10
17
  const isDevelopment = process.env.NODE_ENV !== "production";
18
+ const envLevel = process.env.LOG_LEVEL;
19
+ const defaultLevel = isDevelopment ? log_levels_js_1.LogLevel.DEBUG : log_levels_js_1.LogLevel.INFO;
20
+ const level = envLevel ? (0, log_levels_js_1.parseLogLevel)(envLevel) : defaultLevel;
11
21
  return {
12
- level: isDevelopment ? log_levels_js_1.LogLevel.DEBUG : log_levels_js_1.LogLevel.INFO,
22
+ level,
13
23
  transports: [], // Will be populated with ConsoleTransport by default
14
24
  filters: {},
15
25
  structured: !isDevelopment, // JSON in production, pretty in dev
@@ -7,6 +7,17 @@ exports.formatGroupedProd = formatGroupedProd;
7
7
  const log_levels_js_1 = require("./utils/log-levels.js");
8
8
  const color_codes_js_1 = require("../../console/color-codes.js");
9
9
  const logger_redaction_js_1 = require("./logger.redaction.js");
10
+ /**
11
+ * Strip ANSI escape sequences (SGR colors, cursor moves, etc.) from a
12
+ * string. Cheap fast-path bails out when no escape character is present.
13
+ */
14
+ // eslint-disable-next-line no-control-regex
15
+ const ANSI_ESCAPE_REGEX = /\u001b\[[\d;?]*[ -/]*[@-~]/g;
16
+ function stripAnsiEscapes(input) {
17
+ if (!input || input.indexOf("\u001b") === -1)
18
+ return input;
19
+ return input.replace(ANSI_ESCAPE_REGEX, "");
20
+ }
10
21
  /**
11
22
  * Format log entry for development (human-readable, colored).
12
23
  * @param entry - Log entry to format
@@ -50,6 +61,13 @@ function formatDev(entry, options) {
50
61
  if (processedEntry.flow) {
51
62
  output += formatFlow(processedEntry.flow, 2);
52
63
  }
64
+ // Strip ANSI escapes once at the end when colors are disabled. This is
65
+ // far simpler than threading a "useColors" flag through every nested
66
+ // `colorText(...)` call site and the perf cost (one regex pass over a
67
+ // log line) is negligible relative to the I/O that follows.
68
+ if (options?.colors === false) {
69
+ return stripAnsiEscapes(output);
70
+ }
53
71
  return output;
54
72
  }
55
73
  /**
@@ -112,7 +130,7 @@ function formatTimestamp(date) {
112
130
  */
113
131
  function getLevelColor(level) {
114
132
  switch (level) {
115
- case log_levels_js_1.LogLevel.TRACE:
133
+ case log_levels_js_1.LogLevel.ALL:
116
134
  return "white";
117
135
  case log_levels_js_1.LogLevel.DEBUG:
118
136
  return "blue";
@@ -337,6 +355,9 @@ function formatGroupedDev(groupedEntry, options) {
337
355
  }
338
356
  }
339
357
  }
358
+ if (options?.colors === false) {
359
+ return stripAnsiEscapes(output);
360
+ }
340
361
  return output;
341
362
  }
342
363
  /**
@@ -28,7 +28,7 @@ const logger_query_js_1 = require("./logger.query.js");
28
28
  * - Automatic context detection (class/method names)
29
29
  * - Request-scoped context via AsyncLocalStorage
30
30
  * - Child logger creation with inherited context
31
- * - Multiple log levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
31
+ * - Multiple log levels (ALL, DEBUG, INFO, WARN, ERROR, FATAL, SILENT)
32
32
  * - Pluggable transports (console, file, HTTP)
33
33
  * @public API
34
34
  */
@@ -44,15 +44,27 @@ let Logger = Logger_1 = class Logger {
44
44
  this.author = "Richard Zampieri";
45
45
  this.repo = "https://github.com/expressots/expressots";
46
46
  this.pid = process.pid;
47
- this.config = (0, logger_config_js_1.getDefaultLoggerConfig)();
48
- // Initialize with default console transport if none provided
47
+ // Merge module-level overrides on top of the built-in defaults so values
48
+ // set via Logger.configure(...) before container creation propagate to
49
+ // every instance. Caller-supplied transports replace the defaults, level
50
+ // is forwarded to the auto-installed console transport below.
51
+ const baseConfig = (0, logger_config_js_1.getDefaultLoggerConfig)();
52
+ this.config = {
53
+ ...baseConfig,
54
+ ...Logger_1.defaultOverrides,
55
+ };
56
+ // Initialize with default console transport if none provided.
57
+ // Aligning the transport's level with the resolved logger level
58
+ // (which already honours `process.env.LOG_LEVEL`) keeps both
59
+ // filters consistent for messages emitted before any explicit
60
+ // `logger.configure({ level })` call.
49
61
  if (this.config.transports.length === 0) {
50
62
  const isDevelopment = process.env.NODE_ENV !== "production";
51
- this.config.transports = [
52
- isDevelopment
53
- ? console_transport_js_1.ConsoleTransport.forDevelopment()
54
- : console_transport_js_1.ConsoleTransport.forProduction(),
55
- ];
63
+ const transport = isDevelopment
64
+ ? console_transport_js_1.ConsoleTransport.forDevelopment()
65
+ : console_transport_js_1.ConsoleTransport.forProduction();
66
+ transport.level = (0, log_levels_js_1.parseLogLevel)(this.config.level);
67
+ this.config.transports = [transport];
56
68
  }
57
69
  this.transports = this.config.transports;
58
70
  // Initialize grouping manager with default config
@@ -60,6 +72,37 @@ let Logger = Logger_1 = class Logger {
60
72
  // Initialize query manager with default config
61
73
  this.queryManager = new logger_query_js_1.LogQueryManager(this.config.query);
62
74
  }
75
+ /**
76
+ * Configure the default LoggerConfig used by every future `Logger`
77
+ * instance constructed in this process.
78
+ *
79
+ * Use this from application config files **before** bootstrap so the DI
80
+ * container's Logger picks up the correct level / transports / grouping /
81
+ * health / redaction settings without needing to inject and reconfigure it.
82
+ * Calls are merged: later calls override earlier ones, but unspecified
83
+ * keys keep their previously-set values.
84
+ *
85
+ * Already-constructed Logger instances are NOT mutated by this call —
86
+ * use the instance method `logger.configure(...)` to update a live one.
87
+ *
88
+ * @param config - Partial configuration to merge with the defaults.
89
+ * @public API
90
+ */
91
+ static configure(config) {
92
+ Logger_1.defaultOverrides = {
93
+ ...Logger_1.defaultOverrides,
94
+ ...config,
95
+ };
96
+ }
97
+ /**
98
+ * Reset the module-level overrides set by `Logger.configure`. Useful in
99
+ * tests to undo cross-test pollution.
100
+ *
101
+ * @public API
102
+ */
103
+ static resetConfigure() {
104
+ Logger_1.defaultOverrides = {};
105
+ }
63
106
  /**
64
107
  * Configure the logger.
65
108
  * @param config - Partial configuration to merge with defaults
@@ -131,7 +174,7 @@ let Logger = Logger_1 = class Logger {
131
174
  * @public API
132
175
  */
133
176
  trace(message, data) {
134
- this.log(log_levels_js_1.LogLevel.TRACE, message, { data });
177
+ this.log(log_levels_js_1.LogLevel.ALL, message, { data });
135
178
  }
136
179
  /**
137
180
  * Log a debug message (detailed diagnostic).
@@ -739,6 +782,13 @@ let Logger = Logger_1 = class Logger {
739
782
  }
740
783
  };
741
784
  exports.Logger = Logger;
785
+ /**
786
+ * Module-level overrides applied by `Logger.configure(...)` on top of the
787
+ * built-in defaults. Used by the constructor of every future `Logger`
788
+ * instance so application-wide config (e.g. log level, transports) can be
789
+ * set once before bootstrap, before any DI container exists.
790
+ */
791
+ Logger.defaultOverrides = {};
742
792
  exports.Logger = Logger = Logger_1 = __decorate([
743
793
  (0, scope_binding_js_1.provideSingleton)(Logger, "builtin"),
744
794
  __metadata("design:paramtypes", [])
@@ -3,6 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConsoleTransport = void 0;
4
4
  const log_levels_js_1 = require("../utils/log-levels.js");
5
5
  const logger_formatter_js_1 = require("../logger.formatter.js");
6
+ /**
7
+ * Decide whether ANSI color codes should be emitted by default.
8
+ *
9
+ * Order of precedence (industry standard, matches chalk / supports-color):
10
+ * 1. `NO_COLOR` env var (any non-empty value) → never colors
11
+ * 2. `FORCE_COLOR` env var (any non-"0"/"false" value) → always colors
12
+ * 3. Process running in production → never colors
13
+ * 4. Stdout is a TTY → colors
14
+ * 5. Otherwise (pipes, redirects, cloud log capture) → no colors
15
+ *
16
+ * This means cloud platforms like Azure App Service, AWS CloudWatch,
17
+ * Heroku, Docker, Kubernetes, etc. — which all capture stdout into a
18
+ * non-TTY pipe — receive plain text by default, without users having
19
+ * to configure anything.
20
+ */
21
+ function detectColorsDefault() {
22
+ const noColor = process.env.NO_COLOR;
23
+ if (noColor !== undefined && noColor !== "")
24
+ return false;
25
+ const forceColor = process.env.FORCE_COLOR;
26
+ if (forceColor !== undefined &&
27
+ forceColor !== "0" &&
28
+ forceColor !== "false") {
29
+ return true;
30
+ }
31
+ if (process.env.NODE_ENV === "production")
32
+ return false;
33
+ return Boolean(process.stdout && process.stdout.isTTY);
34
+ }
6
35
  /**
7
36
  * Console transport for logging to stdout/stderr.
8
37
  * @public API
@@ -18,7 +47,7 @@ class ConsoleTransport {
18
47
  this.enabled = options?.enabled ?? true;
19
48
  this.structured =
20
49
  options?.structured ?? process.env.NODE_ENV === "production";
21
- this.colors = options?.colors ?? process.env.NODE_ENV !== "production";
50
+ this.colors = options?.colors ?? detectColorsDefault();
22
51
  this.pretty = options?.pretty ?? process.env.NODE_ENV !== "production";
23
52
  // Enable redaction by default in production
24
53
  this.redact = options?.redact ?? process.env.NODE_ENV === "production";
@@ -26,6 +55,13 @@ class ConsoleTransport {
26
55
  }
27
56
  /**
28
57
  * Create a console transport optimized for development.
58
+ *
59
+ * Colors are auto-detected: they're emitted when stdout is a TTY
60
+ * (your terminal) and disabled when stdout is piped or captured by
61
+ * a cloud log collector (Azure App Service, AWS CloudWatch, Heroku,
62
+ * Docker, Kubernetes, etc.). Respects the standard `NO_COLOR` and
63
+ * `FORCE_COLOR` environment variables.
64
+ *
29
65
  * @param redact - Enable redaction (default: false for development)
30
66
  * @returns ConsoleTransport configured for development
31
67
  * @public API
@@ -34,7 +70,7 @@ class ConsoleTransport {
34
70
  return new ConsoleTransport({
35
71
  level: log_levels_js_1.LogLevel.DEBUG,
36
72
  structured: false,
37
- colors: true,
73
+ // Omit `colors` so the constructor's auto-detect kicks in.
38
74
  pretty: true,
39
75
  redact, // Usually disabled in development for easier debugging
40
76
  });
@@ -58,17 +94,44 @@ class ConsoleTransport {
58
94
  if (!this.enabled || !(0, log_levels_js_1.shouldLog)(entry.level, this.level)) {
59
95
  return;
60
96
  }
61
- // Build format options with redaction settings
97
+ // Build format options with redaction + color settings. The color
98
+ // flag is honoured by `formatDev` / `formatGroupedDev`; `formatProd`
99
+ // (structured JSON) never emits color regardless.
62
100
  const formatOptions = {
63
101
  redact: this.redact,
64
102
  redactor: this.redactor,
103
+ colors: this.colors,
65
104
  };
66
105
  const formatted = this.structured
67
106
  ? (0, logger_formatter_js_1.formatProd)(entry, formatOptions)
68
107
  : (0, logger_formatter_js_1.formatDev)(entry, formatOptions);
69
- // Use process.stdout/stderr directly to allow runtime interception
70
- const stream = entry.level >= log_levels_js_1.LogLevel.ERROR ? process.stderr : process.stdout;
71
- stream.write(formatted);
108
+ // Strip trailing newline since console.* methods append their own.
109
+ const msg = formatted.endsWith("\n") ? formatted.slice(0, -1) : formatted;
110
+ // Route through console methods so Studio's LogCapture (which
111
+ // intercepts console.*) can forward these entries to the Live Logs UI.
112
+ // The level mapping mirrors the level chips Studio renders so messages
113
+ // land under the expected filter. We pass a single pre-formatted string
114
+ // so util.format never tries to interpret stray "%s"/"%d" tokens in
115
+ // user-supplied messages.
116
+ switch (entry.level) {
117
+ case log_levels_js_1.LogLevel.DEBUG:
118
+ case log_levels_js_1.LogLevel.ALL:
119
+ console.debug(msg);
120
+ break;
121
+ case log_levels_js_1.LogLevel.INFO:
122
+ console.info(msg);
123
+ break;
124
+ case log_levels_js_1.LogLevel.WARN:
125
+ console.warn(msg);
126
+ break;
127
+ case log_levels_js_1.LogLevel.ERROR:
128
+ case log_levels_js_1.LogLevel.FATAL:
129
+ console.error(msg);
130
+ break;
131
+ default:
132
+ console.log(msg);
133
+ break;
134
+ }
72
135
  }
73
136
  flush() {
74
137
  // Console doesn't need flushing
@@ -87,29 +87,34 @@ class FileTransport {
87
87
  if (!this.enabled || !(0, log_levels_js_1.shouldLog)(entry.level, this.level)) {
88
88
  return;
89
89
  }
90
- const filename = this.getFilename();
91
- const shouldRotate = this.shouldRotate(filename);
92
- if (shouldRotate) {
93
- await this.rotateFile(filename);
94
- }
95
- if (filename !== this.currentFilename) {
96
- this.currentFilename = filename;
97
- this.currentFileSize = await this.getFileSize(filename);
98
- }
99
- await this.ensureDirectory();
100
- const filePath = (0, path_1.join)(this.directory, this.currentFilename);
101
- const formatted = (0, logger_formatter_js_1.formatProd)(entry) + "\n";
102
- const entrySize = Buffer.byteLength(formatted, "utf8");
90
+ // Wrap the entire write pipeline in a single try/catch.
91
+ //
92
+ // FileTransport must NEVER throw out of `log()` — a logger that crashes
93
+ // the request that just wrote to it would be worse than a silent drop.
94
+ // We log a single line to stderr and move on; this matches the
95
+ // ConsoleTransport contract and keeps the framework's "logging never
96
+ // takes down the app" invariant.
103
97
  try {
98
+ const filename = this.getFilename();
99
+ const shouldRotate = this.shouldRotate(filename);
100
+ if (shouldRotate) {
101
+ await this.rotateFile(filename);
102
+ }
103
+ if (filename !== this.currentFilename) {
104
+ this.currentFilename = filename;
105
+ this.currentFileSize = await this.getFileSize(filename);
106
+ }
107
+ await this.ensureDirectory();
108
+ const filePath = (0, path_1.join)(this.directory, this.currentFilename);
109
+ const formatted = (0, logger_formatter_js_1.formatProd)(entry) + "\n";
110
+ const entrySize = Buffer.byteLength(formatted, "utf8");
104
111
  await fs_1.promises.appendFile(filePath, formatted, "utf8");
105
112
  this.currentFileSize += entrySize;
106
- // Check if we need to rotate after this write
107
113
  if (this.maxSize > 0 && this.currentFileSize >= this.maxSize) {
108
114
  await this.rotateFile(this.currentFilename);
109
115
  }
110
116
  }
111
117
  catch (error) {
112
- // Silently fail to avoid log loops
113
118
  console.error(`[FileTransport] Failed to write log:`, error);
114
119
  }
115
120
  }
@@ -217,13 +222,17 @@ class FileTransport {
217
222
  }
218
223
  }
219
224
  startCleanupInterval() {
220
- // Run cleanup every hour
221
225
  this.cleanupInterval = setInterval(() => {
222
226
  this.cleanupOldFiles().catch((error) => {
223
227
  console.error(`[FileTransport] Cleanup failed:`, error);
224
228
  });
225
- }, 60 * 60 * 1000); // 1 hour
226
- // Run initial cleanup
229
+ }, 60 * 60 * 1000);
230
+ // unref() so the cleanup timer never keeps the event loop alive on its own.
231
+ // Without this a short-lived script that uses FileTransport would hang
232
+ // for an hour at exit waiting on the next tick.
233
+ if (typeof this.cleanupInterval.unref === "function") {
234
+ this.cleanupInterval.unref();
235
+ }
227
236
  this.cleanupOldFiles().catch((error) => {
228
237
  console.error(`[FileTransport] Initial cleanup failed:`, error);
229
238
  });
@@ -11,8 +11,8 @@ exports.shouldLog = shouldLog;
11
11
  */
12
12
  var LogLevel;
13
13
  (function (LogLevel) {
14
- /** Ultra-detailed diagnostic information (function entry/exit) */
15
- LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
14
+ /** Show all logs (ultra-detailed diagnostic information, function entry/exit) */
15
+ LogLevel[LogLevel["ALL"] = 0] = "ALL";
16
16
  /** Detailed diagnostic information for debugging */
17
17
  LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
18
18
  /** General informational messages */
@@ -47,8 +47,9 @@ function parseLogLevel(level) {
47
47
  return LogLevel.INFO;
48
48
  }
49
49
  switch (upperLevel) {
50
+ case "ALL":
50
51
  case "TRACE":
51
- return LogLevel.TRACE;
52
+ return LogLevel.ALL;
52
53
  case "DEBUG":
53
54
  return LogLevel.DEBUG;
54
55
  case "INFO":
@@ -73,8 +74,8 @@ function parseLogLevel(level) {
73
74
  */
74
75
  function logLevelToString(level) {
75
76
  switch (level) {
76
- case LogLevel.TRACE:
77
- return "TRACE";
77
+ case LogLevel.ALL:
78
+ return "ALL";
78
79
  case LogLevel.DEBUG:
79
80
  return "DEBUG";
80
81
  case LogLevel.INFO:
@@ -3,12 +3,19 @@
3
3
  * Validation Adapters
4
4
  * @module @expressots/core/validation/adapters
5
5
  *
6
- * Built-in adapters for validation libraries.
7
- * Additional adapters (Zod, Yup, Joi) available as separate packages:
8
- * - @expressots/validator-zod
9
- * - @expressots/validator-yup
6
+ * Built-in adapters for validation libraries. The validation libraries
7
+ * themselves (`class-validator`, `zod`, `yup`) are *optional peer
8
+ * dependencies* — install only the one(s) you actually use.
9
+ *
10
+ * Additional Joi adapter is on the roadmap.
10
11
  */
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.ClassValidatorAdapter = void 0;
13
+ exports.createYupValidator = exports.YupValidatorAdapter = exports.createZodValidator = exports.ZodValidatorAdapter = exports.ClassValidatorAdapter = void 0;
13
14
  var class_validator_adapter_js_1 = require("./class-validator.adapter.js");
14
15
  Object.defineProperty(exports, "ClassValidatorAdapter", { enumerable: true, get: function () { return class_validator_adapter_js_1.ClassValidatorAdapter; } });
16
+ var zod_adapter_js_1 = require("./zod.adapter.js");
17
+ Object.defineProperty(exports, "ZodValidatorAdapter", { enumerable: true, get: function () { return zod_adapter_js_1.ZodValidatorAdapter; } });
18
+ Object.defineProperty(exports, "createZodValidator", { enumerable: true, get: function () { return zod_adapter_js_1.createZodValidator; } });
19
+ var yup_adapter_js_1 = require("./yup.adapter.js");
20
+ Object.defineProperty(exports, "YupValidatorAdapter", { enumerable: true, get: function () { return yup_adapter_js_1.YupValidatorAdapter; } });
21
+ Object.defineProperty(exports, "createYupValidator", { enumerable: true, get: function () { return yup_adapter_js_1.createYupValidator; } });