{"_id":"@ahrowe/audit-log","_rev":"5-482c8e66bba383c4495133bbe660a438","name":"@ahrowe/audit-log","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.1":{"name":"@ahrowe/audit-log","version":"0.1.1","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"author":"AndreasWeinzierl","license":"MIT","_id":"@ahrowe/audit-log@0.1.1","maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"dist":{"shasum":"f40adf3167a3dbb771f006ffd4a7b57c1f8c2d64","tarball":"https://registry.npmjs.org/@ahrowe/audit-log/-/audit-log-0.1.1.tgz","fileCount":34,"integrity":"sha512-8fjm6S1g0HJbP7ERKZRocwAuYqKazD+NNBYZIriv5YjUIV/hzfmuhyKTmHIYgW1gBWXKFchUfjQ4OX3UrCg0Sw==","signatures":[{"sig":"MEUCIDs4qPdea7Amt6zgdBaSKZ3dkIONhKPAaEtvSv0OMl2nAiEAnXDBgH5OaYCIWXhaBZAzAUG1a94lVVYUW7eEOCFxUZg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32578},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./koa":{"types":"./dist/koa.d.ts","import":"./dist/koa.js"}},"scripts":{"lint":"eslint .","test":"vitest","build":"tsc","release":"bash scripts/release.sh","prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""},"_npmUser":{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"},"repository":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log","type":"git"},"description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"lodash":"^4.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"koa":"^3.2.1","eslint":"^10.5.0","vitest":"^4.1.8","globals":"^17.6.0","@eslint/js":"^10.0.1","@types/koa":"^3.0.3","typescript":"^6.0.3","@types/node":"^24.13.2","@types/lodash":"^4.17.24","typescript-eslint":"^8.61.0"},"peerDependencies":{"koa":"^3.2.1"},"peerDependenciesMeta":{"koa":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/audit-log_0.1.1_1782289369936_0.4017655079693925","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@ahrowe/audit-log","version":"0.1.2","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"author":"AndreasWeinzierl","license":"MIT","_id":"@ahrowe/audit-log@0.1.2","maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"dist":{"shasum":"83acfba7567a00abebf767ba0268a01b842ac3b0","tarball":"https://registry.npmjs.org/@ahrowe/audit-log/-/audit-log-0.1.2.tgz","fileCount":35,"integrity":"sha512-aGB7Ij7WXNZt9CgmT4IQrXjh9rExEwqvHlEHhu024mp4YUnf1yzR/8WGDBWiIIazC8BozL6u/7b6y5qQNVCw0Q==","signatures":[{"sig":"MEUCIGj8vL4sB4kSEycB/TodcrqC2jRNU3302rrGsAYfd/fxAiEAhx+qEmQRZG6ONHzR6WrFYWjgIoWLYyoB6XVe5qrcUdc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38754},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./koa":{"types":"./dist/koa.d.ts","import":"./dist/koa.js"}},"scripts":{"lint":"eslint .","test":"vitest","build":"tsc","release":"bash scripts/release.sh","prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""},"_npmUser":{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"},"repository":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log","type":"git"},"description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"lodash":"^4.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"koa":"^3.2.1","eslint":"^10.5.0","vitest":"^4.1.8","globals":"^17.6.0","@eslint/js":"^10.0.1","@types/koa":"^3.0.3","typescript":"^6.0.3","@types/node":"^24.13.2","@types/lodash":"^4.17.24","typescript-eslint":"^8.61.0"},"peerDependencies":{"koa":"^3.2.1"},"peerDependenciesMeta":{"koa":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/audit-log_0.1.2_1782289973908_0.1954164782711787","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ahrowe/audit-log","version":"0.2.0","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"author":{"name":"AndreasWeinzierl"},"license":"MIT","_id":"@ahrowe/audit-log@0.2.0","maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"dist":{"shasum":"17ff98d59a8cc90ad1e14dcffdf49cd7b6c1834e","tarball":"https://registry.npmjs.org/@ahrowe/audit-log/-/audit-log-0.2.0.tgz","fileCount":35,"integrity":"sha512-acdjK5991HhsfycKCyfywG/BdR8NDC1GK8KIEJHfsMiME6eRWWyZLYh4SGEt2gx8LuxBAUPQ9gyqPnAvqb1xsg==","signatures":[{"sig":"MEUCIEdXG3PvfGJEYjAgsi0hz68H/ShtQFWr4vGE5PY1NgauAiEA6CyQVhNk5c8XnDq5+O0oKvFwgIrvpZ+PB9vwvKhNmmQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41744},"main":"./dist/index.js","type":"module","_from":"file:ahrowe-audit-log-0.2.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./koa":{"types":"./dist/koa.d.ts","import":"./dist/koa.js"}},"scripts":{"lint":"eslint .","test":"vitest","build":"tsc","release":"bash scripts/release.sh","prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""},"_npmUser":{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"},"_resolved":"/tmp/6b7c45daa5cf511663ebd13eafec158f/ahrowe-audit-log-0.2.0.tgz","_integrity":"sha512-acdjK5991HhsfycKCyfywG/BdR8NDC1GK8KIEJHfsMiME6eRWWyZLYh4SGEt2gx8LuxBAUPQ9gyqPnAvqb1xsg==","repository":{"url":"git+https://github.com/AndreasWeinzierl/ahrowe-audit-log.git","type":"git"},"_npmVersion":"11.9.0","description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","dependencies":{"lodash":"^4.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"koa":"^3.2.1","eslint":"^10.5.0","vitest":"^4.1.8","globals":"^17.6.0","@eslint/js":"^10.0.1","@types/koa":"^3.0.3","typescript":"^6.0.3","@types/node":"^24.13.2","@types/lodash":"^4.17.24","typescript-eslint":"^8.61.0"},"peerDependencies":{"koa":"^3.2.1"},"peerDependenciesMeta":{"koa":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/audit-log_0.2.0_1782849688059_0.3648906567750012","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ahrowe/audit-log","version":"0.3.0","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"author":{"name":"AndreasWeinzierl"},"license":"MIT","_id":"@ahrowe/audit-log@0.3.0","maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"dist":{"shasum":"247d263fb73baf41a10bdda634c23d9ded68bd5f","tarball":"https://registry.npmjs.org/@ahrowe/audit-log/-/audit-log-0.3.0.tgz","fileCount":35,"integrity":"sha512-HSUrUVGmloHKzPlalP1icrDbgPLuABnUU1HY3c/5LNj3Sur5t/ep1SHgb1DB8uEKo5ZJbjBrcUG6BNhshLExMQ==","signatures":[{"sig":"MEUCIQDPzjfl7cvNoLgidJDEfZbCG5vdbX3ZRmrbCHdd5N5IHwIgLPTXrpHJqboR4nUvTRtl4jqAjcYte+KyXHmKmC+DlT4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45704},"main":"./dist/index.js","type":"module","_from":"file:ahrowe-audit-log-0.3.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./koa":{"types":"./dist/koa.d.ts","import":"./dist/koa.js"}},"scripts":{"lint":"eslint .","test":"vitest","build":"tsc","release":"bash scripts/release.sh","prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""},"_npmUser":{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"},"_resolved":"/tmp/bed82426d252e5ab6792db60fb6d4107/ahrowe-audit-log-0.3.0.tgz","_integrity":"sha512-HSUrUVGmloHKzPlalP1icrDbgPLuABnUU1HY3c/5LNj3Sur5t/ep1SHgb1DB8uEKo5ZJbjBrcUG6BNhshLExMQ==","repository":{"url":"git+https://github.com/AndreasWeinzierl/ahrowe-audit-log.git","type":"git"},"_npmVersion":"11.9.0","description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","dependencies":{"lodash":"^4.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"koa":"^3.2.1","eslint":"^10.5.0","vitest":"^4.1.8","globals":"^17.6.0","@eslint/js":"^10.0.1","@types/koa":"^3.0.3","typescript":"^6.0.3","@types/node":"^24.13.2","@types/lodash":"^4.17.24","typescript-eslint":"^8.61.0"},"peerDependencies":{"koa":"^3.2.1"},"peerDependenciesMeta":{"koa":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/audit-log_0.3.0_1787850113353_0.7745194111714311","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@ahrowe/audit-log","version":"0.4.0","type":"module","license":"MIT","description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"author":{"name":"AndreasWeinzierl"},"repository":{"type":"git","url":"git+https://github.com/AndreasWeinzierl/ahrowe-audit-log.git"},"bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./koa":{"import":"./dist/koa.js","types":"./dist/koa.d.ts"}},"publishConfig":{"access":"public"},"dependencies":{"lodash":"^4.18.1"},"peerDependencies":{"koa":"^3.2.1"},"peerDependenciesMeta":{"koa":{"optional":true}},"devDependencies":{"@eslint/js":"^10.0.1","@types/koa":"^3.0.3","@types/lodash":"^4.17.24","@types/node":"^24.13.2","eslint":"^10.5.0","globals":"^17.6.0","koa":"^3.2.1","typescript":"^6.0.3","typescript-eslint":"^8.61.0","vitest":"^4.1.8"},"scripts":{"prebuild":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","build":"tsc","lint":"eslint .","test":"vitest","release":"bash scripts/release.sh"},"_id":"@ahrowe/audit-log@0.4.0","_integrity":"sha512-XgCssYTHZBTgn2pnU8hihDNZrE5G1ABlja+H+wahlleRpqxzhQdfGade7xE8byIhXt0kwEJtHTA7kBCwNlsKFg==","_resolved":"/tmp/28812c218cc9bc2f35ad1aa1e5ba8a75/ahrowe-audit-log-0.4.0.tgz","_from":"file:ahrowe-audit-log-0.4.0.tgz","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-XgCssYTHZBTgn2pnU8hihDNZrE5G1ABlja+H+wahlleRpqxzhQdfGade7xE8byIhXt0kwEJtHTA7kBCwNlsKFg==","shasum":"098438cd3381bcf1adb4bc2b73fb09a6a0cb4574","tarball":"https://registry.npmjs.org/@ahrowe/audit-log/-/audit-log-0.4.0.tgz","fileCount":35,"unpackedSize":46217,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID68j3Smah7mhZfc0gmbSKhXxZcCeDHiQRE7UBk8r1ScAiAPnSULJ3h+HMl75vSd6a+qR8Lm75Upkt9PgXlqjb0dCg=="}]},"_npmUser":{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"},"directories":{},"maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/audit-log_0.4.0_1787852323504_0.5034344262244443"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-24T08:22:49.764Z","modified":"2026-08-27T17:38:44.010Z","0.1.1":"2026-06-24T08:22:50.070Z","0.1.2":"2026-06-24T08:32:54.060Z","0.2.0":"2026-06-30T20:01:28.182Z","0.3.0":"2026-08-27T17:01:53.527Z","0.4.0":"2026-08-27T17:38:43.806Z"},"bugs":{"url":"https://github.com/AndreasWeinzierl/ahrowe-audit-log/issues"},"author":{"name":"AndreasWeinzierl"},"license":"MIT","homepage":"https://github.com/AndreasWeinzierl/ahrowe-audit-log#readme","keywords":["audit","audit-log","changelog","mongodb","koa","asynclocalstorage"],"repository":{"type":"git","url":"git+https://github.com/AndreasWeinzierl/ahrowe-audit-log.git"},"description":"Framework-agnostic audit-log recorder: hooks a DB event bus, records who/when/what (field-level diff + redacted snapshots) with AsyncLocalStorage request context.","maintainers":[{"name":"plaguedoctor19","email":"ahrowe.dev@gmail.com"}],"readme":"# @ahrowe/audit-log\n\nFramework-agnostic audit trail for debugging: hook a database event bus and get\none immutable row per mutation — **who** changed **what**, **when**, and **in\nwhich request** — with a field-level diff on updates and redacted snapshots on\ncreate/delete.\n\nIt carries **no database dependency**. You inject any service that satisfies a\ntiny structural interface (`on` / `insert` / `createIndex`), so it works with any\n`createService(...)`-style db wrapper that emits create/update/remove events.\n\n## How it works\n\nThe db wrapper you already use emits `created` / `updated` / `removed` events.\n`registerAudit` listens, diffs the docs, and writes one idempotent audit row.\nContext (actor, tenantId, requestId, …) is captured at mutation time and carried\non the event payload, so an outbox relay can deliver it safely after the request ends.\n\n```\nrequest → context middleware (requestId, ip, …)\n          → your auth sets actor → your code mutates a doc\n            → db emits 'updated' { prevDoc, doc, context, eventId }\n              → recorder diffs → writes one audit row (idempotent via eventId)\n```\n\n## Install\n\n```bash\npnpm add @ahrowe/audit-log\n# koa is an OPTIONAL peer — only needed for the `/koa` request-context middleware\npnpm add koa\n```\n\n## Usage\n\n### 1. Bind the recorder to your audit collection\n\n```ts\nimport { createAuditLog, AUDIT_LOG_COLLECTION } from '@ahrowe/audit-log';\nimport db from 'your-db-layer';\n\n// You create the service (the db binding); the package owns the indexes.\n// `db.createService` here is any wrapper that returns an `AuditDbService`-compatible\n// object (emits created/updated/removed, has insert + createIndex).\nconst store = db.createService(AUDIT_LOG_COLLECTION, null, { addUpdatedOnField: false });\n\nexport const { registerAudit, recordEvent } = createAuditLog(store, { ttlDays: 30 });\n```\n\n> Audit rows are built internally and are correct by construction, so no write\n> schema is shipped. If your db layer supports a validator and you want belt-and-\n> suspenders write validation, pass your own in place of `null`.\n\n### 2. Register the collections you want audited\n\n```ts\nregisterAudit(invoiceService, 'invoices');\nregisterAudit(userService, 'users', { redact: ['resetToken'] }); // + per-service secrets\n```\n\n`registerAudit` requires the service to emit `created` / `updated` / `removed`\nas `AuditDbEvent<T>` (see `AuditDbService`). The `context` and `eventId` fields\nare optional — services that don't emit them still work. `passwordHash`,\n`passwordSalt` and `updatedOn` are always redacted; add more per service via `redact`.\n\n### 3. Open a request context so rows get an actor\n\n```ts\nimport { requestContext } from '@ahrowe/audit-log/koa';\nimport { auditContext, AuditActorType } from '@ahrowe/audit-log';\n\napp.use(requestContext); // FIRST middleware\n\n// after you authenticate the user:\nauditContext.setActor({ type: AuditActorType.User, _id: user._id, email: user.email });\nauditContext.setTenantId(company._id);\n```\n\nNo request context (cron/webhook) → rows are recorded with `actor: { type: 'system' }`.\n\nNot on Koa? Skip `/koa` and open the store yourself with `auditContext.run(store, next)`.\n\n### 4. Record custom (non-CRUD) events\n\nFor domain events that aren't a raw mutation — and carry intent a diff can't:\n\n```ts\nawait recordEvent({\n  action: 'invoice.finalized',     // any string; CRUD verbs autocomplete\n  entityType: 'invoices',\n  entityId: invoice._id,           // optional — omit for events with no single subject\n  metaData: { number: invoice.invoiceNumber, total: invoice.total },\n});\n// actor, requestId, tenantId, method/route/ip are filled from ALS context.\n```\n\n`recordEvent` returns the write promise — `await` it for delivery confirmation, or ignore it for fire-and-forget. Recording only: there's no query/replay layer (that's an event store, a different tool).\n\n## What gets stored\n\n| Source | `action` | `changes` | `snapshot` | `metaData` |\n|---|---|---|---|---|\n| CRUD created | `created` | — | redacted full doc | — |\n| CRUD updated | `updated` | field-level before/after diff | — | — |\n| CRUD removed | `removed` | — | redacted full doc | — |\n| `recordEvent` | any string | — | — | your payload |\n\nRows are immutable and expire automatically `ttlDays` (default 30) after `createdOn`.\nSet `ttlDays: false` to keep audit rows forever. Doing so requires your store to\nimplement the optional `dropIndex(indexName)` method so the existing TTL index can be\nremoved — MongoDB won't apply a changed/disabled TTL to an index that already\nexists otherwise. Without `dropIndex`, disabling TTL is logged as an error and rows\nwill keep expiring on the old schedule until you drop the index by hand.\n\n## API\n\n- `createAuditLog(store, { ttlDays?, logger?, tenantField? }) → { registerAudit, recordEvent, recordAudit }`\n- `registerAudit(service, entityType, { redact? })` — auto-capture CRUD off the db bus\n- `recordEvent({ action, entityType, entityId?, metaData?, tenantId? })` — custom events\n- `auditContext` — `run` / `get` / `setActor` / `setTenantId`\n- `requestContext` (from `@ahrowe/audit-log/koa`)\n- `AUDIT_LOG_COLLECTION`\n- `diffDocs`, `redactDoc`, `REDACT` — pure helpers\n- types: `AuditLog`, `AuditAction`, `AuditActor`, `AuditActorType`, `AuditChange`, `AuditDbService`, `AuditDbEvent`\n","readmeFilename":"README.md"}