@macss/modular-api 0.2.0 → 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.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Use-case centric toolkit for building modular APIs with Express.
4
4
  Define `UseCase` classes (input → validate → execute → output), connect them to HTTP routes, and get automatic Swagger/OpenAPI documentation.
5
5
 
6
- > TypeScript port of [modular_api](https://pub.dev/packages/modular_api) (Dart/Shelf)
6
+ > Also available in **Dart**: [modular_api](https://pub.dev/packages/modular_api)
7
7
 
8
8
  ---
9
9
 
@@ -54,6 +54,7 @@ See `example/example.ts` for the full implementation including Input, Output, Us
54
54
  - Swagger UI at `/docs` — auto-generated from registered use cases
55
55
  - Health check at `GET /health` — [IETF Health Check Response Format](doc/health_check_guide.md)
56
56
  - Prometheus metrics at `GET /metrics` — [Prometheus exposition format](doc/metrics_guide.md)
57
+ - Structured JSON logging — Loki/Grafana compatible, [request-scoped with trace_id](doc/logger_guide.md)
57
58
  - All endpoints default to `POST` (configurable per use case)
58
59
  - Full TypeScript declarations (`.d.ts`) included
59
60
 
@@ -62,7 +63,7 @@ See `example/example.ts` for the full implementation including Input, Output, Us
62
63
  ## Installation
63
64
 
64
65
  ```bash
65
- npm install modular_api
66
+ npm install @macss/modular-api
66
67
  ```
67
68
 
68
69
  ---
@@ -116,17 +117,6 @@ HTTP Request → ModularApi → Module → UseCase → Business Logic → Output
116
117
 
117
118
  ---
118
119
 
119
- ## Dart version
120
-
121
- This is the TypeScript port. The original Dart version is available at:
122
-
123
- - **pub.dev**: [modular_api](https://pub.dev/packages/modular_api)
124
- - **GitHub**: [macss-dev/modular_api](https://github.com/macss-dev/modular_api)
125
-
126
- Both SDKs share the same architecture and API surface at v0.1.0.
127
-
128
- ---
129
-
130
120
  ## License
131
121
 
132
122
  MIT © [ccisne.dev](https://ccisne.dev)
@@ -0,0 +1,79 @@
1
+ /**
2
+ * RFC 5424 log levels in descending severity order.
3
+ *
4
+ * Filtering rule: if configured `logLevel = X`, only messages with
5
+ * `value <= X` are emitted. Higher values produce total silence.
6
+ */
7
+ export declare enum LogLevel {
8
+ emergency = 0,// system unusable
9
+ alert = 1,// immediate action required
10
+ critical = 2,// critical condition
11
+ error = 3,// operation error, 5xx
12
+ warning = 4,// abnormal condition, 4xx
13
+ notice = 5,// normal but significant
14
+ info = 6,// normal flow, 2xx/3xx
15
+ debug = 7
16
+ }
17
+ /**
18
+ * Public logger interface exposed to UseCases.
19
+ *
20
+ * Each method corresponds to an RFC 5424 severity level.
21
+ * `fields` is an optional map of structured data attached to the log entry.
22
+ */
23
+ export interface ModularLogger {
24
+ /** Request-scoped trace ID for correlation. */
25
+ readonly traceId: string;
26
+ emergency(msg: string, fields?: Record<string, unknown>): void;
27
+ alert(msg: string, fields?: Record<string, unknown>): void;
28
+ critical(msg: string, fields?: Record<string, unknown>): void;
29
+ error(msg: string, fields?: Record<string, unknown>): void;
30
+ warning(msg: string, fields?: Record<string, unknown>): void;
31
+ notice(msg: string, fields?: Record<string, unknown>): void;
32
+ info(msg: string, fields?: Record<string, unknown>): void;
33
+ debug(msg: string, fields?: Record<string, unknown>): void;
34
+ }
35
+ /** Function signature for the output sink — defaults to `console.log`. */
36
+ export type WriteFn = (line: string) => void;
37
+ /**
38
+ * Per-request logger that carries `traceId` and respects `logLevel` filtering.
39
+ *
40
+ * Created by `loggingMiddleware` for each incoming HTTP request and injected
41
+ * into the UseCase via the `logger` property.
42
+ *
43
+ * Accepts an optional `writeFn` for output — defaults to `console.log`.
44
+ * In tests, pass a capturing function to inspect output without side-effects.
45
+ */
46
+ export declare class RequestScopedLogger implements ModularLogger {
47
+ readonly traceId: string;
48
+ readonly logLevel: LogLevel;
49
+ readonly serviceName: string;
50
+ private readonly writeFn;
51
+ constructor(traceId: string, logLevel: LogLevel, serviceName: string, writeFn?: WriteFn);
52
+ emergency(msg: string, fields?: Record<string, unknown>): void;
53
+ alert(msg: string, fields?: Record<string, unknown>): void;
54
+ critical(msg: string, fields?: Record<string, unknown>): void;
55
+ error(msg: string, fields?: Record<string, unknown>): void;
56
+ warning(msg: string, fields?: Record<string, unknown>): void;
57
+ notice(msg: string, fields?: Record<string, unknown>): void;
58
+ info(msg: string, fields?: Record<string, unknown>): void;
59
+ debug(msg: string, fields?: Record<string, unknown>): void;
60
+ /** Emits a "request received" log at `info` level. */
61
+ logRequest(opts: {
62
+ method: string;
63
+ route: string;
64
+ }): void;
65
+ /** Emits a "request completed" log at the level determined by `statusCode`. */
66
+ logResponse(opts: {
67
+ method: string;
68
+ route: string;
69
+ statusCode: number;
70
+ durationMs: number;
71
+ }): void;
72
+ /** Emits an "unhandled exception" log at `error` level. No stack trace. */
73
+ logUnhandledException(opts: {
74
+ route: string;
75
+ }): void;
76
+ private log;
77
+ /** Maps HTTP status code → RFC 5424 log level. */
78
+ static levelForStatus(status: number): LogLevel;
79
+ }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // core/logger/logger.ts
4
+ // LogLevel enum, ModularLogger interface, RequestScopedLogger.
5
+ // Mirror of logger.dart — RFC 5424, JSON to stdout, zero deps.
6
+ // ============================================================
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.RequestScopedLogger = exports.LogLevel = void 0;
9
+ /**
10
+ * RFC 5424 log levels in descending severity order.
11
+ *
12
+ * Filtering rule: if configured `logLevel = X`, only messages with
13
+ * `value <= X` are emitted. Higher values produce total silence.
14
+ */
15
+ var LogLevel;
16
+ (function (LogLevel) {
17
+ LogLevel[LogLevel["emergency"] = 0] = "emergency";
18
+ LogLevel[LogLevel["alert"] = 1] = "alert";
19
+ LogLevel[LogLevel["critical"] = 2] = "critical";
20
+ LogLevel[LogLevel["error"] = 3] = "error";
21
+ LogLevel[LogLevel["warning"] = 4] = "warning";
22
+ LogLevel[LogLevel["notice"] = 5] = "notice";
23
+ LogLevel[LogLevel["info"] = 6] = "info";
24
+ LogLevel[LogLevel["debug"] = 7] = "debug";
25
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
26
+ /** Human-readable name for each LogLevel value. */
27
+ const LEVEL_NAME = {
28
+ [LogLevel.emergency]: 'emergency',
29
+ [LogLevel.alert]: 'alert',
30
+ [LogLevel.critical]: 'critical',
31
+ [LogLevel.error]: 'error',
32
+ [LogLevel.warning]: 'warning',
33
+ [LogLevel.notice]: 'notice',
34
+ [LogLevel.info]: 'info',
35
+ [LogLevel.debug]: 'debug',
36
+ };
37
+ /**
38
+ * Per-request logger that carries `traceId` and respects `logLevel` filtering.
39
+ *
40
+ * Created by `loggingMiddleware` for each incoming HTTP request and injected
41
+ * into the UseCase via the `logger` property.
42
+ *
43
+ * Accepts an optional `writeFn` for output — defaults to `console.log`.
44
+ * In tests, pass a capturing function to inspect output without side-effects.
45
+ */
46
+ class RequestScopedLogger {
47
+ constructor(traceId, logLevel, serviceName, writeFn = (line) => process.stdout.write(line + '\n')) {
48
+ this.traceId = traceId;
49
+ this.logLevel = logLevel;
50
+ this.serviceName = serviceName;
51
+ this.writeFn = writeFn;
52
+ }
53
+ // ─── Public API (8 RFC 5424 levels) ──────────────────────────────
54
+ emergency(msg, fields) {
55
+ this.log(LogLevel.emergency, msg, { fields });
56
+ }
57
+ alert(msg, fields) {
58
+ this.log(LogLevel.alert, msg, { fields });
59
+ }
60
+ critical(msg, fields) {
61
+ this.log(LogLevel.critical, msg, { fields });
62
+ }
63
+ error(msg, fields) {
64
+ this.log(LogLevel.error, msg, { fields });
65
+ }
66
+ warning(msg, fields) {
67
+ this.log(LogLevel.warning, msg, { fields });
68
+ }
69
+ notice(msg, fields) {
70
+ this.log(LogLevel.notice, msg, { fields });
71
+ }
72
+ info(msg, fields) {
73
+ this.log(LogLevel.info, msg, { fields });
74
+ }
75
+ debug(msg, fields) {
76
+ this.log(LogLevel.debug, msg, { fields });
77
+ }
78
+ // ─── Framework-internal: request/response logging ────────────────
79
+ /** Emits a "request received" log at `info` level. */
80
+ logRequest(opts) {
81
+ this.log(LogLevel.info, 'request received', {
82
+ extra: { method: opts.method, route: opts.route },
83
+ });
84
+ }
85
+ /** Emits a "request completed" log at the level determined by `statusCode`. */
86
+ logResponse(opts) {
87
+ this.log(RequestScopedLogger.levelForStatus(opts.statusCode), 'request completed', {
88
+ extra: {
89
+ method: opts.method,
90
+ route: opts.route,
91
+ status: opts.statusCode,
92
+ duration_ms: opts.durationMs,
93
+ },
94
+ });
95
+ }
96
+ /** Emits an "unhandled exception" log at `error` level. No stack trace. */
97
+ logUnhandledException(opts) {
98
+ this.log(LogLevel.error, 'unhandled exception', {
99
+ extra: { route: opts.route, status: 500 },
100
+ });
101
+ }
102
+ // ─── Internal ────────────────────────────────────────────────────
103
+ log(level, msg, opts = {}) {
104
+ // Filtering: only emit if the message level <= configured logLevel.
105
+ if (level > this.logLevel)
106
+ return;
107
+ const entry = {
108
+ ts: Date.now() / 1000,
109
+ level: LEVEL_NAME[level],
110
+ severity: level,
111
+ msg,
112
+ service: this.serviceName,
113
+ trace_id: this.traceId,
114
+ };
115
+ if (opts.extra)
116
+ Object.assign(entry, opts.extra);
117
+ if (opts.fields)
118
+ entry['fields'] = opts.fields;
119
+ this.writeFn(JSON.stringify(entry));
120
+ }
121
+ /** Maps HTTP status code → RFC 5424 log level. */
122
+ static levelForStatus(status) {
123
+ if (status >= 500)
124
+ return LogLevel.error;
125
+ if (status >= 400)
126
+ return LogLevel.warning;
127
+ if (status >= 200 && status < 400)
128
+ return LogLevel.info;
129
+ return LogLevel.notice; // 1xx
130
+ }
131
+ }
132
+ exports.RequestScopedLogger = RequestScopedLogger;
@@ -0,0 +1,25 @@
1
+ import type { RequestHandler } from 'express';
2
+ import { LogLevel } from './logger';
3
+ import type { WriteFn } from './logger';
4
+ /** Key used in `res.locals` to propagate the logger to downstream handlers. */
5
+ export declare const LOGGER_LOCALS_KEY = "modularLogger";
6
+ export interface LoggingMiddlewareOptions {
7
+ logLevel: LogLevel;
8
+ serviceName: string;
9
+ excludedRoutes?: string[];
10
+ /** Override output for testing. Defaults to stdout. */
11
+ writeFn?: WriteFn;
12
+ }
13
+ /**
14
+ * Creates an Express middleware that:
15
+ *
16
+ * 1. Reads or generates a `trace_id` (from `X-Request-ID` header).
17
+ * 2. Creates a {@link RequestScopedLogger} scoped to the current request.
18
+ * 3. Emits a `"request received"` log at `info` level.
19
+ * 4. Passes the logger via `res.locals` for downstream handlers.
20
+ * 5. Emits a `"request completed"` log (level based on status code).
21
+ * 6. Returns the `X-Request-ID` header in the response.
22
+ *
23
+ * Requests whose path matches `excludedRoutes` are passed through silently.
24
+ */
25
+ export declare function loggingMiddleware(opts: LoggingMiddlewareOptions): RequestHandler;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // core/logger/logging_middleware.ts
4
+ // Express middleware — trace_id, structured JSON logs.
5
+ // Mirror of logging_middleware.dart.
6
+ // ============================================================
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.LOGGER_LOCALS_KEY = void 0;
9
+ exports.loggingMiddleware = loggingMiddleware;
10
+ const node_crypto_1 = require("node:crypto");
11
+ const logger_1 = require("./logger");
12
+ /** Key used in `res.locals` to propagate the logger to downstream handlers. */
13
+ exports.LOGGER_LOCALS_KEY = 'modularLogger';
14
+ /**
15
+ * Creates an Express middleware that:
16
+ *
17
+ * 1. Reads or generates a `trace_id` (from `X-Request-ID` header).
18
+ * 2. Creates a {@link RequestScopedLogger} scoped to the current request.
19
+ * 3. Emits a `"request received"` log at `info` level.
20
+ * 4. Passes the logger via `res.locals` for downstream handlers.
21
+ * 5. Emits a `"request completed"` log (level based on status code).
22
+ * 6. Returns the `X-Request-ID` header in the response.
23
+ *
24
+ * Requests whose path matches `excludedRoutes` are passed through silently.
25
+ */
26
+ function loggingMiddleware(opts) {
27
+ const excludedSet = new Set(opts.excludedRoutes ?? []);
28
+ return (req, res, next) => {
29
+ const path = req.path;
30
+ // Skip excluded routes (health, metrics, docs).
31
+ if (excludedSet.has(path)) {
32
+ return next();
33
+ }
34
+ // 1. Resolve trace_id
35
+ const headerValue = req.headers['x-request-id'];
36
+ const traceId = typeof headerValue === 'string' && headerValue.length > 0 ? headerValue : (0, node_crypto_1.randomUUID)();
37
+ // 2. Create per-request logger
38
+ const logger = new logger_1.RequestScopedLogger(traceId, opts.logLevel, opts.serviceName, opts.writeFn);
39
+ const method = req.method.toUpperCase();
40
+ const route = path;
41
+ // 3. "request received"
42
+ logger.logRequest({ method, route });
43
+ // 4. Propagate logger via res.locals
44
+ res.locals[exports.LOGGER_LOCALS_KEY] = logger;
45
+ // 5. Attach X-Request-ID to response
46
+ res.setHeader('X-Request-ID', traceId);
47
+ // 6. Capture timing and emit response log on finish
48
+ const startNs = process.hrtime.bigint();
49
+ res.on('finish', () => {
50
+ const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
51
+ logger.logResponse({
52
+ method,
53
+ route,
54
+ statusCode: res.statusCode,
55
+ durationMs,
56
+ });
57
+ });
58
+ next();
59
+ };
60
+ }
@@ -2,6 +2,7 @@ import { type RequestHandler } from 'express';
2
2
  import { ModuleBuilder } from './module_builder';
3
3
  import type { HealthCheck } from './health/health_check';
4
4
  import { MetricsRegistrar } from './metrics/metric_registry';
5
+ import { LogLevel } from './logger/logger';
5
6
  export interface ModularApiOptions {
6
7
  /** Base path prefix for all module routes. Default: '/api' */
7
8
  basePath?: string;
@@ -20,6 +21,11 @@ export interface ModularApiOptions {
20
21
  metricsPath?: string;
21
22
  /** Routes excluded from instrumentation. Default: ['/metrics', '/health', '/docs'] */
22
23
  excludedMetricsRoutes?: string[];
24
+ /**
25
+ * Minimum log level for the structured JSON logger.
26
+ * Default: LogLevel.info (emits emergency..info, suppresses debug).
27
+ */
28
+ logLevel?: LogLevel;
23
29
  }
24
30
  /**
25
31
  * Main entry point for modular_api.
@@ -57,6 +63,7 @@ export declare class ModularApi {
57
63
  private readonly httpRequestsTotal?;
58
64
  private readonly httpRequestsInFlight?;
59
65
  private readonly httpRequestDuration?;
66
+ private readonly logLevel;
60
67
  /** Public accessor for custom-metric registration. Undefined when metrics are disabled. */
61
68
  get metrics(): MetricsRegistrar | undefined;
62
69
  constructor(options?: ModularApiOptions);
@@ -17,6 +17,8 @@ const health_service_1 = require("./health/health_service");
17
17
  const health_handler_1 = require("./health/health_handler");
18
18
  const metric_registry_1 = require("./metrics/metric_registry");
19
19
  const metrics_middleware_1 = require("./metrics/metrics_middleware");
20
+ const logging_middleware_1 = require("./logger/logging_middleware");
21
+ const logger_1 = require("./logger/logger");
20
22
  const registry_1 = require("./registry");
21
23
  /**
22
24
  * Main entry point for modular_api.
@@ -56,6 +58,8 @@ class ModularApi {
56
58
  this.metricsEnabled = options.metricsEnabled ?? false;
57
59
  this.metricsPath = options.metricsPath ?? '/metrics';
58
60
  this.excludedMetricsRoutes = options.excludedMetricsRoutes ?? ['/metrics', '/health', '/docs'];
61
+ // Logging
62
+ this.logLevel = options.logLevel ?? logger_1.LogLevel.info;
59
63
  if (this.metricsEnabled) {
60
64
  this.metricRegistry = new metric_registry_1.MetricRegistry();
61
65
  this._metricsRegistrar = new metric_registry_1.MetricsRegistrar(this.metricRegistry);
@@ -134,7 +138,14 @@ class ModularApi {
134
138
  serve(options) {
135
139
  const { port, host = '0.0.0.0' } = options;
136
140
  return new Promise((resolve) => {
137
- // Metrics middleware FIRST — before user middlewares & routes.
141
+ // Logging middleware FIRST — trace_id + structured JSON logs.
142
+ const excludedLogRoutes = ['/health', this.metricsPath, '/docs', '/docs/'];
143
+ this.app.use((0, logging_middleware_1.loggingMiddleware)({
144
+ logLevel: this.logLevel,
145
+ serviceName: this.title,
146
+ excludedRoutes: excludedLogRoutes,
147
+ }));
148
+ // Metrics middleware — before user middlewares & routes.
138
149
  // Created here so registeredPaths is populated from apiRegistry.
139
150
  if (this.metricsEnabled &&
140
151
  this.httpRequestsTotal &&
@@ -1,3 +1,4 @@
1
+ import type { ModularLogger } from './logger/logger';
1
2
  /**
2
3
  * **Contract** — use `implements Input`.
3
4
  *
@@ -94,6 +95,12 @@ export declare abstract class UseCase<I extends Input, O extends Output> {
94
95
  abstract readonly input: I;
95
96
  /** Output DTO — set in execute(). */
96
97
  abstract output: O;
98
+ /**
99
+ * Request-scoped logger injected by the framework's logging middleware.
100
+ * Available inside `execute()`. Undefined when running without middleware
101
+ * or in tests that don't provide one.
102
+ */
103
+ logger?: ModularLogger;
97
104
  /**
98
105
  * Synchronous validation.
99
106
  * Return a human-readable error string to abort execution with HTTP 400.
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.useCaseHandler = useCaseHandler;
9
9
  const use_case_exception_1 = require("./use_case_exception");
10
+ const logging_middleware_1 = require("./logger/logging_middleware");
10
11
  const JSON_HEADERS = { 'Content-Type': 'application/json; charset=utf-8' };
11
12
  /**
12
13
  * Wraps any UseCase factory into an Express RequestHandler.
@@ -36,6 +37,11 @@ function useCaseHandler(factory) {
36
37
  : (req.body ?? {});
37
38
  // 2. Build use case
38
39
  const useCase = factory(data);
40
+ // 2b. Inject request-scoped logger (if logging middleware is active)
41
+ const logger = res.locals[logging_middleware_1.LOGGER_LOCALS_KEY];
42
+ if (logger) {
43
+ useCase.logger = logger;
44
+ }
39
45
  // 3. Validate
40
46
  const validationError = useCase.validate();
41
47
  if (validationError !== null) {
@@ -1,4 +1,5 @@
1
1
  import type { UseCaseFactory, Input, Output } from './usecase';
2
+ import type { ModularLogger } from './logger/logger';
2
3
  export interface TestResponse {
3
4
  statusCode: number;
4
5
  body: Record<string, unknown>;
@@ -21,4 +22,6 @@ export interface TestResponse {
21
22
  * expect(response.body).toEqual({ message: 'Hello, World!' });
22
23
  * ```
23
24
  */
24
- export declare function useCaseTestHandler<I extends Input, O extends Output>(factory: UseCaseFactory<I, O>, input?: Record<string, unknown>): Promise<TestResponse>;
25
+ export declare function useCaseTestHandler<I extends Input, O extends Output>(factory: UseCaseFactory<I, O>, input?: Record<string, unknown>, options?: {
26
+ logger?: ModularLogger;
27
+ }): Promise<TestResponse>;
@@ -25,9 +25,13 @@ const use_case_exception_1 = require("./use_case_exception");
25
25
  * expect(response.body).toEqual({ message: 'Hello, World!' });
26
26
  * ```
27
27
  */
28
- async function useCaseTestHandler(factory, input = {}) {
28
+ async function useCaseTestHandler(factory, input = {}, options) {
29
29
  try {
30
30
  const useCase = factory(input);
31
+ // Inject logger if provided
32
+ if (options?.logger) {
33
+ useCase.logger = options.logger;
34
+ }
31
35
  const validationError = useCase.validate();
32
36
  if (validationError !== null) {
33
37
  return {
package/dist/index.d.ts CHANGED
@@ -17,3 +17,7 @@ export { healthHandler } from './core/health/health_handler';
17
17
  export { MetricRegistry, MetricsRegistrar } from './core/metrics/metric_registry';
18
18
  export { metricsMiddleware, metricsHandler } from './core/metrics/metrics_middleware';
19
19
  export type { MetricsMiddlewareOptions } from './core/metrics/metrics_middleware';
20
+ export { LogLevel, RequestScopedLogger } from './core/logger/logger';
21
+ export type { ModularLogger } from './core/logger/logger';
22
+ export { loggingMiddleware, LOGGER_LOCALS_KEY } from './core/logger/logging_middleware';
23
+ export type { LoggingMiddlewareOptions } from './core/logger/logging_middleware';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // import { ModularApi, UseCase, Input, Output } from 'modular_api'
6
6
  // ============================================================
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.metricsHandler = exports.metricsMiddleware = exports.MetricsRegistrar = exports.MetricRegistry = exports.healthHandler = exports.HealthResponse = exports.HealthService = exports.HealthCheckResult = exports.HealthCheck = exports.cors = exports.useCaseTestHandler = exports.ModuleBuilder = exports.ModularApi = exports.UseCaseException = exports.UseCase = exports.Output = exports.Input = void 0;
8
+ exports.LOGGER_LOCALS_KEY = exports.loggingMiddleware = exports.RequestScopedLogger = exports.LogLevel = exports.metricsHandler = exports.metricsMiddleware = exports.MetricsRegistrar = exports.MetricRegistry = exports.healthHandler = exports.HealthResponse = exports.HealthService = exports.HealthCheckResult = exports.HealthCheck = exports.cors = exports.useCaseTestHandler = exports.ModuleBuilder = exports.ModularApi = exports.UseCaseException = exports.UseCase = exports.Output = exports.Input = void 0;
9
9
  // Core abstractions
10
10
  var usecase_1 = require("./core/usecase");
11
11
  Object.defineProperty(exports, "Input", { enumerable: true, get: function () { return usecase_1.Input; } });
@@ -42,3 +42,10 @@ Object.defineProperty(exports, "MetricsRegistrar", { enumerable: true, get: func
42
42
  var metrics_middleware_1 = require("./core/metrics/metrics_middleware");
43
43
  Object.defineProperty(exports, "metricsMiddleware", { enumerable: true, get: function () { return metrics_middleware_1.metricsMiddleware; } });
44
44
  Object.defineProperty(exports, "metricsHandler", { enumerable: true, get: function () { return metrics_middleware_1.metricsHandler; } });
45
+ // Logger — Structured JSON logging (Loki/Grafana compatible)
46
+ var logger_1 = require("./core/logger/logger");
47
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return logger_1.LogLevel; } });
48
+ Object.defineProperty(exports, "RequestScopedLogger", { enumerable: true, get: function () { return logger_1.RequestScopedLogger; } });
49
+ var logging_middleware_1 = require("./core/logger/logging_middleware");
50
+ Object.defineProperty(exports, "loggingMiddleware", { enumerable: true, get: function () { return logging_middleware_1.loggingMiddleware; } });
51
+ Object.defineProperty(exports, "LOGGER_LOCALS_KEY", { enumerable: true, get: function () { return logging_middleware_1.LOGGER_LOCALS_KEY; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@macss/modular-api",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Use-case-centric toolkit for building modular APIs with Express. Define UseCase classes (input → validate → execute → output), connect them to HTTP routes, and expose Swagger/OpenAPI documentation automatically.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",