@cqrs-ddd/pipeline-audit 0.5.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.
Files changed (52) hide show
  1. package/COMMERCIAL_LICENSE.txt +34 -0
  2. package/LICENSE +661 -0
  3. package/README.md +35 -0
  4. package/dist/audit.behavior.d.ts +102 -0
  5. package/dist/audit.behavior.d.ts.map +1 -0
  6. package/dist/audit.behavior.js +262 -0
  7. package/dist/audit.behavior.js.map +1 -0
  8. package/dist/constants/tokens.d.ts +27 -0
  9. package/dist/constants/tokens.d.ts.map +1 -0
  10. package/dist/constants/tokens.js +28 -0
  11. package/dist/constants/tokens.js.map +1 -0
  12. package/dist/helpers/audit.intent.d.ts +15 -0
  13. package/dist/helpers/audit.intent.d.ts.map +1 -0
  14. package/dist/helpers/audit.intent.js +15 -0
  15. package/dist/helpers/audit.intent.js.map +1 -0
  16. package/dist/helpers/build-record.d.ts +42 -0
  17. package/dist/helpers/build-record.d.ts.map +1 -0
  18. package/dist/helpers/build-record.js +85 -0
  19. package/dist/helpers/build-record.js.map +1 -0
  20. package/dist/helpers/json.d.ts +7 -0
  21. package/dist/helpers/json.d.ts.map +1 -0
  22. package/dist/helpers/json.js +102 -0
  23. package/dist/helpers/json.js.map +1 -0
  24. package/dist/helpers/redact.d.ts +2 -0
  25. package/dist/helpers/redact.d.ts.map +1 -0
  26. package/dist/helpers/redact.js +3 -0
  27. package/dist/helpers/redact.js.map +1 -0
  28. package/dist/index.d.ts +12 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +9 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/interfaces/audit-options.interface.d.ts +95 -0
  33. package/dist/interfaces/audit-options.interface.d.ts.map +1 -0
  34. package/dist/interfaces/audit-options.interface.js +3 -0
  35. package/dist/interfaces/audit-options.interface.js.map +1 -0
  36. package/dist/interfaces/audit-record.interface.d.ts +79 -0
  37. package/dist/interfaces/audit-record.interface.d.ts.map +1 -0
  38. package/dist/interfaces/audit-record.interface.js +3 -0
  39. package/dist/interfaces/audit-record.interface.js.map +1 -0
  40. package/dist/interfaces/audit-sink.interface.d.ts +37 -0
  41. package/dist/interfaces/audit-sink.interface.d.ts.map +1 -0
  42. package/dist/interfaces/audit-sink.interface.js +3 -0
  43. package/dist/interfaces/audit-sink.interface.js.map +1 -0
  44. package/dist/sinks/log.sink.d.ts +33 -0
  45. package/dist/sinks/log.sink.d.ts.map +1 -0
  46. package/dist/sinks/log.sink.js +31 -0
  47. package/dist/sinks/log.sink.js.map +1 -0
  48. package/dist/sinks/postgres.sink.d.ts +69 -0
  49. package/dist/sinks/postgres.sink.d.ts.map +1 -0
  50. package/dist/sinks/postgres.sink.js +132 -0
  51. package/dist/sinks/postgres.sink.js.map +1 -0
  52. package/package.json +69 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Serialize an audit value without silently collapsing or dropping JavaScript
3
+ * values that native JSON.stringify cannot represent faithfully. Tagged objects
4
+ * keep the representation explicit and portable across log and database sinks.
5
+ */
6
+ export declare function stringifyAuditValue(value: unknown, space?: number): string;
7
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/helpers/json.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1E"}
@@ -0,0 +1,102 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ /**
3
+ * Serialize an audit value without silently collapsing or dropping JavaScript
4
+ * values that native JSON.stringify cannot represent faithfully. Tagged objects
5
+ * keep the representation explicit and portable across log and database sinks.
6
+ */
7
+ export function stringifyAuditValue(value, space) {
8
+ return JSON.stringify(normalizeAuditValue(value, new WeakSet()), null, space);
9
+ }
10
+ function normalizeAuditValue(value, ancestors) {
11
+ if (typeof value === 'bigint') {
12
+ return { $type: 'BigInt', value: value.toString() };
13
+ }
14
+ if (typeof value === 'number' && !Number.isFinite(value)) {
15
+ return { $type: 'Number', value: String(value) };
16
+ }
17
+ if (typeof value === 'function') {
18
+ return { $type: 'Function', name: value.name || null };
19
+ }
20
+ if (typeof value === 'symbol') {
21
+ return { $type: 'Symbol', description: value.description ?? null };
22
+ }
23
+ if (value === undefined)
24
+ return { $type: 'Undefined' };
25
+ if (value === null || typeof value !== 'object')
26
+ return value;
27
+ if (ancestors.has(value))
28
+ return '[Circular]';
29
+ if (value instanceof Date)
30
+ return value.toISOString();
31
+ if (value instanceof RegExp) {
32
+ return {
33
+ $type: 'RegExp',
34
+ source: value.source,
35
+ flags: value.flags,
36
+ lastIndex: value.lastIndex,
37
+ };
38
+ }
39
+ ancestors.add(value);
40
+ try {
41
+ if (value instanceof Error) {
42
+ const symbolProperties = normalizeSymbolProperties(value, ancestors);
43
+ return {
44
+ $type: 'Error',
45
+ name: value.name,
46
+ message: value.message,
47
+ stack: value.stack,
48
+ properties: normalizeAuditValue(Object.fromEntries(Object.entries(value)), ancestors),
49
+ ...(symbolProperties.length > 0 ? { symbolProperties } : {}),
50
+ };
51
+ }
52
+ if (value instanceof Map) {
53
+ return {
54
+ $type: 'Map',
55
+ entries: Array.from(value, ([key, item]) => [
56
+ normalizeAuditValue(key, ancestors),
57
+ normalizeAuditValue(item, ancestors),
58
+ ]),
59
+ };
60
+ }
61
+ if (value instanceof Set) {
62
+ return {
63
+ $type: 'Set',
64
+ values: Array.from(value, (item) => normalizeAuditValue(item, ancestors)),
65
+ };
66
+ }
67
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
68
+ const bytes = value instanceof ArrayBuffer
69
+ ? new Uint8Array(value)
70
+ : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
71
+ return {
72
+ $type: value.constructor.name,
73
+ bytes: Array.from(bytes),
74
+ };
75
+ }
76
+ if (Array.isArray(value)) {
77
+ return Array.from({ length: value.length }, (_, index) => index in value
78
+ ? normalizeAuditValue(value[index], ancestors)
79
+ : { $type: 'ArrayHole' });
80
+ }
81
+ const properties = Object.fromEntries(Object.entries(value).map(([key, item]) => [
82
+ key,
83
+ normalizeAuditValue(item, ancestors),
84
+ ]));
85
+ const symbolProperties = normalizeSymbolProperties(value, ancestors);
86
+ return symbolProperties.length > 0
87
+ ? { $type: 'Object', properties, symbolProperties }
88
+ : properties;
89
+ }
90
+ finally {
91
+ ancestors.delete(value);
92
+ }
93
+ }
94
+ function normalizeSymbolProperties(value, ancestors) {
95
+ return Object.getOwnPropertySymbols(value)
96
+ .filter((key) => Object.getOwnPropertyDescriptor(value, key)?.enumerable === true)
97
+ .map((key) => [
98
+ normalizeAuditValue(key, ancestors),
99
+ normalizeAuditValue(Reflect.get(value, key), ancestors),
100
+ ]);
101
+ }
102
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/helpers/json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { DEFAULT_REDACT_KEYS, REDACTED, redactValue, } from '@cqrs-ddd/safe-stringify';
2
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/helpers/redact.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,WAAW,GACZ,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,3 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export { DEFAULT_REDACT_KEYS, REDACTED, redactValue, } from '@cqrs-ddd/safe-stringify';
3
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/helpers/redact.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,WAAW,GACZ,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,12 @@
1
+ export { AUDIT_RECORD_ITEM, AUDIT_RECORD_ITEM_TOKEN, AUDIT_START_RECORD_ITEM_TOKEN, AuditBehavior, } from './audit.behavior.js';
2
+ export { AUDIT_OUTCOMES, AUDIT_REQUEST_KINDS, AUDIT_SEVERITY, } from './constants/tokens.js';
3
+ export { type AuditIntentOptions, audit, } from './helpers/audit.intent.js';
4
+ export type { BuildAuditRecordInput, BuildAuditStartRecordInput, } from './helpers/build-record.js';
5
+ export { buildAuditRecord, buildAuditStartRecord, } from './helpers/build-record.js';
6
+ export { DEFAULT_REDACT_KEYS, REDACTED, redactValue, } from './helpers/redact.js';
7
+ export type { AuditActorFactory, AuditBehaviorOptions, AuditMetadataFactory, AuditRedactor, } from './interfaces/audit-options.interface.js';
8
+ export type { AuditActor, AuditError, AuditOutcome, AuditRecord, AuditRequestKind, AuditSeverity, AuditStartRecord, } from './interfaces/audit-record.interface.js';
9
+ export type { AuditSink } from './interfaces/audit-sink.interface.js';
10
+ export { type AuditLoggerLike, LogAuditSink, type LogAuditSinkOptions, } from './sinks/log.sink.js';
11
+ export { createAuditTableSql, PostgresAuditSink, type PostgresAuditSinkOptions, type PostgresQueryableLike, } from './sinks/postgres.sink.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,6BAA6B,EAC7B,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,GACN,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,GACd,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,gBAAgB,GACjB,MAAM,wCAAwC,CAAC;AAChD,YAAY,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EACL,KAAK,eAAe,EACpB,YAAY,EACZ,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,GAC3B,MAAM,0BAA0B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export { AUDIT_RECORD_ITEM, AUDIT_RECORD_ITEM_TOKEN, AUDIT_START_RECORD_ITEM_TOKEN, AuditBehavior, } from './audit.behavior.js';
3
+ export { AUDIT_OUTCOMES, AUDIT_REQUEST_KINDS, AUDIT_SEVERITY, } from './constants/tokens.js';
4
+ export { audit, } from './helpers/audit.intent.js';
5
+ export { buildAuditRecord, buildAuditStartRecord, } from './helpers/build-record.js';
6
+ export { DEFAULT_REDACT_KEYS, REDACTED, redactValue, } from './helpers/redact.js';
7
+ export { LogAuditSink, } from './sinks/log.sink.js';
8
+ export { createAuditTableSql, PostgresAuditSink, } from './sinks/postgres.sink.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,6BAA6B,EAC7B,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEL,KAAK,GACN,MAAM,2BAA2B,CAAC;AAKnC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,QAAQ,EACR,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAiB7B,OAAO,EAEL,YAAY,GAEb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,95 @@
1
+ import type { IPipelineContext } from '@cqrs-ddd/pipeline';
2
+ import type { AuditActor, AuditRequestKind, AuditSeverity } from './audit-record.interface.js';
3
+ /** Resolves the acting principal from the pipeline context. */
4
+ export type AuditActorFactory = (context: IPipelineContext) => AuditActor | undefined;
5
+ /** Produces extra, request-aware metadata to attach to an audit record. */
6
+ export type AuditMetadataFactory = (context: IPipelineContext) => Record<string, unknown>;
7
+ /** Custom redactor replacing a raw payload/response with a safe-to-store value. */
8
+ export type AuditRedactor = (value: unknown) => unknown;
9
+ /**
10
+ * Per-handler (and constructor-default) options for {@link AuditBehavior}.
11
+ *
12
+ * Supplied per handler via `pipeline.wrap(options, [AuditBehavior, { ... }])`,
13
+ * shallow-merged over the constructor defaults (handler keys win).
14
+ *
15
+ * @example Audit a sensitive login command
16
+ * ```ts
17
+ * class LoginHandler {
18
+ * @pipeline.wrap({ kind: 'command' }, [AuditBehavior, {
19
+ * action: 'auth.login',
20
+ * severity: 'medium',
21
+ * redactKeys: ['code'],
22
+ * actor: (ctx) => {
23
+ * const command = ctx.request as LoginCommand;
24
+ * return { id: command.email, email: command.email };
25
+ * },
26
+ * }])
27
+ * async handle(command: LoginCommand) {}
28
+ * }
29
+ * ```
30
+ */
31
+ export interface AuditBehaviorOptions {
32
+ /**
33
+ * Logical action name recorded on the entry (e.g. `user.create`).
34
+ * Default: `context.requestName`.
35
+ */
36
+ action?: string;
37
+ /**
38
+ * Severity recorded on the entry. Default: `'medium'`, or `'low'` for queries.
39
+ */
40
+ severity?: AuditSeverity;
41
+ /**
42
+ * Resolve the acting principal — typically reads an id from `context.items`
43
+ * set by an upstream auth behavior, e.g.
44
+ * `actor: (c) => ({ id: c.items.get('currentUserId') })`.
45
+ */
46
+ actor?: AuditActorFactory;
47
+ /**
48
+ * Record the (redacted) request payload. Default `true`. Set `false` for
49
+ * high-volume or sensitive handlers where the action alone is enough.
50
+ */
51
+ captureRequest?: boolean;
52
+ /**
53
+ * Record the (redacted) handler response. Default `false` — responses are
54
+ * often large and rarely needed for an audit trail.
55
+ */
56
+ captureResponse?: boolean;
57
+ /**
58
+ * Request kinds to audit. Default `['command']`: queries change nothing, and
59
+ * a domain event follows a command that is already audited. Example:
60
+ * `['command', 'query']` to also audit reads.
61
+ */
62
+ captureKinds?: AuditRequestKind[];
63
+ /**
64
+ * Case-insensitive payload/response field names whose values are masked with
65
+ * `'[REDACTED]'` before storage. Merged with the built-in defaults
66
+ * (`password`, `token`, `secret`, …). Set `redact` for full control.
67
+ */
68
+ redactKeys?: string[];
69
+ /**
70
+ * Full custom redaction of the payload/response, replacing the built-in
71
+ * key-masking. Receives the raw value, returns the safe-to-store value.
72
+ */
73
+ redact?: AuditRedactor;
74
+ /** Produce extra metadata to merge into the audit record. */
75
+ metadata?: AuditMetadataFactory;
76
+ /**
77
+ * Write a pending start record before the handler runs, when the sink
78
+ * implements `begin`. Default `true`. Set `false` to save that write when a
79
+ * lost record is acceptable.
80
+ */
81
+ recordStart?: boolean;
82
+ /**
83
+ * Include the error stack trace on failure records. Default `true`.
84
+ */
85
+ includeStack?: boolean;
86
+ /**
87
+ * When record construction (actor/metadata/redactor factories) or the sink itself throws,
88
+ * allow the request to continue (`true`, default) or fail a successful request with the
89
+ * audit error (`false`). A handler error is always rethrown unchanged, and an audit failure
90
+ * on that path is only logged. Fail-open favors availability; fail-closed favors a
91
+ * guaranteed audit trail.
92
+ */
93
+ failOpen?: boolean;
94
+ }
95
+ //# sourceMappingURL=audit-options.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-options.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/audit-options.interface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,6BAA6B,CAAC;AAErC,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,OAAO,EAAE,gBAAgB,KACtB,UAAU,GAAG,SAAS,CAAC;AAE5B,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,gBAAgB,KACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7B,mFAAmF;AACnF,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
@@ -0,0 +1,3 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export {};
3
+ //# sourceMappingURL=audit-options.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-options.interface.js","sourceRoot":"","sources":["../../src/interfaces/audit-options.interface.ts"],"names":[],"mappings":"AAAA,sEAAsE"}
@@ -0,0 +1,79 @@
1
+ /** Request kinds as classified by the pipeline. */
2
+ export type AuditRequestKind = 'command' | 'query' | 'event' | 'unknown';
3
+ /** Whether the audited operation succeeded or threw. */
4
+ export type AuditOutcome = 'success' | 'failure';
5
+ /** Relative importance of an audited action, for filtering / alerting. */
6
+ export type AuditSeverity = 'low' | 'medium' | 'high' | 'critical';
7
+ /**
8
+ * The principal that triggered the audited action — resolved from the pipeline
9
+ * context (e.g. read from `context.items` populated by an upstream auth
10
+ * behavior). `id` is conventional; arbitrary extra fields are allowed.
11
+ */
12
+ export interface AuditActor {
13
+ /** Stable identifier of the actor (user id, service account, api key id). */
14
+ id?: string;
15
+ [key: string]: unknown;
16
+ }
17
+ /** Serializable description of a failure captured in an audit record. */
18
+ export interface AuditError {
19
+ /** Error class name (e.g. `ForbiddenException`), or `'unknown'` for non-Error throws. */
20
+ name: string;
21
+ /** Error message. */
22
+ message: string;
23
+ /** Stack trace, unless suppressed via `includeStack: false`. */
24
+ stack?: string;
25
+ }
26
+ /**
27
+ * A single audit-trail entry for one pipeline operation (who did what, when,
28
+ * and with what outcome), forwarded to an
29
+ * {@link AuditSink}.
30
+ *
31
+ * Unlike a dead letter (failures only), an audit record is written for **both**
32
+ * successful and failed operations, so denied/rejected attempts are captured too.
33
+ */
34
+ export interface AuditRecord {
35
+ /** Unique id for this entry (UUIDv7, so ids sort in start order). */
36
+ id: string;
37
+ /** Correlation ID of the pipeline run (for cross-system tracing). */
38
+ correlationId: string;
39
+ /** Active tenant identifier if execution occurred within a multi-tenant context. */
40
+ tenantId?: string;
41
+ /** Logical action name, e.g. `user.create`. Defaults to the request name. */
42
+ action: string;
43
+ /** Severity of the action. Defaults to `'medium'` (`'low'` for queries). */
44
+ severity: AuditSeverity;
45
+ /** Whether the operation succeeded or failed. */
46
+ outcome: AuditOutcome;
47
+ /** The principal that performed the action, if resolvable. */
48
+ actor?: AuditActor;
49
+ /** Whether the request was a command, query, event, or unknown. */
50
+ requestKind: AuditRequestKind;
51
+ /** Request class name, e.g. `CreateUserCommand`. */
52
+ requestName: string;
53
+ /** Handler class name, e.g. `CreateUserHandler`. */
54
+ handlerName: string;
55
+ /** The request payload (redacted), unless `captureRequest: false`. */
56
+ payload?: unknown;
57
+ /** The handler response (redacted), only when `captureResponse: true`. */
58
+ response?: unknown;
59
+ /** Error details — present only when `outcome` is `'failure'`. */
60
+ error?: AuditError;
61
+ /** Wall-clock duration of the operation in milliseconds. */
62
+ durationMs: number;
63
+ /** ISO-8601 timestamp of when the operation started. */
64
+ timestamp: string;
65
+ /** Metadata from the `metadata` factory, plus `tenantId` when present. */
66
+ metadata?: Record<string, unknown>;
67
+ }
68
+ /**
69
+ * The record written before the handler runs, when the sink implements
70
+ * {@link AuditSink.begin}. Its `outcome` stays `'pending'` until the final
71
+ * {@link AuditRecord}, written under the same `id`, replaces it. A record still
72
+ * pending after the process stopped marks an attempt whose outcome is unknown:
73
+ * the handler may or may not have completed.
74
+ */
75
+ export type AuditStartRecord = Omit<AuditRecord, 'outcome' | 'response' | 'error' | 'durationMs'> & {
76
+ /** Always `'pending'`: the operation has not finished. */
77
+ outcome: 'pending';
78
+ };
79
+ //# sourceMappingURL=audit-record.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-record.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/audit-record.interface.ts"],"names":[],"mappings":"AAEA,mDAAmD;AACnD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAEzE,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAEjD,0EAA0E;AAC1E,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,yEAAyE;AACzE,MAAM,WAAW,UAAU;IACzB,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,QAAQ,EAAE,aAAa,CAAC;IACxB,iDAAiD;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,mEAAmE;IACnE,WAAW,EAAE,gBAAgB,CAAC;IAC9B,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,WAAW,EACX,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,CAChD,GAAG;IACF,0DAA0D;IAC1D,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC"}
@@ -0,0 +1,3 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export {};
3
+ //# sourceMappingURL=audit-record.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-record.interface.js","sourceRoot":"","sources":["../../src/interfaces/audit-record.interface.ts"],"names":[],"mappings":"AAAA,sEAAsE"}
@@ -0,0 +1,37 @@
1
+ import type { AuditRecord, AuditStartRecord } from './audit-record.interface.js';
2
+ /**
3
+ * Backend-agnostic sink for audit records — the single seam every storage
4
+ * backend implements.
5
+ *
6
+ * Implement this once per backend; the bundled implementations are
7
+ * {@link LogAuditSink} (default, zero-dependency) and
8
+ * {@link PostgresAuditSink}. Because {@link AuditBehavior} depends only on this
9
+ * interface, swapping the backend (Postgres, an event store, Kafka, an HTTP
10
+ * collector, …) is a one-line change: the sink passed to its constructor.
11
+ */
12
+ export interface AuditSink {
13
+ /**
14
+ * Persist / publish a single audit record.
15
+ *
16
+ * Implementations should be resilient. A throw here is logged by the
17
+ * behavior; with `failOpen: false` it also fails a successful request. A
18
+ * handler error is always rethrown unchanged.
19
+ *
20
+ * @param record - The completed audit entry to forward.
21
+ */
22
+ write(record: AuditRecord): Promise<void> | void;
23
+ /**
24
+ * Persist the start of an operation, before its handler runs. Optional:
25
+ * when implemented, {@link AuditBehavior} calls it first (unless
26
+ * `recordStart: false`), then calls {@link write} with the final record
27
+ * under the same `id`, which must replace this one. An attempt interrupted
28
+ * by a process stop then stays visible as `'pending'` instead of being lost.
29
+ *
30
+ * A throw here is logged by the behavior; with `failOpen: false` it also
31
+ * stops the request before its handler runs.
32
+ *
33
+ * @param record - The pending entry for the operation about to run.
34
+ */
35
+ begin?(record: AuditStartRecord): Promise<void> | void;
36
+ }
37
+ //# sourceMappingURL=audit-sink.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-sink.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/audit-sink.interface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEjD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD"}
@@ -0,0 +1,3 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ export {};
3
+ //# sourceMappingURL=audit-sink.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-sink.interface.js","sourceRoot":"","sources":["../../src/interfaces/audit-sink.interface.ts"],"names":[],"mappings":"AAAA,sEAAsE"}
@@ -0,0 +1,33 @@
1
+ import type { AuditRecord } from '../interfaces/audit-record.interface.js';
2
+ import type { AuditSink } from '../interfaces/audit-sink.interface.js';
3
+ /** Minimal structural logger satisfied by the NestJS `Logger` and `console`. */
4
+ export interface AuditLoggerLike {
5
+ log(message: string): void;
6
+ warn(message: string): void;
7
+ }
8
+ /** Options for {@link LogAuditSink}. */
9
+ export interface LogAuditSinkOptions {
10
+ /** Logger to write to. Default: the global `console`. */
11
+ logger?: AuditLoggerLike;
12
+ /** Pretty-print the JSON record over multiple lines. Default `false`. */
13
+ pretty?: boolean;
14
+ }
15
+ /**
16
+ * Zero-dependency {@link AuditSink} that writes each record as a JSON line to a
17
+ * logger (the global `console` by default). The default sink — ideal for local
18
+ * development, tests, or shipping audit logs through your existing log pipeline.
19
+ *
20
+ * Failures are routed to `warn`, successes to `log`, so they are easy to filter.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * * new LogAuditSink({ logger: console, pretty: true });
25
+ * ```
26
+ */
27
+ export declare class LogAuditSink implements AuditSink {
28
+ private readonly logger;
29
+ private readonly pretty;
30
+ constructor(options?: LogAuditSinkOptions);
31
+ write(record: AuditRecord): void;
32
+ }
33
+ //# sourceMappingURL=log.sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.sink.d.ts","sourceRoot":"","sources":["../../src/sinks/log.sink.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAEvE,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IAEjC,YAAY,OAAO,GAAE,mBAAwB,EAG5C;IAED,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAQ/B;CACF"}
@@ -0,0 +1,31 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { stringifyAuditValue } from '../helpers/json.js';
3
+ /**
4
+ * Zero-dependency {@link AuditSink} that writes each record as a JSON line to a
5
+ * logger (the global `console` by default). The default sink — ideal for local
6
+ * development, tests, or shipping audit logs through your existing log pipeline.
7
+ *
8
+ * Failures are routed to `warn`, successes to `log`, so they are easy to filter.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * * new LogAuditSink({ logger: console, pretty: true });
13
+ * ```
14
+ */
15
+ export class LogAuditSink {
16
+ logger;
17
+ pretty;
18
+ constructor(options = {}) {
19
+ this.logger = options.logger ?? console;
20
+ this.pretty = options.pretty ?? false;
21
+ }
22
+ write(record) {
23
+ const line = stringifyAuditValue(record, this.pretty ? 2 : undefined);
24
+ if (record.outcome === 'failure') {
25
+ this.logger.warn(line);
26
+ return;
27
+ }
28
+ this.logger.log(line);
29
+ }
30
+ }
31
+ //# sourceMappingURL=log.sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.sink.js","sourceRoot":"","sources":["../../src/sinks/log.sink.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAkBzD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,YAAY;IACN,MAAM,CAAkB;IACxB,MAAM,CAAU;IAEjC,YAAY,OAAO,GAAwB,EAAE;QAC3C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,MAAmB;QACvB,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAEtE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,69 @@
1
+ import type { AuditRecord, AuditStartRecord } from '../interfaces/audit-record.interface.js';
2
+ import type { AuditSink } from '../interfaces/audit-sink.interface.js';
3
+ /**
4
+ * Minimal structural shape of a `pg` `Pool` / `Client`. Declared locally so this
5
+ * package does not hard-depend on `pg` — a real `Pool` or `Client` satisfies it.
6
+ * Add it in your app: `pnpm add pg`.
7
+ */
8
+ export interface PostgresQueryableLike {
9
+ query(text: string, values?: unknown[]): Promise<unknown>;
10
+ }
11
+ /** Options for {@link PostgresAuditSink}. */
12
+ export interface PostgresAuditSinkOptions {
13
+ /**
14
+ * Destination table, optionally schema-qualified (e.g. `audit.audit_log`).
15
+ * Default `'audit_log'`. Validated as a safe SQL identifier.
16
+ */
17
+ table?: string;
18
+ }
19
+ /**
20
+ * SQL to create the audit table. Run once in a migration.
21
+ *
22
+ * A row whose `outcome` is `'pending'` is an operation that started and has no
23
+ * final record: `duration_ms` and `completed_at` stay null. An old pending row
24
+ * marks an attempt interrupted by a process stop, whose outcome is unknown:
25
+ *
26
+ * ```sql
27
+ * SELECT * FROM audit_log
28
+ * WHERE outcome = 'pending' AND occurred_at < now() - interval '1 hour';
29
+ * ```
30
+ *
31
+ * @param table - Table name (validated). Default `'audit_log'`.
32
+ */
33
+ export declare function createAuditTableSql(table?: string): string;
34
+ /**
35
+ * {@link AuditSink} backed by **Postgres** (`pg`) — a drop-in replacement for
36
+ * the console sink. {@link begin} inserts a pending row when an operation
37
+ * starts, and {@link write} completes it under the same id (or inserts it, when
38
+ * no pending row exists), so an operation interrupted by a process stop stays
39
+ * visible as `'pending'`.
40
+ *
41
+ * Create the table once with {@link createAuditTableSql}. The table name is
42
+ * validated as a plain SQL identifier (it is interpolated, not parameterized);
43
+ * all record values are passed as bound parameters. A NUL character or a lone
44
+ * surrogate, which `jsonb` rejects, is stored as U+FFFD (see
45
+ * `toPostgresJson`), so one such character cannot lose the whole record.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { Pool } from 'pg';
50
+ * const pool = new Pool({ connectionString: process.env.DATABASE_URL });
51
+ * await pool.query(createAuditTableSql());
52
+ * const sink = new PostgresAuditSink(pool);
53
+ * ```
54
+ */
55
+ export declare class PostgresAuditSink implements AuditSink {
56
+ private readonly db;
57
+ private readonly beginSql;
58
+ private readonly writeSql;
59
+ constructor(db: PostgresQueryableLike, options?: PostgresAuditSinkOptions);
60
+ /** Inserts the pending row of an operation about to run. */
61
+ begin(record: AuditStartRecord): Promise<void>;
62
+ /**
63
+ * Inserts the final row, or completes the pending row that {@link begin}
64
+ * inserted under the same id.
65
+ */
66
+ write(record: AuditRecord): Promise<void>;
67
+ private values;
68
+ }
69
+ //# sourceMappingURL=postgres.sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.sink.d.ts","sourceRoot":"","sources":["../../src/sinks/postgres.sink.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAEvE;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,SAAc,GAAG,MAAM,CAoB/D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IAK/C,OAAO,CAAC,QAAQ,CAAC,EAAE;IAJrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YACmB,EAAE,EAAE,qBAAqB,EAC1C,OAAO,GAAE,wBAA6B,EAmBvC;IAED,4DAA4D;IACtD,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnD;IAED;;;OAGG;IACG,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9C;IAED,OAAO,CAAC,MAAM;CA6Bf"}