-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/ak 009 retention redaction idempotency validation #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
y-aithnini
merged 21 commits into
develop
from
feature/AK-009-retention-redaction-idempotency-validation
Apr 2, 2026
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
6c1cf06
Feature/ak 001 core domain types (#3)
y-aithnini 370361d
core audit service implementation (#4)
y-aithnini e82150b
implemented adapter (#5)
y-aithnini a303774
implemented utility providers (#6)
y-aithnini be23582
implemented nestjs module (#7)
y-aithnini ce8e606
Feature/ak 006 comprehensive testing (#8)
y-aithnini 4dea4b8
fixed mongodb repository tests results (#9)
y-aithnini 9373521
implemented remaining test fixes (#10)
y-aithnini 489e087
feat: add retention redaction idempotency and config validation
y-aithnini 0b52904
fix: use compatible ignoreDeprecations value
y-aithnini e9ce193
feat: add cursor pagination, OTel observer hooks, mutation testing, a…
y-aithnini ee86de4
feat: add event streaming, docs updates, and CI compatibility matrix
y-aithnini 27c8694
style: enforce LF line endings and add .gitattributes
y-aithnini 8a60f9c
fix: resolve SonarCloud quality gate failures and warnings
y-aithnini 49ac99c
Feature/ak 009 retention redaction idempotency validation (#11)
y-aithnini 2a7c1d8
fix: update @nestjs/common to 11.1.17 to patch file-type CVEs
y-aithnini aa0ddaa
Feature/ak 009 retention redaction idempotency validation (#13)
y-aithnini c6b5255
refactor: remove MongoDB adapter, add custom repository config
y-aithnini 1a26143
chore: merge develop and resolve conflicts (keep custom repository, r…
y-aithnini 46221cc
chore: release v0.1.0
y-aithnini a2f8fc2
chore: merge develop (simplify CI to ubuntu/Node 22)
y-aithnini File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Enforce LF line endings for all text files on all platforms | ||
| * text=auto eol=lf | ||
|
|
||
| # Explicitly declare binary files to prevent corruption | ||
| *.png binary | ||
| *.jpg binary | ||
| *.jpeg binary | ||
| *.gif binary | ||
| *.ico binary | ||
| *.woff binary | ||
| *.woff2 binary | ||
| *.ttf binary | ||
| *.otf binary | ||
| *.eot binary |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # @ciscode/audit-kit | ||
|
|
||
| ## 0.1.0 | ||
|
|
||
| ### Minor Changes | ||
|
|
||
| - Initial feature release of @ciscode/audit-kit. | ||
| - Cursor-based (keyset) pagination via `queryWithCursor()` | ||
| - OpenTelemetry-compatible observer hooks (`IAuditObserver`) | ||
| - Audit event streaming adapter (`IAuditEventPublisher`, `EventEmitterAuditEventPublisher`) | ||
| - PII redaction, idempotency, and retention policies | ||
| - Custom repository config (`type: "custom"`) — bring your own repository from a database package | ||
| - In-memory repository for testing | ||
| - Stryker mutation testing configuration | ||
| - Vitest performance benchmarks | ||
| - CI compatibility matrix (Ubuntu + Windows × Node 20 + 22) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,148 @@ | ||
| # NestJS DeveloperKit (Template) | ||
| # @ciscode/audit-kit | ||
|
|
||
| Template repository for building reusable NestJS npm packages. | ||
| AuditKit is a reusable NestJS module for immutable audit logging with clean architecture (`core` / `infra` / `nest`). | ||
|
|
||
| ## What you get | ||
| It provides: | ||
|
|
||
| - ESM + CJS + Types build (tsup) | ||
| - Jest testing | ||
| - ESLint + Prettier | ||
| - Changesets (Release PR flow) | ||
| - Husky (pre-commit + pre-push) | ||
| - Enforced package architecture (core / infra / nest) with strict public API | ||
| - Framework-free core service (`AuditService`) | ||
| - Pluggable repositories (MongoDB, in-memory) | ||
| - Automatic change detection | ||
| - Configurable redaction, idempotency, and retention policies | ||
| - Cursor-based pagination for stable listing | ||
| - Observability hooks (OpenTelemetry-friendly observer port) | ||
| - Event streaming hooks (publisher port + default EventEmitter adapter) | ||
|
|
||
| ## Scripts | ||
| ## Install | ||
|
|
||
| - `npm run build` – build to `dist/` | ||
| - `npm test` – run tests | ||
| - `npm run typecheck` – TypeScript typecheck | ||
| - `npm run lint` – ESLint | ||
| - `npm run format` / `npm run format:write` – Prettier | ||
| - `npx changeset` – create a changeset | ||
| ```bash | ||
| npm install @ciscode/audit-kit | ||
| ``` | ||
|
|
||
| ## Release flow (summary) | ||
| Peer dependencies are managed by consuming applications. | ||
|
|
||
| - Work on a `feature` branch from `develop` | ||
| - Merge to `develop` | ||
| - Add a changeset for user-facing changes: `npx changeset` | ||
| - Automation opens “Version Packages” PR into `master` | ||
| - Merge to `master`, tag `vX.Y.Z` to publish | ||
| ## Quick Start | ||
|
|
||
| See `docs/RELEASE.md` for details. | ||
| ```typescript | ||
| import { Module } from "@nestjs/common"; | ||
| import { AuditKitModule } from "@ciscode/audit-kit"; | ||
|
|
||
| @Module({ | ||
| imports: [ | ||
| AuditKitModule.register({ | ||
| repository: { | ||
| type: "in-memory", | ||
| }, | ||
| redaction: { | ||
| enabled: true, | ||
| fields: ["actor.email", "metadata.password"], | ||
| mask: "[REDACTED]", | ||
| }, | ||
| idempotency: { | ||
| enabled: true, | ||
| keyStrategy: "idempotencyKey", | ||
| }, | ||
| }), | ||
| ], | ||
| }) | ||
| export class AppModule {} | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ```typescript | ||
| import { Injectable } from "@nestjs/common"; | ||
| import { ActorType, AuditActionType, AuditService } from "@ciscode/audit-kit"; | ||
|
|
||
| @Injectable() | ||
| export class UserService { | ||
| constructor(private readonly auditService: AuditService) {} | ||
|
|
||
| async updateUser(): Promise<void> { | ||
| await this.auditService.log({ | ||
| actor: { id: "user-1", type: ActorType.USER, email: "user@example.com" }, | ||
| action: AuditActionType.UPDATE, | ||
| resource: { type: "user", id: "user-1" }, | ||
| metadata: { reason: "profile update" }, | ||
| idempotencyKey: "req-123", | ||
| }); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Advanced Features | ||
|
|
||
| ### Cursor Pagination | ||
|
|
||
| ```typescript | ||
| const page1 = await auditService.queryWithCursor({ actorId: "user-1" }, { limit: 20 }); | ||
|
|
||
| if (page1.hasMore) { | ||
| const page2 = await auditService.queryWithCursor( | ||
| { actorId: "user-1" }, | ||
| { limit: 20, cursor: page1.nextCursor }, | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ### Observability Hooks | ||
|
|
||
| Provide an observer to emit metrics/spans/log events (for OpenTelemetry, Prometheus, etc.): | ||
|
|
||
| ```typescript | ||
| AuditKitModule.register({ | ||
| repository: { type: "in-memory" }, | ||
| observer: { | ||
| onEvent(event) { | ||
| // event.operation, event.durationMs, event.success | ||
| console.log(event); | ||
| }, | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| ### Event Streaming | ||
|
|
||
| Emit domain events after successful audit creation: | ||
|
|
||
| ```typescript | ||
| AuditKitModule.register({ | ||
| repository: { type: "in-memory" }, | ||
| eventStreaming: { | ||
| enabled: true, | ||
| // optional custom publisher; default is EventEmitter adapter | ||
| publisher: { | ||
| publish(event) { | ||
| console.log(event.type, event.payload.id); | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| ## Tooling | ||
|
|
||
| - Tests: `npm test` | ||
| - Typecheck: `npm run typecheck` | ||
| - Lint: `npm run lint` | ||
| - Build: `npm run build` | ||
| - Mutation testing: `npm run mutation` | ||
| - Benchmarks: `npm run bench` | ||
|
|
||
| ## CI Compatibility Matrix | ||
|
|
||
| PR validation runs on a matrix to catch environment regressions early: | ||
|
|
||
| - Node.js: 20, 22 | ||
| - OS: ubuntu-latest, windows-latest | ||
|
|
||
| See [.github/workflows/pr-validation.yml](.github/workflows/pr-validation.yml). | ||
|
|
||
| ## Release Flow (Summary) | ||
|
|
||
| 1. Work on a feature branch from `develop` | ||
| 2. Add a changeset for user-facing changes: `npx changeset` | ||
| 3. Merge into `develop` | ||
| 4. Automation opens "Version Packages" PR into `master` | ||
| 5. Merge and publish | ||
|
|
||
| See [docs/RELEASE.md](docs/RELEASE.md) for details. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /** | ||
| * Jest mock for nanoid ESM module | ||
| * | ||
| * SECURITY NOTE: This mock uses Math.random() which is NOT cryptographically secure. | ||
| * This is acceptable because: | ||
| * 1. This code is ONLY used in Jest tests, never in production | ||
| * 2. Test IDs don't require cryptographic security | ||
| * 3. The real nanoid library (used in production) uses crypto.randomBytes() | ||
| * | ||
| * SonarQube Security Hotspot Review: Accepted as safe for test-only code | ||
| */ | ||
|
|
||
| export const nanoid = jest.fn((size = 21) => { | ||
| let result = ""; | ||
| const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"; | ||
| for (let i = 0; i < size; i++) { | ||
| // NOSONAR: Math.random() is acceptable for test mocks | ||
| result += chars.charAt(Math.floor(Math.random() * chars.length)); // NOSONAR | ||
| } | ||
| return result; | ||
| }); | ||
|
|
||
| export const customAlphabet = jest.fn((alphabet: string, defaultSize = 21) => { | ||
| return (size = defaultSize) => { | ||
| let result = ""; | ||
| for (let i = 0; i < size; i++) { | ||
| // NOSONAR: Math.random() is acceptable for test mocks | ||
| result += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); // NOSONAR | ||
| } | ||
| return result; | ||
| }; | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
README claims "Pluggable repositories (MongoDB, in-memory)", but the public
RepositoryConfigtype only allowstype: "in-memory" | "custom"and there is no MongoDB adapter in this PR. This is misleading for consumers; either implement and document MongoDB support, or update the README to describe the supported repository types (in-memory + custom).