{"_id":"@aydope/mokat","_rev":"6-a6dff91791ab84f603a77894acd70c96","name":"@aydope/mokat","dist-tags":{"latest":"0.1.5"},"versions":{"0.1.0":{"name":"@aydope/mokat","version":"0.1.0","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"url":"https://github.com/aydope","name":"Aydope","email":"amin0xa1b@gmail.com"},"license":"MIT","_id":"@aydope/mokat@0.1.0","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"homepage":"https://github.com/aydope/mokat#readme","bugs":{"url":"https://github.com/aydope/mokat/issues"},"dist":{"shasum":"50e4bda8ba5ed4d302fb592dc19c20e114850da8","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.0.tgz","fileCount":23,"integrity":"sha512-Wox9CsvMM9hD6wOSSlW5wp86wY9RIZhX3JvW4iwvGTsOkYK1U8q+Ka7EBir1ijw1PAA0lG7Cdd6r+ryPhgJ3lQ==","signatures":[{"sig":"MEUCIQD0LV0Ve2xfIRIKVx5hDPIM6oqPyx5t3lvAcNL4Y2Mn/AIgY02/Gh2NHWzpwO07Ue0OgG4WfotMAW6vWdW+GzNccBQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102174},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"42762e563e1d3e00ed20c04cbec6089c8391b37d","scripts":{"dev":"tsc --watch","test":"node dist/index.js","build":"tsc","prepare":"npm run build"},"typings":"./dist/index.d.ts","_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"repository":{"url":"git+https://github.com/aydope/mokat.git","type":"git"},"_npmVersion":"11.13.0","description":"A professional logging and timeout utility for Node.js","directories":{},"_nodeVersion":"24.19.0","dependencies":{"chalk":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mokat_0.1.0_1787002016756_0.7869479722113153","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aydope/mokat","version":"0.1.1","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"url":"https://github.com/aydope","name":"Aydope","email":"amin0xa1b@gmail.com"},"license":"MIT","_id":"@aydope/mokat@0.1.1","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"homepage":"https://github.com/aydope/mokat#readme","bugs":{"url":"https://github.com/aydope/mokat/issues"},"dist":{"shasum":"dd00fa1f612cc060bef0145727f12abbd277b94e","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.1.tgz","fileCount":23,"integrity":"sha512-gNuPQLjgr6NksKLNQVC9spaoBfbAmVfvK5g0Y1IW9YettDVYtxSZRvsyl0ez2fNV1lMP8ZqybA4ECuc3XVR8Bg==","signatures":[{"sig":"MEYCIQCGQ5/A2d/sMDP/9Tk+TMufrBUHtx587IxNZ5hnkc+hXAIhAPaTVs2TNg21GW8HA+rGOsbzO5VFGMvzb4sFHXWTlN9Y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102212},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"7bbae4636c901cf33818c7bcb933b42397f0fcef","scripts":{"dev":"tsc --watch","test":"node dist/index.js","build":"tsc","prepare":"npm run build"},"typings":"./dist/index.d.ts","_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"repository":{"url":"git+https://github.com/aydope/mokat.git","type":"git"},"_npmVersion":"11.13.0","description":"A professional logging and timeout utility for Node.js","directories":{},"_nodeVersion":"24.19.0","dependencies":{"chalk":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mokat_0.1.1_1787002659829_0.38203023937415725","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aydope/mokat","version":"0.1.2","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"url":"https://github.com/aydope","name":"Aydope","email":"amin0xa1b@gmail.com"},"license":"MIT","_id":"@aydope/mokat@0.1.2","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"homepage":"https://github.com/aydope/mokat#readme","bugs":{"url":"https://github.com/aydope/mokat/issues"},"dist":{"shasum":"95617c341191286bc7d3afaa93b18ae11cb01900","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.2.tgz","fileCount":23,"integrity":"sha512-icZSVRYaR3b7QqyQPF4Hgotejrh3q1THIyF3QrQQuL2EH9Vj+OE3iCQ8HC/ccDWtUDSFrrrdOz7EQBOmh873Eg==","signatures":[{"sig":"MEYCIQC8ohAsnXddROy/E2VYvKWxYuHaPt9iXMaAtm6keoSD+QIhALBZmv6Qlg0jiydyovUvRN+qWiwkSG1cD/i0iNDCzB1A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102192},"main":"./dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"8b7532744d186ad1bc2becbaff2a3cdaa00275a4","scripts":{"dev":"tsc --watch","test":"node dist/index.js","build":"tsc","prepare":"npm run build"},"typings":"./dist/index.d.ts","_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"repository":{"url":"git+https://github.com/aydope/mokat.git","type":"git"},"_npmVersion":"11.13.0","description":"A professional logging and timeout utility for Node.js","directories":{},"_nodeVersion":"24.19.0","dependencies":{"chalk":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mokat_0.1.2_1787003070915_0.4017732743312059","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aydope/mokat","version":"0.1.3","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"url":"https://github.com/aydope","name":"Aydope","email":"amin0xa1b@gmail.com"},"license":"MIT","_id":"@aydope/mokat@0.1.3","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"homepage":"https://github.com/aydope/mokat#readme","bugs":{"url":"https://github.com/aydope/mokat/issues"},"dist":{"shasum":"cc7dde1c49022adc455fc1c3ec3685152a1a6520","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.3.tgz","fileCount":23,"integrity":"sha512-t/R1i/9cE+trRHv7VyuNerQZorUDYQ2gNk6na+jFeatGEn36dWndoZgRb0uSCJslzwOg/uX0AhNrpV30/DNqBA==","signatures":[{"sig":"MEYCIQC1E7vd9J5xhLZEqUlh1BZIVjptSXrjlxTXLMuZ2RZdkwIhAJcmCJJLS7D2POIIbD7cPb4GifptGl54H8SYbt0xYvsc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102430},"main":"./dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"cc8324e8eec3a87c2e65b9afe78a20beaccd1264","scripts":{"dev":"tsc --watch","test":"node dist/index.js","build":"tsc","prepare":"npm run build"},"typings":"./dist/index.d.ts","_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"repository":{"url":"git+https://github.com/aydope/mokat.git","type":"git"},"_npmVersion":"11.13.0","description":"A professional logging and timeout utility for Node.js","directories":{},"_nodeVersion":"24.19.0","dependencies":{"chalk":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mokat_0.1.3_1787003559479_0.3834246015506164","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@aydope/mokat","version":"0.1.4","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"url":"https://github.com/aydope","name":"Aydope","email":"amin0xa1b@gmail.com"},"license":"MIT","_id":"@aydope/mokat@0.1.4","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"homepage":"https://github.com/aydope/mokat#readme","bugs":{"url":"https://github.com/aydope/mokat/issues"},"dist":{"shasum":"6fb41d612c5be64ce25d0f80f2861796df13a857","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.4.tgz","fileCount":23,"integrity":"sha512-aeqlEQzXMllXdF/8VZrvlLyUDxah9FvNmMwCulo+lt04Jb45mFrw+aNTFcxgwpr4NuBazuPnn2R+LusmrT7vYA==","signatures":[{"sig":"MEYCIQCTPt8uIM/RQgVHHavB4FqhO0Z8ll6VH9AsnqRf3xBWgQIhAOy2be4vpPX8BrnERDI2KaBAb7ZXr8OWjLTsSlAoWuo+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":103146},"main":"./dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"747b9b07b58894ad4fc5c6ea96ba28d72ea59e51","scripts":{"dev":"tsc --watch","test":"node dist/index.js","build":"tsc","prepare":"npm run build"},"typings":"./dist/index.d.ts","_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"repository":{"url":"git+https://github.com/aydope/mokat.git","type":"git"},"_npmVersion":"11.13.0","description":"A professional logging and timeout utility for Node.js","directories":{},"_nodeVersion":"24.19.0","dependencies":{"chalk":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.2.0"},"_npmOperationalInternal":{"tmp":"tmp/mokat_0.1.4_1787004867484_0.06335524313012053","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@aydope/mokat","version":"0.1.5","description":"A professional logging and timeout utility for Node.js","main":"./dist/index.js","types":"./dist/index.d.ts","typings":"./dist/index.d.ts","scripts":{"build":"tsc","prepare":"npm run build","test":"node dist/index.js","dev":"tsc --watch"},"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js","import":"./dist/index.js","default":"./dist/index.js"},"./package.json":"./package.json"},"keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"author":{"name":"Aydope","email":"amin0xa1b@gmail.com","url":"https://github.com/aydope"},"repository":{"type":"git","url":"git+https://github.com/aydope/mokat.git"},"bugs":{"url":"https://github.com/aydope/mokat/issues"},"homepage":"https://github.com/aydope/mokat#readme","license":"MIT","engines":{"node":">=18.0.0"},"dependencies":{"chalk":"^6.0.0"},"devDependencies":{"@types/node":"^26.2.0","typescript":"^7.0.2"},"gitHead":"31885b6215a7743b42e77fd04f42da993f095dc0","_id":"@aydope/mokat@0.1.5","_nodeVersion":"24.19.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-B+ivB2CJ+TMJmY2gLk/SZED7A3v9Mi06gUCxZPuKpN4fsKWftvnU/0JXfxhev8orxizk+ix12OlwE/WjY7vKcA==","shasum":"fd0b9c7df43618e822b03aec4f70b263aaf30a54","tarball":"https://registry.npmjs.org/@aydope/mokat/-/mokat-0.1.5.tgz","fileCount":23,"unpackedSize":103298,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHRFbcqagPIuYiGehRiL5C/bGReEvZWMulRuv6vF4B2CAiEA8BFaJbOkpQg/uPOYqe4xBzCRIt2x5omZkJ+NUsg0rg4="}]},"_npmUser":{"name":"aydope","email":"amin0xa1b@gmail.com"},"directories":{},"maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mokat_0.1.5_1787008984165_0.39580553328780743"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-17T21:26:56.336Z","modified":"2026-08-17T23:23:04.448Z","0.1.0":"2026-08-17T21:26:56.919Z","0.1.1":"2026-08-17T21:37:39.988Z","0.1.2":"2026-08-17T21:44:31.056Z","0.1.3":"2026-08-17T21:52:39.620Z","0.1.4":"2026-08-17T22:14:27.658Z","0.1.5":"2026-08-17T23:23:04.308Z"},"bugs":{"url":"https://github.com/aydope/mokat/issues"},"author":{"name":"Aydope","email":"amin0xa1b@gmail.com","url":"https://github.com/aydope"},"license":"MIT","homepage":"https://github.com/aydope/mokat#readme","keywords":["typescript","timeout","delay","logger","promise","mokat","node-utils"],"repository":{"type":"git","url":"git+https://github.com/aydope/mokat.git"},"description":"A professional logging and timeout utility for Node.js","maintainers":[{"name":"aydope","email":"amin0xa1b@gmail.com"}],"readme":"# Mokat\r\n\r\n> A professional, lightweight logging, timeout, profiling, context, file logging, middleware, and event utility for Node.js and TypeScript.\r\n\r\n[![npm version](https://img.shields.io/npm/v/@aydope%2Fmokat.svg)](https://www.npmjs.com/package/mokat)\r\n[![npm downloads](https://img.shields.io/npm/dm/@aydope%2Fmokat.svg)](https://www.npmjs.com/package/mokat)\r\n[![License](https://img.shields.io/npm/l/@aydope%2Fmokat.svg)](LICENSE)\r\n[![Node.js](https://img.shields.io/badge/node-%3E%3D16-brightgreen.svg)](https://nodejs.org/)\r\n[![TypeScript](https://img.shields.io/badge/TypeScript-supported-blue.svg)](https://www.typescriptlang.org/)\r\n\r\nMokat is a lightweight and configurable utility toolkit for Node.js and TypeScript applications.\r\n\r\nIt provides structured logging, pretty console output, file logging, buffered writes, startup file rotation, sensitive-data redaction, global bindings, child loggers, nested contexts, session IDs, middleware, events, profiling, function timing, assertions, runtime statistics, graceful shutdown, and Promise-based timeout utilities.\r\n\r\n---\r\n\r\n## Features\r\n\r\n- TypeScript-first API\r\n- Node.js logging utility\r\n- ESM/CommonJS-friendly package API\r\n- Six log levels: `fatal`, `error`, `warn`, `info`, `debug`, `trace`\r\n- Structured JSON logging\r\n- Human-readable pretty logging\r\n- Colored console output\r\n- File logging\r\n- Automatic log directory creation\r\n- Buffered file writes\r\n- Configurable batch flushing\r\n- Startup file rotation\r\n- Sensitive-data redaction\r\n- Process ID bindings\r\n- Hostname bindings\r\n- Custom global bindings\r\n- Child loggers\r\n- Nested logging contexts\r\n- Automatic context cleanup\r\n- Logger session IDs\r\n- EventEmitter integration\r\n- Configurable logging events\r\n- Logging middleware\r\n- Runtime log-level changes\r\n- Logging statistics\r\n- Logger uptime tracking\r\n- Manual performance profiling\r\n- Automatic function timing\r\n- Promise-aware function wrappers\r\n- Runtime assertions\r\n- Promise-based timeout utility\r\n- Custom timeout errors\r\n- Polling with `waitFor()`\r\n- Silent mode\r\n- Graceful logger shutdown\r\n\r\n---\r\n\r\n## Installation\r\n\r\n### npm\r\n\r\n```bash\r\nnpm install @aydope/mokat\r\n```\r\n\r\n### pnpm\r\n\r\n```bash\r\npnpm add @aydope/mokat\r\n```\r\n\r\n### yarn\r\n\r\n```bash\r\nyarn add @aydope/mokat\r\n```\r\n\r\n### Bun\r\n\r\n```bash\r\nbun add @aydope/mokat\r\n```\r\n\r\n---\r\n\r\n## Requirements\r\n\r\n- Node.js `>= 16`\r\n- TypeScript recommended\r\n- ESM or CommonJS-compatible Node.js environment\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n```ts\r\nimport { Logger } from \"@aydope/mokat\";\r\n\r\nLogger.info(\"Application started\");\r\n\r\nLogger.warn(\"This is a warning\");\r\n\r\nLogger.error(\"Something went wrong\", {\r\n  code: \"DATABASE_CONNECTION_FAILED\",\r\n});\r\n\r\nLogger.debug(\"Debug information\");\r\n```\r\n\r\nBy default, Mokat uses the `info` log level and pretty console output.\r\n\r\nExample:\r\n\r\n```text\r\n[14:32:10 UTC] INFO: Application started\r\n```\r\n\r\nMokat can also be configured for structured JSON logging:\r\n\r\n```ts\r\nLogger.configure({\r\n  pretty: false,\r\n});\r\n\r\nLogger.info(\"Application started\");\r\n```\r\n\r\nExample JSON:\r\n\r\n```json\r\n{\r\n  \"level\": \"info\",\r\n  \"msg\": \"Application started\",\r\n  \"pid\": 12345,\r\n  \"hostname\": \"server-01\",\r\n  \"time\": \"2026-08-18T12:00:00.000Z\"\r\n}\r\n```\r\n\r\n---\r\n\r\n## Log Levels\r\n\r\nMokat supports six log levels:\r\n\r\n| Level   | Priority | Label   |\r\n| ------- | -------: | ------- |\r\n| `fatal` |       60 | `FATAL` |\r\n| `error` |       50 | `ERROR` |\r\n| `warn`  |       40 | `WARN`  |\r\n| `info`  |       30 | `INFO`  |\r\n| `debug` |       20 | `DEBUG` |\r\n| `trace` |       10 | `TRACE` |\r\n\r\nThe configured level acts as the minimum level that will be logged.\r\n\r\n```ts\r\nLogger.configure({\r\n  level: \"debug\",\r\n});\r\n\r\nLogger.fatal(\"Fatal error\");\r\nLogger.error(\"Error\");\r\nLogger.warn(\"Warning\");\r\nLogger.info(\"Information\");\r\nLogger.debug(\"Debug information\");\r\nLogger.trace(\"Trace information\");\r\n```\r\n\r\nWith `debug` configured, `trace` is filtered because its priority is lower than `debug`.\r\n\r\n---\r\n\r\n## Configuration\r\n\r\nMokat can be configured globally through `Logger.configure()`.\r\n\r\n```ts\r\nLogger.configure({\r\n  level: \"debug\",\r\n\r\n  pretty: true,\r\n  timestamp: true,\r\n  colors: true,\r\n\r\n  file: \"./logs/app.log\",\r\n\r\n  redact: [\"password\", \"token\", \"secret\"],\r\n\r\n  bindings: {\r\n    service: \"my-api\",\r\n    environment: \"production\",\r\n  },\r\n});\r\n```\r\n\r\n### Options\r\n\r\n| Option             | Type                  | Default      | Description                                            |\r\n| ------------------ | --------------------- | ------------ | ------------------------------------------------------ |\r\n| `level`            | `LogLevel`            | `\"info\"`     | Minimum log level                                      |\r\n| `pretty`           | `boolean`             | `true`       | Enable human-readable output                           |\r\n| `timestamp`        | `boolean`             | `true`       | Include timestamps                                     |\r\n| `file`             | `string`              | `\"\"`         | Log file path                                          |\r\n| `colors`           | `boolean`             | `true`       | Enable color configuration                             |\r\n| `silent`           | `boolean`             | `false`      | Disable logging                                        |\r\n| `bindings`         | `Record<string, any>` | `{}`         | Global log metadata                                    |\r\n| `maxFileSize`      | `number`              | `10 MB`      | Startup rotation threshold                             |\r\n| `rotateOnStart`    | `boolean`             | `false`      | Rotate oversized files on startup                      |\r\n| `showPid`          | `boolean`             | `true`       | Include process ID                                     |\r\n| `showHostname`     | `boolean`             | `true`       | Include hostname                                       |\r\n| `redact`           | `string[]`            | `[]`         | Sensitive field patterns                               |\r\n| `events`           | `boolean`             | `true`       | Enable logger events                                   |\r\n| `maxListeners`     | `number`              | `100`        | Maximum EventEmitter listeners                         |\r\n| `bufferSize`       | `number`              | `10`         | Number of buffered file entries                        |\r\n| `batchInterval`    | `number`              | `1000`       | Batch interval in milliseconds                         |\r\n| `timeFormat`       | `string`              | `\"HH:mm:ss\"` | Pretty timestamp format                                |\r\n| `hideLevel`        | `boolean`             | `false`      | Hide level label in pretty mode                        |\r\n| `emitErrorOnLevel` | `LogLevel \\| false`   | `\"error\"`    | Emit error events at or above the configured threshold |\r\n| `middleware`       | `LoggerMiddleware[]`  | `[]`         | Initial middleware                                     |\r\n\r\n---\r\n\r\n## Structured Logging\r\n\r\nPass metadata directly to the logger:\r\n\r\n```ts\r\nLogger.info(\"User logged in\", {\r\n  userId: 123,\r\n  role: \"admin\",\r\n});\r\n```\r\n\r\nExample JSON output:\r\n\r\n```json\r\n{\r\n  \"level\": \"info\",\r\n  \"msg\": \"User logged in\",\r\n  \"pid\": 12345,\r\n  \"hostname\": \"server\",\r\n  \"userId\": 123,\r\n  \"role\": \"admin\",\r\n  \"time\": \"2026-08-18T12:00:00.000Z\"\r\n}\r\n```\r\n\r\nNested objects and arrays are supported:\r\n\r\n```ts\r\nLogger.info(\"Request completed\", {\r\n  request: {\r\n    method: \"GET\",\r\n    path: \"/api/users\",\r\n  },\r\n  response: {\r\n    statusCode: 200,\r\n  },\r\n});\r\n```\r\n\r\n---\r\n\r\n## Pretty Logging\r\n\r\nEnable human-readable output:\r\n\r\n```ts\r\nLogger.configure({\r\n  pretty: true,\r\n});\r\n```\r\n\r\nExample:\r\n\r\n```text\r\n[14:32:10 UTC] INFO: User logged in\r\n    userId: 123\r\n    role: \"admin\"\r\n```\r\n\r\nObjects are formatted as JSON:\r\n\r\n```text\r\n    user: {\r\n      \"id\": 123,\r\n      \"role\": \"admin\"\r\n    }\r\n```\r\n\r\n---\r\n\r\n## Colors\r\n\r\nMokat uses `chalk` for log-level colors.\r\n\r\nThe configured level colors are:\r\n\r\n| Level   | Color          |\r\n| ------- | -------------- |\r\n| `fatal` | Background red |\r\n| `error` | Red            |\r\n| `warn`  | Yellow         |\r\n| `info`  | Cyan           |\r\n| `debug` | Gray           |\r\n| `trace` | Dim            |\r\n\r\nExample:\r\n\r\n```ts\r\nLogger.configure({\r\n  pretty: true,\r\n  colors: true,\r\n});\r\n```\r\n\r\n---\r\n\r\n## Hide Level\r\n\r\nIn pretty mode, the level label can be hidden:\r\n\r\n```ts\r\nLogger.configure({\r\n  pretty: true,\r\n  hideLevel: true,\r\n});\r\n```\r\n\r\nInstead of:\r\n\r\n```text\r\n[14:32:10 UTC] INFO: Application started\r\n```\r\n\r\nthe output becomes:\r\n\r\n```text\r\n[14:32:10 UTC]  Application started\r\n```\r\n\r\n---\r\n\r\n## Timestamps\r\n\r\nTimestamps are enabled by default:\r\n\r\n```ts\r\nLogger.configure({\r\n  timestamp: true,\r\n});\r\n```\r\n\r\nDisable timestamps:\r\n\r\n```ts\r\nLogger.configure({\r\n  timestamp: false,\r\n});\r\n```\r\n\r\nJSON logs use an ISO timestamp in the `time` field.\r\n\r\nPretty logs use the configured `timeFormat`.\r\n\r\n---\r\n\r\n## Time Format\r\n\r\nThe default pretty timestamp format is:\r\n\r\n```text\r\nHH:mm:ss\r\n```\r\n\r\nConfigure it with:\r\n\r\n```ts\r\nLogger.configure({\r\n  timeFormat: \"HH:mm:ss\",\r\n});\r\n```\r\n\r\nThe implementation supports the following replacements:\r\n\r\n| Token | Description       |\r\n| ----- | ----------------- |\r\n| `HH`  | Two-digit hours   |\r\n| `mm`  | Two-digit minutes |\r\n| `ss`  | Two-digit seconds |\r\n| `SSS` | Milliseconds      |\r\n| `H`   | Hours             |\r\n| `m`   | Minutes           |\r\n| `s`   | Seconds           |\r\n\r\nFor example:\r\n\r\n```ts\r\nLogger.configure({\r\n  pretty: true,\r\n  timeFormat: \"HH:mm:ss.SSS\",\r\n});\r\n```\r\n\r\n---\r\n\r\n## File Logging\r\n\r\nWrite logs to a file:\r\n\r\n```ts\r\nLogger.configure({\r\n  file: \"./logs/app.log\",\r\n});\r\n```\r\n\r\nMokat automatically creates the parent directory if it does not exist.\r\n\r\nExample:\r\n\r\n```text\r\nlogs/\r\n└── app.log\r\n```\r\n\r\nLogs are appended to the file and buffered before being written.\r\n\r\n---\r\n\r\n## Log Buffering\r\n\r\nConfigure the file buffer:\r\n\r\n```ts\r\nLogger.configure({\r\n  file: \"./logs/app.log\",\r\n  bufferSize: 20,\r\n  batchInterval: 2000,\r\n});\r\n```\r\n\r\nIn this example:\r\n\r\n- Up to 20 entries can be buffered.\r\n- The buffer is flushed every 2000ms.\r\n- The buffer is flushed immediately when it reaches `bufferSize`.\r\n- Multiple entries are written together.\r\n\r\nManually flush the buffer:\r\n\r\n```ts\r\nLogger.flush();\r\n```\r\n\r\n---\r\n\r\n## File Rotation\r\n\r\nMokat supports startup file rotation:\r\n\r\n```ts\r\nLogger.configure({\r\n  file: \"./logs/app.log\",\r\n  maxFileSize: 10 * 1024 * 1024,\r\n  rotateOnStart: true,\r\n});\r\n```\r\n\r\nIf the existing file is larger than `maxFileSize`, it is renamed to a timestamped backup.\r\n\r\nExample:\r\n\r\n```text\r\nlogs/\r\n├── app.log\r\n└── app.log.1723728000000.bak\r\n```\r\n\r\nListen for rotation:\r\n\r\n```ts\r\nLogger.onRotate(({ oldFile, newFile }) => {\r\n  console.log(\"Old file:\", oldFile);\r\n  console.log(\"New file:\", newFile);\r\n});\r\n```\r\n\r\n> File rotation currently occurs only during logger startup. Runtime file-size monitoring is not implemented.\r\n\r\n---\r\n\r\n## Redacting Sensitive Data\r\n\r\nProtect sensitive information with regular-expression patterns:\r\n\r\n```ts\r\nLogger.configure({\r\n  redact: [\"password\", \"token\", \"secret\", \"authorization\"],\r\n});\r\n\r\nLogger.info(\"Login request\", {\r\n  username: \"john\",\r\n  password: \"secret123\",\r\n  token: \"abc123\",\r\n});\r\n```\r\n\r\nSensitive matching values are replaced with:\r\n\r\n```text\r\n[REDACTED]\r\n```\r\n\r\nNested objects are recursively processed:\r\n\r\n```ts\r\nLogger.info(\"User data\", {\r\n  user: {\r\n    id: 123,\r\n    password: \"secret123\",\r\n  },\r\n});\r\n```\r\n\r\nThe sensitive value becomes:\r\n\r\n```json\r\n{\r\n  \"user\": {\r\n    \"id\": 123,\r\n    \"password\": \"[REDACTED]\"\r\n  }\r\n}\r\n```\r\n\r\nRedaction patterns are converted into case-insensitive global regular expressions.\r\n\r\n---\r\n\r\n## Global Bindings\r\n\r\nAdd metadata to every log:\r\n\r\n```ts\r\nLogger.configure({\r\n  bindings: {\r\n    service: \"api\",\r\n    environment: \"production\",\r\n    version: \"1.0.0\",\r\n  },\r\n});\r\n```\r\n\r\nMokat also adds `pid` and `hostname` by default.\r\n\r\nDisable them when required:\r\n\r\n```ts\r\nLogger.configure({\r\n  showPid: false,\r\n  showHostname: false,\r\n});\r\n```\r\n\r\n---\r\n\r\n## Child Logger\r\n\r\nCreate a logger with additional bindings:\r\n\r\n```ts\r\nconst userLogger = Logger.child({\r\n  module: \"users\",\r\n});\r\n\r\nuserLogger.info(\"User created\");\r\n```\r\n\r\nParent bindings are preserved:\r\n\r\n```ts\r\nLogger.configure({\r\n  bindings: {\r\n    service: \"api\",\r\n  },\r\n});\r\n\r\nconst userLogger = Logger.child({\r\n  module: \"users\",\r\n});\r\n```\r\n\r\nThe child logger contains:\r\n\r\n```json\r\n{\r\n  \"service\": \"api\",\r\n  \"module\": \"users\"\r\n}\r\n```\r\n\r\nChild bindings can override existing bindings.\r\n\r\nChild loggers also inherit the parent's middleware.\r\n\r\n---\r\n\r\n## Context\r\n\r\nMokat provides a simple context stack:\r\n\r\n```ts\r\nLogger.pushContext(\"api\");\r\nLogger.pushContext(\"users\");\r\n\r\nLogger.info(\"Creating user\");\r\n```\r\n\r\nThe active context becomes:\r\n\r\n```text\r\napi>users\r\n```\r\n\r\nThe context is stored as the `context` binding.\r\n\r\nRemove the latest context:\r\n\r\n```ts\r\nconst context = Logger.popContext();\r\n\r\nconsole.log(context);\r\n```\r\n\r\nGet the current context:\r\n\r\n```ts\r\nconsole.log(Logger.getContext());\r\n```\r\n\r\n---\r\n\r\n## Automatic Context Management\r\n\r\nUse `withContext()` for automatic cleanup:\r\n\r\n```ts\r\nawait Logger.withContext(\"database\", async () => {\r\n  Logger.info(\"Connecting to database\");\r\n\r\n  await connectToDatabase();\r\n\r\n  Logger.info(\"Database connected\");\r\n});\r\n```\r\n\r\nNested contexts are supported:\r\n\r\n```ts\r\nawait Logger.withContext(\"api\", async () => {\r\n  await Logger.withContext(\"users\", async () => {\r\n    Logger.info(\"Creating user\");\r\n  });\r\n});\r\n```\r\n\r\nDuring the inner callback:\r\n\r\n```text\r\napi>users\r\n```\r\n\r\nAfter the callback completes, the previous context is restored.\r\n\r\nIf the callback throws, the context is also removed before the error is propagated.\r\n\r\n---\r\n\r\n## Session ID\r\n\r\nEach logger instance receives a generated session ID:\r\n\r\n```ts\r\nconsole.log(Logger.getSessionId());\r\n```\r\n\r\nExample:\r\n\r\n```text\r\nm8abc123-x7k2p\r\n```\r\n\r\nThe session ID identifies the logger instance during its lifetime.\r\n\r\n---\r\n\r\n## Middleware\r\n\r\nMokat supports logging middleware.\r\n\r\nA middleware receives the current `LogEvent` and a `next()` function:\r\n\r\n```ts\r\nLogger.use((event, next) => {\r\n  console.log(\"Middleware:\", event.level, event.msg);\r\n\r\n  next(event);\r\n});\r\n```\r\n\r\nMiddleware can modify the event before it continues:\r\n\r\n```ts\r\nLogger.use((event, next) => {\r\n  event.msg = `[API] ${event.msg}`;\r\n\r\n  next(event);\r\n});\r\n```\r\n\r\nMultiple middleware functions are executed in registration order.\r\n\r\n```ts\r\nLogger.use((event, next) => {\r\n  console.log(\"First middleware\");\r\n  next(event);\r\n});\r\n\r\nLogger.use((event, next) => {\r\n  console.log(\"Second middleware\");\r\n  next(event);\r\n});\r\n```\r\n\r\nIf a middleware throws an error, Mokat emits an `error` event and continues the logging operation.\r\n\r\n---\r\n\r\n## Built-in Middleware\r\n\r\nMokat provides several middleware helpers.\r\n\r\n```ts\r\nimport { middleware } from \"@aydope/mokat\";\r\n```\r\n\r\nAvailable middleware:\r\n\r\n- `filterLevel()`\r\n- `addTimestamp`\r\n- `redactData()`\r\n- `consoleLog`\r\n\r\n---\r\n\r\n### `filterLevel()`\r\n\r\nFilter events by minimum level:\r\n\r\n```ts\r\nLogger.use(middleware.filterLevel(\"warn\"));\r\n```\r\n\r\nOnly `warn`, `error`, and `fatal` events continue through this middleware.\r\n\r\n---\r\n\r\n### `addTimestamp`\r\n\r\nAdd an ISO timestamp to the event metadata:\r\n\r\n```ts\r\nLogger.use(middleware.addTimestamp);\r\n```\r\n\r\n---\r\n\r\n### `redactData()`\r\n\r\nRedact matching metadata values:\r\n\r\n```ts\r\nLogger.use(middleware.redactData([\"password\", \"token\", \"secret\"]));\r\n```\r\n\r\n---\r\n\r\n### `consoleLog`\r\n\r\nPrint middleware events to the console:\r\n\r\n```ts\r\nLogger.use(middleware.consoleLog);\r\n```\r\n\r\n---\r\n\r\n## Events\r\n\r\nMokat's logger extends Node.js `EventEmitter`.\r\n\r\nEvents are enabled by default:\r\n\r\n```ts\r\nLogger.configure({\r\n  events: true,\r\n});\r\n```\r\n\r\nDisable logger events:\r\n\r\n```ts\r\nLogger.configure({\r\n  events: false,\r\n});\r\n```\r\n\r\nAvailable events include:\r\n\r\n| Event         | Payload                  | Description                                   |\r\n| ------------- | ------------------------ | --------------------------------------------- |\r\n| `log`         | `LogEvent`               | Emitted for processed log entries             |\r\n| `error`       | `Error`                  | Logger errors and configured log-level errors |\r\n| `rotate`      | `{ oldFile, newFile }`   | Emitted after startup rotation                |\r\n| `levelChange` | `{ oldLevel, newLevel }` | Emitted after changing log level              |\r\n| `batch`       | `{ count, content }`     | Emitted after a buffer flush                  |\r\n| `flush`       | None                     | Emitted when `flush()` is called              |\r\n| `close`       | None                     | Emitted when the logger is closed             |\r\n\r\n---\r\n\r\n## Log Events\r\n\r\n```ts\r\nLogger.onLog((event) => {\r\n  console.log(\"Level:\", event.level);\r\n  console.log(\"Message:\", event.msg);\r\n  console.log(\"Metadata:\", event.meta);\r\n  console.log(\"Formatted:\", event.formatted);\r\n  console.log(\"Timestamp:\", event.timestamp);\r\n});\r\n```\r\n\r\nEvent structure:\r\n\r\n```ts\r\ntype LogEvent = {\r\n  level: LogLevel;\r\n  msg: string;\r\n  meta?: Record<string, any>;\r\n  formatted: string;\r\n  timestamp: Date;\r\n};\r\n```\r\n\r\n---\r\n\r\n## Error Events\r\n\r\n```ts\r\nLogger.onError((error) => {\r\n  console.error(\"Logger error:\", error);\r\n});\r\n```\r\n\r\nBy default:\r\n\r\n```ts\r\nemitErrorOnLevel: \"error\";\r\n```\r\n\r\nWith this configuration, `error` and `fatal` logs emit an `error` event.\r\n\r\nChange the threshold:\r\n\r\n```ts\r\nLogger.configure({\r\n  emitErrorOnLevel: \"warn\",\r\n});\r\n```\r\n\r\nWith `warn`, the logger can emit an error event for:\r\n\r\n- `warn`\r\n- `error`\r\n- `fatal`\r\n\r\nDisable automatic error-event emission:\r\n\r\n```ts\r\nLogger.configure({\r\n  emitErrorOnLevel: false,\r\n});\r\n```\r\n\r\n---\r\n\r\n## Level Change Events\r\n\r\nChange the log level:\r\n\r\n```ts\r\nLogger.setLevel(\"debug\");\r\n```\r\n\r\nListen for changes:\r\n\r\n```ts\r\nLogger.onLevelChange(({ oldLevel, newLevel }) => {\r\n  console.log(`${oldLevel} -> ${newLevel}`);\r\n});\r\n```\r\n\r\n---\r\n\r\n## Rotation Events\r\n\r\n```ts\r\nLogger.onRotate(({ oldFile, newFile }) => {\r\n  console.log(\"Rotated:\", oldFile);\r\n  console.log(\"New file:\", newFile);\r\n});\r\n```\r\n\r\n---\r\n\r\n## One-Time Log Events\r\n\r\n```ts\r\nLogger.onceLog((event) => {\r\n  console.log(\"First log:\", event.msg);\r\n});\r\n```\r\n\r\nThe listener is automatically removed after the first `log` event.\r\n\r\n---\r\n\r\n## Event Listeners\r\n\r\nRemove all listeners:\r\n\r\n```ts\r\nLogger.clearListeners();\r\n```\r\n\r\nConfigure the maximum number of listeners:\r\n\r\n```ts\r\nLogger.configure({\r\n  maxListeners: 200,\r\n});\r\n```\r\n\r\n---\r\n\r\n## Profiling\r\n\r\nStart a manual performance profile:\r\n\r\n```ts\r\nconst end = Logger.profile(\"database query\");\r\n\r\nawait database.query();\r\n\r\nend();\r\n```\r\n\r\nThe completion log contains duration metadata:\r\n\r\n```json\r\n{\r\n  \"duration\": \"153ms\"\r\n}\r\n```\r\n\r\nThe generated message is:\r\n\r\n```text\r\nProfile: database query\r\n```\r\n\r\n---\r\n\r\n## Automatic Timing\r\n\r\nUse `withTimer()` for synchronous or asynchronous operations.\r\n\r\n### Async\r\n\r\n```ts\r\nawait Logger.withTimer(\"fetch users\", async () => {\r\n  await fetchUsers();\r\n});\r\n```\r\n\r\n### Sync\r\n\r\n```ts\r\nconst result = Logger.withTimer(\"calculate total\", () => {\r\n  return calculateTotal();\r\n});\r\n```\r\n\r\nIf an asynchronous operation rejects, the timer is still completed before the rejection propagates.\r\n\r\n---\r\n\r\n## Function Wrapper\r\n\r\nWrap functions to automatically measure execution time:\r\n\r\n```ts\r\nconst wrappedFetch = Logger.wrap(async (userId: string) => {\r\n  return fetchUser(userId);\r\n});\r\n\r\nconst user = await wrappedFetch(\"123\");\r\n```\r\n\r\nSuccessful execution produces a debug message:\r\n\r\n```text\r\nWrapped call completed\r\n```\r\n\r\nFailed execution produces an error message:\r\n\r\n```text\r\nWrapped call failed\r\n```\r\n\r\nThe original error is re-thrown.\r\n\r\n---\r\n\r\n## Assertions\r\n\r\nUse `assert()` for lightweight runtime checks:\r\n\r\n```ts\r\nLogger.assert(user !== null, \"User must exist\", {\r\n  userId,\r\n});\r\n```\r\n\r\nIf the condition is `true`, nothing happens.\r\n\r\nIf the condition is `false`, an error log is generated:\r\n\r\n```text\r\nAssertion failed: User must exist\r\n```\r\n\r\n---\r\n\r\n## Timeout\r\n\r\nMokat provides a Promise-based timeout utility:\r\n\r\n```ts\r\nimport { timeOut } from \"@aydope/mokat\";\r\n\r\nawait timeOut(2);\r\n\r\nconsole.log(\"2 seconds passed\");\r\n```\r\n\r\nThe duration is specified in seconds.\r\n\r\nBy default, the Promise resolves after the specified duration.\r\n\r\n---\r\n\r\n## Sleep\r\n\r\n`sleep()` is a convenience wrapper around `timeOut()`:\r\n\r\n```ts\r\nimport { sleep } from \"@aydope/mokat\";\r\n\r\nawait sleep(2);\r\n\r\nconsole.log(\"2 seconds passed\");\r\n```\r\n\r\n---\r\n\r\n## Reject on Timeout\r\n\r\nSet `shouldReject` to `true`:\r\n\r\n```ts\r\nawait timeOut(5, {\r\n  shouldReject: true,\r\n});\r\n```\r\n\r\nThe default error message is:\r\n\r\n```text\r\nTimeout rejected\r\n```\r\n\r\n---\r\n\r\n## Custom Timeout Error\r\n\r\n```ts\r\nawait timeOut(5, {\r\n  shouldReject: true,\r\n  errorMessage: \"Request timed out\",\r\n});\r\n```\r\n\r\nThe Promise rejects with:\r\n\r\n```text\r\nRequest timed out\r\n```\r\n\r\n---\r\n\r\n## `waitFor()`\r\n\r\nWait until a synchronous or asynchronous condition becomes true:\r\n\r\n```ts\r\nimport { waitFor } from \"@aydope/mokat\";\r\n\r\nawait waitFor(() => {\r\n  return server.isReady;\r\n});\r\n```\r\n\r\nThe default timeout is `30000ms`.\r\n\r\nThe default polling interval is `100ms`.\r\n\r\n---\r\n\r\n## Custom `waitFor()` Options\r\n\r\n```ts\r\nawait waitFor(\r\n  async () => {\r\n    return await checkDatabase();\r\n  },\r\n  {\r\n    timeout: 10000,\r\n    interval: 250,\r\n  },\r\n);\r\n```\r\n\r\nOptions:\r\n\r\n```ts\r\n{\r\n  timeout?: number;\r\n  interval?: number;\r\n}\r\n```\r\n\r\nBoth values are expressed in milliseconds.\r\n\r\nIf the condition does not become true before the timeout, `waitFor()` throws:\r\n\r\n```text\r\nError: waitFor timeout after 10000ms\r\n```\r\n\r\n---\r\n\r\n## Promise Race\r\n\r\n`timeOut()` can be combined with `Promise.race()`:\r\n\r\n```ts\r\nconst result = await Promise.race([\r\n  fetchData(),\r\n\r\n  timeOut(5, {\r\n    shouldReject: true,\r\n    errorMessage: \"Fetching data timed out\",\r\n  }),\r\n]);\r\n```\r\n\r\nThis is useful for enforcing operation time limits.\r\n\r\n---\r\n\r\n## Runtime Log Level\r\n\r\nChange the log level while the application is running:\r\n\r\n```ts\r\nLogger.configure({\r\n  level: \"info\",\r\n});\r\n\r\nLogger.debug(\"This is hidden\");\r\n\r\nLogger.setLevel(\"debug\");\r\n\r\nLogger.debug(\"This is now visible\");\r\n```\r\n\r\nA `levelChange` event is emitted when the level changes.\r\n\r\n---\r\n\r\n## Statistics\r\n\r\nGet logger statistics:\r\n\r\n```ts\r\nconsole.log(Logger.stats());\r\n```\r\n\r\nExample:\r\n\r\n```ts\r\n{\r\n  logCount: 42,\r\n  errorCount: 3,\r\n  uptime: 12.531\r\n}\r\n```\r\n\r\n### Statistics\r\n\r\n| Property     | Description                              |\r\n| ------------ | ---------------------------------------- |\r\n| `logCount`   | Number of emitted `log` events           |\r\n| `errorCount` | Number of `error` and `fatal` log events |\r\n| `uptime`     | Logger uptime in seconds                 |\r\n\r\nGet uptime directly:\r\n\r\n```ts\r\nconsole.log(Logger.uptime());\r\n```\r\n\r\n---\r\n\r\n## Silent Mode\r\n\r\nDisable logging:\r\n\r\n```ts\r\nLogger.configure({\r\n  silent: true,\r\n});\r\n```\r\n\r\nWhen enabled, log calls return without producing console or file output.\r\n\r\n---\r\n\r\n## Graceful Shutdown\r\n\r\nClose the logger when the application shuts down:\r\n\r\n```ts\r\nprocess.on(\"SIGTERM\", () => {\r\n  Logger.info(\"Shutting down\");\r\n\r\n  Logger.close();\r\n\r\n  process.exit(0);\r\n});\r\n```\r\n\r\nHandle `SIGINT` as well:\r\n\r\n```ts\r\nprocess.on(\"SIGINT\", () => {\r\n  Logger.close();\r\n\r\n  process.exit(0);\r\n});\r\n```\r\n\r\nCalling `close()`:\r\n\r\n- Stops the batching interval.\r\n- Prevents further logging.\r\n- Flushes the current buffer.\r\n- Closes the file stream.\r\n- Emits the `close` event.\r\n- Removes all listeners.\r\n\r\nCalling `close()` multiple times is safe.\r\n\r\n---\r\n\r\n## TypeScript\r\n\r\nMokat is written with TypeScript support in mind.\r\n\r\nTypes can be imported directly:\r\n\r\n```ts\r\nimport type {\r\n  LogLevel,\r\n  LoggerConfig,\r\n  LogEvent,\r\n  LoggerMiddleware,\r\n  TimeOutOptions,\r\n} from \"@aydope/mokat\";\r\n```\r\n\r\n### `LogLevel`\r\n\r\n```ts\r\ntype LogLevel = \"fatal\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"trace\";\r\n```\r\n\r\n### `TimeOutOptions`\r\n\r\n```ts\r\ntype TimeOutOptions = {\r\n  errorMessage?: string;\r\n  shouldReject?: boolean;\r\n};\r\n```\r\n\r\n### `LoggerConfig`\r\n\r\n```ts\r\ntype LoggerConfig = {\r\n  level?: LogLevel;\r\n  pretty?: boolean;\r\n  timestamp?: boolean;\r\n  file?: string;\r\n  colors?: boolean;\r\n  silent?: boolean;\r\n  bindings?: Record<string, any>;\r\n  maxFileSize?: number;\r\n  rotateOnStart?: boolean;\r\n  showPid?: boolean;\r\n  showHostname?: boolean;\r\n  redact?: string[];\r\n  events?: boolean;\r\n  maxListeners?: number;\r\n  bufferSize?: number;\r\n  batchInterval?: number;\r\n  timeFormat?: string;\r\n  hideLevel?: boolean;\r\n  emitErrorOnLevel?: LogLevel | false;\r\n  middleware?: LoggerMiddleware[];\r\n};\r\n```\r\n\r\n### `LogEvent`\r\n\r\n```ts\r\ntype LogEvent = {\r\n  level: LogLevel;\r\n  msg: string;\r\n  meta?: Record<string, any>;\r\n  formatted: string;\r\n  timestamp: Date;\r\n};\r\n```\r\n\r\n### `LoggerMiddleware`\r\n\r\n```ts\r\ntype LoggerMiddleware = (\r\n  event: LogEvent,\r\n  next: (event: LogEvent) => void,\r\n) => void;\r\n```\r\n\r\n---\r\n\r\n## API Reference\r\n\r\n### Logging\r\n\r\n```ts\r\nLogger.fatal(msg, meta?)\r\nLogger.error(msg, meta?)\r\nLogger.warn(msg, meta?)\r\nLogger.info(msg, meta?)\r\nLogger.debug(msg, meta?)\r\nLogger.trace(msg, meta?)\r\n```\r\n\r\n### Logger Management\r\n\r\n```ts\r\nLogger.configure(config);\r\nLogger.use(middleware);\r\nLogger.child(bindings);\r\nLogger.setLevel(level);\r\nLogger.flush();\r\nLogger.close();\r\nLogger.clearListeners();\r\n```\r\n\r\n### Context\r\n\r\n```ts\r\nLogger.pushContext(context);\r\nLogger.popContext();\r\nLogger.getContext();\r\nLogger.withContext(context, fn);\r\n```\r\n\r\n### Session\r\n\r\n```ts\r\nLogger.getSessionId();\r\n```\r\n\r\n### Performance\r\n\r\n```ts\r\nLogger.profile(label);\r\nLogger.withTimer(label, fn);\r\nLogger.wrap(fn);\r\n```\r\n\r\n### Statistics\r\n\r\n```ts\r\nLogger.stats();\r\nLogger.uptime();\r\n```\r\n\r\n### Assertions\r\n\r\n```ts\r\nLogger.assert(condition, msg, meta?)\r\n```\r\n\r\n### Events\r\n\r\n```ts\r\nLogger.onLog(callback);\r\nLogger.onError(callback);\r\nLogger.onRotate(callback);\r\nLogger.onLevelChange(callback);\r\nLogger.onceLog(callback);\r\n```\r\n\r\n### Timeout Utilities\r\n\r\n```ts\r\ntimeOut(seconds, options?)\r\nsleep(seconds)\r\nwaitFor(condition, options?)\r\n```\r\n\r\n---\r\n\r\n## Production Configuration\r\n\r\nA production-oriented configuration:\r\n\r\n```ts\r\nimport { Logger } from \"@aydope/mokat\";\r\n\r\nLogger.configure({\r\n  level: \"info\",\r\n\r\n  pretty: false,\r\n  timestamp: true,\r\n\r\n  file: \"./logs/app.log\",\r\n\r\n  showPid: true,\r\n  showHostname: true,\r\n\r\n  bufferSize: 50,\r\n  batchInterval: 1000,\r\n\r\n  maxFileSize: 10 * 1024 * 1024,\r\n  rotateOnStart: true,\r\n\r\n  redact: [\"password\", \"token\", \"authorization\", \"secret\", \"apiKey\"],\r\n\r\n  bindings: {\r\n    service: \"my-api\",\r\n    environment: \"production\",\r\n  },\r\n});\r\n\r\nLogger.info(\"Application started\");\r\n```\r\n\r\n---\r\n\r\n## Development Configuration\r\n\r\nFor local development:\r\n\r\n```ts\r\nimport { Logger } from \"@aydope/mokat\";\r\n\r\nLogger.configure({\r\n  level: \"debug\",\r\n  pretty: true,\r\n  colors: true,\r\n  timestamp: true,\r\n  showPid: true,\r\n  showHostname: true,\r\n});\r\n\r\nLogger.debug(\"Development logger initialized\");\r\nLogger.info(\"Application started\");\r\n```\r\n\r\n---\r\n\r\n## Complete Example\r\n\r\n```ts\r\nimport { Logger, timeOut, waitFor, middleware } from \"@aydope/mokat\";\r\n\r\nLogger.configure({\r\n  level: \"debug\",\r\n\r\n  pretty: true,\r\n\r\n  file: \"./logs/app.log\",\r\n\r\n  bufferSize: 20,\r\n  batchInterval: 1000,\r\n\r\n  redact: [\"password\", \"token\", \"secret\"],\r\n\r\n  bindings: {\r\n    service: \"my-api\",\r\n    environment: \"production\",\r\n  },\r\n});\r\n\r\nLogger.use(middleware.addTimestamp);\r\n\r\nLogger.onLog((event) => {\r\n  console.log(\"LOG EVENT:\", event.level, event.msg);\r\n});\r\n\r\nLogger.onError((error) => {\r\n  console.error(\"LOGGER ERROR:\", error.message);\r\n});\r\n\r\nLogger.info(\"Application started\");\r\n\r\nawait Logger.withContext(\"request\", async () => {\r\n  Logger.info(\"Request started\");\r\n\r\n  await Logger.withTimer(\"Database query\", async () => {\r\n    await timeOut(0.1);\r\n  });\r\n\r\n  await waitFor(async () => true, {\r\n    timeout: 5000,\r\n    interval: 100,\r\n  });\r\n\r\n  Logger.info(\"Request completed\");\r\n});\r\n\r\nLogger.assert(true, \"Application should be healthy\");\r\n\r\nconsole.log(\"Stats:\", Logger.stats());\r\n\r\nLogger.close();\r\n```\r\n\r\n---\r\n\r\n## Default Configuration\r\n\r\nWhen Mokat is imported, a default logger instance is created internally.\r\n\r\nThe effective defaults are:\r\n\r\n```ts\r\n{\r\n  level: \"info\",\r\n  pretty: true,\r\n  timestamp: true,\r\n  file: \"\",\r\n  colors: true,\r\n  silent: false,\r\n\r\n  maxFileSize: 10 * 1024 * 1024,\r\n  rotateOnStart: false,\r\n\r\n  showPid: true,\r\n  showHostname: true,\r\n\r\n  redact: [],\r\n\r\n  events: true,\r\n  maxListeners: 100,\r\n\r\n  bufferSize: 10,\r\n  batchInterval: 1000,\r\n\r\n  timeFormat: \"HH:mm:ss\",\r\n\r\n  hideLevel: false,\r\n\r\n  emitErrorOnLevel: \"error\",\r\n\r\n  middleware: [],\r\n}\r\n```\r\n\r\n---\r\n\r\n## Package Exports\r\n\r\nMokat provides named exports:\r\n\r\n```ts\r\nimport { Logger, timeOut, sleep, waitFor, types, middleware } from \"@aydope/mokat\";\r\n```\r\n\r\nIt also provides a default `Mokat` object:\r\n\r\n```ts\r\nimport Mokat from \"@aydope/mokat\";\r\n\r\nMokat.Logger.info(\"Application started\");\r\n```\r\n\r\nThe default object exposes:\r\n\r\n```ts\r\nMokat.Logger;\r\nMokat.timeOut;\r\nMokat.sleep;\r\nMokat.waitFor;\r\nMokat.middleware;\r\n```\r\n\r\nThe `types` module is available through the named export:\r\n\r\n```ts\r\nimport { types } from \"@aydope/mokat\";\r\n```\r\n\r\n---\r\n\r\n## ESM\r\n\r\nModern Node.js applications can use ESM imports:\r\n\r\n```ts\r\nimport Mokat, { Logger, sleep, waitFor } from \"@aydope/mokat\";\r\n```\r\n\r\n---\r\n\r\n## CommonJS\r\n\r\nMokat also exposes CommonJS-compatible exports:\r\n\r\n```js\r\nconst Mokat = require(\"@aydope/mokat\");\r\n\r\nMokat.Logger.info(\"Application started\");\r\n```\r\n\r\nIndividual exports can also be accessed:\r\n\r\n```js\r\nconst { Logger, timeOut, sleep, waitFor, middleware } = require(\"@aydope/mokat\");\r\n```\r\n\r\n---\r\n\r\n## Recommended Project Structure\r\n\r\n```text\r\nmy-app/\r\n├── src/\r\n│   ├── index.ts\r\n│   ├── logger.ts\r\n│   ├── services/\r\n│   ├── controllers/\r\n│   └── routes/\r\n├── logs/\r\n│   └── app.log\r\n├── package.json\r\n├── tsconfig.json\r\n└── README.md\r\n```\r\n\r\nExample `logger.ts`:\r\n\r\n```ts\r\nimport { Logger } from \"@aydope/mokat\";\r\n\r\nLogger.configure({\r\n  level: process.env.NODE_ENV === \"production\" ? \"info\" : \"debug\",\r\n\r\n  pretty: process.env.NODE_ENV !== \"production\",\r\n\r\n  file: \"./logs/app.log\",\r\n\r\n  redact: [\"password\", \"token\", \"secret\", \"authorization\"],\r\n\r\n  bindings: {\r\n    service: \"my-app\",\r\n  },\r\n});\r\n```\r\n\r\nUse it anywhere:\r\n\r\n```ts\r\nimport { Logger } from \"./logger\";\r\n\r\nLogger.info(\"Service started\");\r\n```\r\n\r\n---\r\n\r\n## Example: HTTP Request Logging\r\n\r\n```ts\r\nawait Logger.withContext(\"http\", async () => {\r\n  const end = Logger.profile(\"GET /users\");\r\n\r\n  try {\r\n    Logger.info(\"Request started\", {\r\n      method: \"GET\",\r\n      path: \"/users\",\r\n    });\r\n\r\n    const users = await getUsers();\r\n\r\n    Logger.info(\"Request completed\", {\r\n      statusCode: 200,\r\n      count: users.length,\r\n    });\r\n\r\n    return users;\r\n  } catch (error) {\r\n    Logger.error(\"Request failed\", {\r\n      error,\r\n    });\r\n\r\n    throw error;\r\n  } finally {\r\n    end();\r\n  }\r\n});\r\n```\r\n\r\n---\r\n\r\n## Example: Service-Specific Logger\r\n\r\n```ts\r\nconst databaseLogger = Logger.child({\r\n  service: \"database\",\r\n});\r\n\r\nconst cacheLogger = Logger.child({\r\n  service: \"cache\",\r\n});\r\n\r\ndatabaseLogger.info(\"Database connected\");\r\n\r\ncacheLogger.info(\"Cache initialized\");\r\n```\r\n\r\nThis keeps service-specific metadata attached automatically.\r\n\r\n---\r\n\r\n## Example: Sensitive Data Protection\r\n\r\n```ts\r\nLogger.configure({\r\n  redact: [\"password\", \"token\", \"authorization\", \"apiKey\", \"secret\"],\r\n});\r\n\r\nLogger.info(\"Authentication request\", {\r\n  username: \"john\",\r\n  password: \"my-password\",\r\n  token: \"secret-token\",\r\n});\r\n```\r\n\r\nSensitive values are replaced with:\r\n\r\n```text\r\n[REDACTED]\r\n```\r\n\r\n---\r\n\r\n## Example: Waiting for a Service\r\n\r\n```ts\r\nawait waitFor(\r\n  async () => {\r\n    return await healthCheck();\r\n  },\r\n  {\r\n    timeout: 30000,\r\n    interval: 500,\r\n  },\r\n);\r\n\r\nLogger.info(\"Service is ready\");\r\n```\r\n\r\n---\r\n\r\n## Example: Performance Monitoring\r\n\r\n```ts\r\nawait Logger.withTimer(\"process payment\", async () => {\r\n  await processPayment();\r\n});\r\n```\r\n\r\nFor reusable functions:\r\n\r\n```ts\r\nconst processUser = Logger.wrap(async (userId: string) => {\r\n  return processUserData(userId);\r\n});\r\n\r\nawait processUser(\"user_123\");\r\n```\r\n\r\n---\r\n\r\n## Security Recommendations\r\n\r\nAvoid logging sensitive information whenever possible.\r\n\r\nDo not directly log:\r\n\r\n- Passwords\r\n- API keys\r\n- Access tokens\r\n- Refresh tokens\r\n- Authorization headers\r\n- Private keys\r\n- Session secrets\r\n- Database credentials\r\n\r\nUse redaction as an additional safety layer:\r\n\r\n```ts\r\nLogger.configure({\r\n  redact: [\"password\", \"token\", \"authorization\", \"secret\", \"apiKey\"],\r\n});\r\n```\r\n\r\nRedaction should be treated as a safety mechanism, not as a replacement for avoiding sensitive data in logs.\r\n\r\n---\r\n\r\n## Implementation Notes\r\n\r\n### `colors`\r\n\r\nThe `colors` option is exposed as part of the public configuration.\r\n\r\nThe current logger implementation selects Chalk color functions for level labels. The option is currently stored in the logger configuration but is not used as a conditional switch around the Chalk calls.\r\n\r\n---\r\n\r\n### File Rotation\r\n\r\n`maxFileSize` is checked when file logging is initialized and `rotateOnStart` is enabled.\r\n\r\nRuntime file-size monitoring and continuous automatic rotation are not implemented.\r\n\r\n---\r\n\r\n### Error Events\r\n\r\nThe `error` event is used for both:\r\n\r\n- Internal logger errors.\r\n- Configured log-level error events.\r\n\r\nBy default:\r\n\r\n```ts\r\nemitErrorOnLevel: \"error\";\r\n```\r\n\r\nThe logger emits an error event when the current level has a priority less than or equal to the configured threshold.\r\n\r\nTherefore, with the default configuration:\r\n\r\n- `error` emits an error event.\r\n- `fatal` emits an error event.\r\n\r\nIf configured as:\r\n\r\n```ts\r\nemitErrorOnLevel: \"warn\";\r\n```\r\n\r\nthen:\r\n\r\n- `warn`\r\n- `error`\r\n- `fatal`\r\n\r\ncan emit error events.\r\n\r\n---\r\n\r\n### Middleware Execution\r\n\r\nMiddleware is executed sequentially.\r\n\r\nEach middleware must call:\r\n\r\n```ts\r\nnext(event);\r\n```\r\n\r\nto continue the pipeline.\r\n\r\nA middleware can modify the event before passing it to the next middleware.\r\n\r\nIf middleware throws, Mokat emits an error and invokes the logging callback so the original logging operation can continue.\r\n\r\n---\r\n\r\n### Buffering\r\n\r\nThe file buffer is flushed when:\r\n\r\n- The buffer reaches `bufferSize`.\r\n- The batch interval executes.\r\n- `flush()` is called.\r\n- `close()` is called.\r\n\r\nWhen a batch is flushed, the `batch` event contains:\r\n\r\n```ts\r\n{\r\n  count: number;\r\n  content: string;\r\n}\r\n```\r\n\r\n---\r\n\r\n### Closing Behavior\r\n\r\nAfter:\r\n\r\n```ts\r\nLogger.close();\r\n```\r\n\r\nthe logger is considered closed.\r\n\r\nFurther logging calls are ignored.\r\n\r\nThe batching interval is stopped, the buffer is flushed, the file stream is ended, the `close` event is emitted, and all listeners are removed.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT\r\n\r\n---\r\n\r\n<div align=\"center\">\r\n\r\n# Mokat\r\n\r\n_Simple logging. Structured data. Better application tooling._\r\n\r\n</div>\r\n","readmeFilename":"README.md"}