@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
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @cqrs-ddd/pipeline-audit
2
+
3
+ Audit records for `@cqrs-ddd/pipeline`: for each audited operation, who did what, in
4
+ which tenant and correlation, with which outcome, and with request and response payloads
5
+ redacted. `LogAuditSink` writes to a logger (`console` by default) and
6
+ `PostgresAuditSink` to a table; any `AuditSink` with `write()` works.
7
+
8
+ **Documentation:** [guide](https://aristoteliss.github.io/ddd-cqrs/packages/pipeline-audit/) · [API reference](https://aristoteliss.github.io/ddd-cqrs/api/cqrs-ddd/pipeline-audit/) · [all packages](https://aristoteliss.github.io/ddd-cqrs/)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pnpm add @cqrs-ddd/pipeline-audit @cqrs-ddd/pipeline
14
+ ```
15
+
16
+ Requires Node.js 22.12 or later. Published as an ES module; a CommonJS application loads
17
+ it with `require()`.
18
+
19
+ ## Example
20
+
21
+ ```ts
22
+ const pipeline = createPipeline({
23
+ behaviors: [new AuditBehavior(new LogAuditSink())],
24
+ });
25
+
26
+ export const renameUser = pipeline.wrap(
27
+ { name: 'renameUser', kind: 'command' },
28
+ audit({ action: 'user.rename', severity: 'high', actor: (c) => ({ id: currentUserId(c) }) }),
29
+ )(async (input: { id: string; name: string }) => users.rename(input));
30
+ ```
31
+
32
+ ## License
33
+
34
+ Dual-licensed under **AGPLv3** and a **Commercial License**. See `LICENSE` and
35
+ `COMMERCIAL_LICENSE.txt`.
@@ -0,0 +1,102 @@
1
+ import { type IPipelineBehavior, type IPipelineBehaviorContract, type IPipelineBehaviorOptionsResolver, type IPipelineContext, type NextDelegate, PIPELINE_BEHAVIOR_CONTRACT, type PipelineItemToken, type PipelineLogger } from '@cqrs-ddd/pipeline';
2
+ import type { AuditBehaviorOptions } from './interfaces/audit-options.interface.js';
3
+ import type { AuditRecord, AuditStartRecord } from './interfaces/audit-record.interface.js';
4
+ import type { AuditSink } from './interfaces/audit-sink.interface.js';
5
+ /**
6
+ * Unique symbol key used on `context.items` to store or retrieve the produced {@link AuditRecord}.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const record = context.items.get(AUDIT_RECORD_ITEM) as AuditRecord | undefined;
11
+ * ```
12
+ */
13
+ export declare const AUDIT_RECORD_ITEM: unique symbol;
14
+ /**
15
+ * Typed token for {@link AUDIT_RECORD_ITEM}: the produced audit record. Reads and writes the same
16
+ * `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
17
+ */
18
+ export declare const AUDIT_RECORD_ITEM_TOKEN: PipelineItemToken<AuditRecord>;
19
+ /**
20
+ * Typed token for the pending {@link AuditStartRecord}, set before the handler
21
+ * runs when a start record is written. A step inside the handler can read it,
22
+ * for example to store the record in its own transaction.
23
+ */
24
+ export declare const AUDIT_START_RECORD_ITEM_TOKEN: PipelineItemToken<AuditStartRecord>;
25
+ /**
26
+ * Pipeline behavior that writes an {@link AuditRecord} for every audited
27
+ * request — **on both success and failure** — to a pluggable {@link AuditSink}.
28
+ * Only commands are audited unless `captureKinds` lists other request kinds.
29
+ *
30
+ * For each run it times the handler, resolves the actor and action, redacts the
31
+ * payload (and optionally the response), then forwards the record. When the
32
+ * sink implements `begin`, a pending start record is written first under the
33
+ * same id, so a process stop during the handler leaves a pending record
34
+ * instead of none; with `failOpen: false`, a failed start write stops the
35
+ * request before its handler runs. Handler
36
+ * failures are recorded before propagation. With the default `failOpen: true`,
37
+ * sink failures are logged and the original handler result/error is preserved.
38
+ * With `failOpen: false`, a sink/build failure on the **success path** fails the
39
+ * request. If the handler already failed, the caller receives the handler's own
40
+ * error, unchanged, and the audit failure is logged.
41
+ *
42
+ * Sink-agnostic by design: it depends only on {@link AuditSink}, so the backend
43
+ * (console, Postgres, an event store, …) is a one-line swap: the sink passed
44
+ * to the constructor. Record-building failures are logged and ignored
45
+ * in fail-open mode, or propagated according to the same fail-closed semantics.
46
+ *
47
+ * **Ordering:** place this near the **outside** of the chain (e.g. global
48
+ * `before`) so the duration covers the whole handler, and after any auth
49
+ * behavior that populates `context.items` for the {@link AuditBehaviorOptions.actor}
50
+ * factory.
51
+ *
52
+ * @example Per-handler, with an actor read from an upstream auth behavior
53
+ * ```ts
54
+ * class DeleteUserHandler {
55
+ * @pipeline.wrap({ kind: 'command' }, [AuditBehavior, {
56
+ * action: 'user.delete',
57
+ * severity: 'high',
58
+ * actor: (c) => ({ id: c.items.get('currentUserId') }),
59
+ * }])
60
+ * async handle(command: DeleteUserCommand) {}
61
+ * }
62
+ * ```
63
+ *
64
+ * @example Compliance-sensitive fail-closed auditing
65
+ * ```ts
66
+ * class RefundPaymentHandler {
67
+ * @pipeline.wrap({ kind: 'command' }, [AuditBehavior, {
68
+ * action: 'payment.refund',
69
+ * failOpen: false,
70
+ * captureResponse: false,
71
+ * }])
72
+ * async handle(command: RefundPaymentCommand) {}
73
+ * }
74
+ * ```
75
+ */
76
+ export declare class AuditBehavior implements IPipelineBehavior, IPipelineBehaviorOptionsResolver<AuditBehaviorOptions> {
77
+ private readonly sink;
78
+ static readonly [PIPELINE_BEHAVIOR_CONTRACT]: IPipelineBehaviorContract;
79
+ private readonly logger;
80
+ private readonly defaults;
81
+ constructor(sink: AuditSink, defaults?: AuditBehaviorOptions, logger?: PipelineLogger);
82
+ handle(context: IPipelineContext, next: NextDelegate): Promise<unknown>;
83
+ /**
84
+ * Build the start record, forward it to the sink's `begin`, and stash it on
85
+ * the context. Throws only when failing closed, before the handler runs.
86
+ */
87
+ private begin;
88
+ /** Build the record, forward it to the sink, and stash it on the context. */
89
+ private record;
90
+ /**
91
+ * Rejects a non-function factory option before request execution when
92
+ * `failOpen` is false, and logs it otherwise.
93
+ */
94
+ private validateFactories;
95
+ /** Logs an audit failure, then fails open or rethrows `error`. */
96
+ private reportFailure;
97
+ /** Diagnostic logging never changes the request outcome. */
98
+ private diagnose;
99
+ /** Shallow-merges per-handler options over the constructor defaults. */
100
+ resolveEffectiveOptions(options?: AuditBehaviorOptions): AuditBehaviorOptions;
101
+ }
102
+ //# sourceMappingURL=audit.behavior.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.behavior.d.ts","sourceRoot":"","sources":["../src/audit.behavior.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gCAAgC,EACrC,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,0BAA0B,EAG1B,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,EACV,WAAW,EAEX,gBAAgB,EACjB,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEtE;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,iBAAiB,CAAC,WAAW,CACM,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,iBAAiB,CAAC,gBAAgB,CACb,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,qBAAa,aACX,YACE,iBAAiB,EACjB,gCAAgC,CAAC,oBAAoB,CAAC;IAyBtD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAvBvB,MAAM,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,EAAE,yBAAyB,CAiBrE;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAEhD,YACmB,IAAI,EAAE,SAAS,EAChC,QAAQ,CAAC,EAAE,oBAAoB,EAC/B,MAAM,CAAC,EAAE,cAAc,EAUxB;IAEK,MAAM,CACV,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,CAAC,CA+DlB;IAED;;;OAGG;YACW,KAAK;IAkCnB,6EAA6E;YAC/D,MAAM;IAmCpB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB,kEAAkE;IAClE,OAAO,CAAC,aAAa;IAarB,4DAA4D;IAC5D,OAAO,CAAC,QAAQ;IAQhB,wEAAwE;IACxE,uBAAuB,CACrB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,oBAAoB,CAGtB;CACF"}
@@ -0,0 +1,262 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { createPipelineItem, PIPELINE_BEHAVIOR_CONTRACT, setPipelineItem, } from '@cqrs-ddd/pipeline';
3
+ import { uuidv7 } from '@cqrs-ddd/uuidv7';
4
+ import { buildAuditRecord, buildAuditStartRecord, } from './helpers/build-record.js';
5
+ /**
6
+ * Unique symbol key used on `context.items` to store or retrieve the produced {@link AuditRecord}.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const record = context.items.get(AUDIT_RECORD_ITEM) as AuditRecord | undefined;
11
+ * ```
12
+ */
13
+ export const AUDIT_RECORD_ITEM = Symbol('AUDIT_RECORD_ITEM');
14
+ /**
15
+ * Typed token for {@link AUDIT_RECORD_ITEM}: the produced audit record. Reads and writes the same
16
+ * `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
17
+ */
18
+ export const AUDIT_RECORD_ITEM_TOKEN = createPipelineItem('AUDIT_RECORD_ITEM', AUDIT_RECORD_ITEM);
19
+ /**
20
+ * Typed token for the pending {@link AuditStartRecord}, set before the handler
21
+ * runs when a start record is written. A step inside the handler can read it,
22
+ * for example to store the record in its own transaction.
23
+ */
24
+ export const AUDIT_START_RECORD_ITEM_TOKEN = createPipelineItem('AUDIT_START_RECORD_ITEM');
25
+ /**
26
+ * Pipeline behavior that writes an {@link AuditRecord} for every audited
27
+ * request — **on both success and failure** — to a pluggable {@link AuditSink}.
28
+ * Only commands are audited unless `captureKinds` lists other request kinds.
29
+ *
30
+ * For each run it times the handler, resolves the actor and action, redacts the
31
+ * payload (and optionally the response), then forwards the record. When the
32
+ * sink implements `begin`, a pending start record is written first under the
33
+ * same id, so a process stop during the handler leaves a pending record
34
+ * instead of none; with `failOpen: false`, a failed start write stops the
35
+ * request before its handler runs. Handler
36
+ * failures are recorded before propagation. With the default `failOpen: true`,
37
+ * sink failures are logged and the original handler result/error is preserved.
38
+ * With `failOpen: false`, a sink/build failure on the **success path** fails the
39
+ * request. If the handler already failed, the caller receives the handler's own
40
+ * error, unchanged, and the audit failure is logged.
41
+ *
42
+ * Sink-agnostic by design: it depends only on {@link AuditSink}, so the backend
43
+ * (console, Postgres, an event store, …) is a one-line swap: the sink passed
44
+ * to the constructor. Record-building failures are logged and ignored
45
+ * in fail-open mode, or propagated according to the same fail-closed semantics.
46
+ *
47
+ * **Ordering:** place this near the **outside** of the chain (e.g. global
48
+ * `before`) so the duration covers the whole handler, and after any auth
49
+ * behavior that populates `context.items` for the {@link AuditBehaviorOptions.actor}
50
+ * factory.
51
+ *
52
+ * @example Per-handler, with an actor read from an upstream auth behavior
53
+ * ```ts
54
+ * class DeleteUserHandler {
55
+ * @pipeline.wrap({ kind: 'command' }, [AuditBehavior, {
56
+ * action: 'user.delete',
57
+ * severity: 'high',
58
+ * actor: (c) => ({ id: c.items.get('currentUserId') }),
59
+ * }])
60
+ * async handle(command: DeleteUserCommand) {}
61
+ * }
62
+ * ```
63
+ *
64
+ * @example Compliance-sensitive fail-closed auditing
65
+ * ```ts
66
+ * class RefundPaymentHandler {
67
+ * @pipeline.wrap({ kind: 'command' }, [AuditBehavior, {
68
+ * action: 'payment.refund',
69
+ * failOpen: false,
70
+ * captureResponse: false,
71
+ * }])
72
+ * async handle(command: RefundPaymentCommand) {}
73
+ * }
74
+ * ```
75
+ */
76
+ export class AuditBehavior {
77
+ sink;
78
+ static [PIPELINE_BEHAVIOR_CONTRACT] = {
79
+ validate: (context) => {
80
+ const invalid = findInvalidFactory((context.effectiveOptions ?? {}));
81
+ if (!invalid)
82
+ return undefined;
83
+ return [
84
+ {
85
+ handlerName: context.handlerName,
86
+ behaviorName: AuditBehavior.name,
87
+ message: invalid,
88
+ fix: 'Pass functions for the actor, metadata and redact options of AuditBehavior.',
89
+ },
90
+ ];
91
+ },
92
+ };
93
+ logger;
94
+ defaults;
95
+ constructor(sink, defaults, logger) {
96
+ this.sink = sink;
97
+ if (typeof sink?.write !== 'function') {
98
+ throw new TypeError('AuditBehavior requires an audit sink with a write() method, such as new LogAuditSink().');
99
+ }
100
+ this.defaults = defaults ?? {};
101
+ this.logger = logger ?? console;
102
+ }
103
+ async handle(context, next) {
104
+ const options = this.resolveEffectiveOptions(context.getBehaviorOptions(AuditBehavior));
105
+ const captureKinds = options.captureKinds ?? DEFAULT_CAPTURE_KINDS;
106
+ if (!captureKinds.includes(context.requestKind)) {
107
+ return next();
108
+ }
109
+ const failOpen = options.failOpen ?? true;
110
+ this.validateFactories(options, failOpen);
111
+ const startedAt = new Date();
112
+ const id = uuidv7();
113
+ const begin = options.recordStart === false
114
+ ? undefined
115
+ : this.sink.begin?.bind(this.sink);
116
+ if (begin) {
117
+ await this.begin(begin, {
118
+ context,
119
+ options,
120
+ id,
121
+ startedAt: startedAt.toISOString(),
122
+ });
123
+ }
124
+ const start = performance.now();
125
+ let response;
126
+ try {
127
+ response = await next();
128
+ }
129
+ catch (error) {
130
+ try {
131
+ await this.record({
132
+ context,
133
+ options,
134
+ id,
135
+ error,
136
+ failed: true,
137
+ durationMs: performance.now() - start,
138
+ startedAt: startedAt.toISOString(),
139
+ });
140
+ }
141
+ catch (recordError) {
142
+ this.diagnose('error', `Audit recording also failed after request error: ${recordError instanceof Error ? recordError.message : recordError}`);
143
+ }
144
+ throw error;
145
+ }
146
+ await this.record({
147
+ context,
148
+ options,
149
+ id,
150
+ response,
151
+ failed: false,
152
+ durationMs: performance.now() - start,
153
+ startedAt: startedAt.toISOString(),
154
+ });
155
+ return response;
156
+ }
157
+ /**
158
+ * Build the start record, forward it to the sink's `begin`, and stash it on
159
+ * the context. Throws only when failing closed, before the handler runs.
160
+ */
161
+ async begin(begin, input) {
162
+ const failOpen = input.options.failOpen ?? true;
163
+ let record;
164
+ try {
165
+ record = buildAuditStartRecord(input);
166
+ }
167
+ catch (buildError) {
168
+ const message = `Failed to build audit start record for ${input.context.requestName}: ` +
169
+ `${buildError instanceof Error ? buildError.message : buildError}`;
170
+ this.reportFailure(message, failOpen, buildError);
171
+ return;
172
+ }
173
+ setPipelineItem(input.context, AUDIT_START_RECORD_ITEM_TOKEN, record);
174
+ try {
175
+ await begin(record);
176
+ }
177
+ catch (sinkError) {
178
+ const message = `Failed to write audit start record for ${record.requestName} ` +
179
+ `(correlationId: ${record.correlationId}): ` +
180
+ `${sinkError instanceof Error ? sinkError.message : sinkError}`;
181
+ this.reportFailure(message, failOpen, sinkError);
182
+ }
183
+ }
184
+ /** Build the record, forward it to the sink, and stash it on the context. */
185
+ async record(input) {
186
+ const failOpen = input.options.failOpen ?? true;
187
+ let record;
188
+ try {
189
+ record = buildAuditRecord(input);
190
+ }
191
+ catch (buildError) {
192
+ const message = `Failed to build audit record for ${input.context.requestName}: ` +
193
+ `${buildError instanceof Error ? buildError.message : buildError}`;
194
+ this.reportFailure(message, failOpen, buildError);
195
+ return;
196
+ }
197
+ setPipelineItem(input.context, AUDIT_RECORD_ITEM_TOKEN, record);
198
+ try {
199
+ await this.sink.write(record);
200
+ }
201
+ catch (sinkError) {
202
+ const message = `Failed to write audit record for ${record.requestName} ` +
203
+ `(correlationId: ${record.correlationId}): ` +
204
+ `${sinkError instanceof Error ? sinkError.message : sinkError}`;
205
+ this.reportFailure(message, failOpen, sinkError);
206
+ }
207
+ }
208
+ /**
209
+ * Rejects a non-function factory option before request execution when
210
+ * `failOpen` is false, and logs it otherwise.
211
+ */
212
+ validateFactories(options, failOpen) {
213
+ const invalid = findInvalidFactory(options);
214
+ if (invalid)
215
+ this.reportFailure(invalid, failOpen, new TypeError(invalid));
216
+ }
217
+ /** Logs an audit failure, then fails open or rethrows `error`. */
218
+ reportFailure(message, failOpen, error) {
219
+ if (failOpen) {
220
+ this.diagnose('warn', `${message}; failing open`);
221
+ return;
222
+ }
223
+ this.diagnose('error', `${message}; failing closed`);
224
+ throw error;
225
+ }
226
+ /** Diagnostic logging never changes the request outcome. */
227
+ diagnose(level, message) {
228
+ try {
229
+ this.logger[level]?.(message, AuditBehavior.name);
230
+ }
231
+ catch {
232
+ // A failing logger must not override the fail-open/fail-closed decision.
233
+ }
234
+ }
235
+ /** Shallow-merges per-handler options over the constructor defaults. */
236
+ resolveEffectiveOptions(options) {
237
+ if (!options)
238
+ return this.defaults;
239
+ return { ...this.defaults, ...options };
240
+ }
241
+ }
242
+ /**
243
+ * Kinds audited when `captureKinds` is omitted: commands, which change state.
244
+ * A query changes nothing, and a domain event follows a command that is
245
+ * already audited.
246
+ */
247
+ const DEFAULT_CAPTURE_KINDS = ['command'];
248
+ const FACTORY_OPTIONS = [
249
+ ['actor', 'actor factory'],
250
+ ['metadata', 'metadata factory'],
251
+ ['redact', 'redactor'],
252
+ ];
253
+ function findInvalidFactory(options) {
254
+ for (const [option, label] of FACTORY_OPTIONS) {
255
+ const value = options[option];
256
+ if (value !== undefined && typeof value !== 'function') {
257
+ return `Invalid audit ${label}: expected a function, received ${typeof value}`;
258
+ }
259
+ }
260
+ return undefined;
261
+ }
262
+ //# sourceMappingURL=audit.behavior.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.behavior.js","sourceRoot":"","sources":["../src/audit.behavior.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Relative importance levels of an audited action.
3
+ */
4
+ export declare const AUDIT_SEVERITY: {
5
+ readonly LOW: 'low';
6
+ readonly MEDIUM: 'medium';
7
+ readonly HIGH: 'high';
8
+ readonly CRITICAL: 'critical';
9
+ };
10
+ /**
11
+ * Standard request kinds categorized by the pipeline.
12
+ */
13
+ export declare const AUDIT_REQUEST_KINDS: {
14
+ readonly COMMAND: 'command';
15
+ readonly QUERY: 'query';
16
+ readonly EVENT: 'event';
17
+ readonly UNKNOWN: 'unknown';
18
+ };
19
+ /**
20
+ * Audit record outcomes. `PENDING` is the outcome of a start record only.
21
+ */
22
+ export declare const AUDIT_OUTCOMES: {
23
+ readonly SUCCESS: 'success';
24
+ readonly FAILURE: 'failure';
25
+ readonly PENDING: 'pending';
26
+ };
27
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/constants/tokens.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,cAAc;aACzB,GAAG,EAAE,KAAK;aACV,MAAM,EAAE,QAAQ;aAChB,IAAI,EAAE,MAAM;aACZ,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,mBAAmB;aAC9B,OAAO,EAAE,SAAS;aAClB,KAAK,EAAE,OAAO;aACd,KAAK,EAAE,OAAO;aACd,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;aACzB,OAAO,EAAE,SAAS;aAClB,OAAO,EAAE,SAAS;aAClB,OAAO,EAAE,SAAS;CACV,CAAC"}
@@ -0,0 +1,28 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ /**
3
+ * Relative importance levels of an audited action.
4
+ */
5
+ export const AUDIT_SEVERITY = {
6
+ LOW: 'low',
7
+ MEDIUM: 'medium',
8
+ HIGH: 'high',
9
+ CRITICAL: 'critical',
10
+ };
11
+ /**
12
+ * Standard request kinds categorized by the pipeline.
13
+ */
14
+ export const AUDIT_REQUEST_KINDS = {
15
+ COMMAND: 'command',
16
+ QUERY: 'query',
17
+ EVENT: 'event',
18
+ UNKNOWN: 'unknown',
19
+ };
20
+ /**
21
+ * Audit record outcomes. `PENDING` is the outcome of a start record only.
22
+ */
23
+ export const AUDIT_OUTCOMES = {
24
+ SUCCESS: 'success',
25
+ FAILURE: 'failure',
26
+ PENDING: 'pending',
27
+ };
28
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/constants/tokens.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACV,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { PipelineBehaviorTuple } from '@cqrs-ddd/pipeline';
2
+ import { AuditBehavior } from '../audit.behavior.js';
3
+ import type { AuditBehaviorOptions } from '../interfaces/audit-options.interface.js';
4
+ export type AuditIntentOptions = AuditBehaviorOptions;
5
+ /**
6
+ * Returns an audit behavior entry for `pipeline.wrap()` or `createPipeline({ globalBehaviors })`.
7
+ * @param options Per-handler audit options; omitted fields use behavior defaults.
8
+ * @returns The behavior class and options tuple.
9
+ * @example
10
+ * ```ts
11
+ * @pipeline.wrap({ kind: 'command' }, audit({ action: 'user.delete', severity: 'high' }))
12
+ * ```
13
+ */
14
+ export declare function audit(options?: AuditIntentOptions): PipelineBehaviorTuple<AuditBehavior, AuditBehaviorOptions>;
15
+ //# sourceMappingURL=audit.intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.intent.d.ts","sourceRoot":"","sources":["../../src/helpers/audit.intent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAErF,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CACnB,OAAO,GAAE,kBAAuB,GAC/B,qBAAqB,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAE5D"}
@@ -0,0 +1,15 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { AuditBehavior } from '../audit.behavior.js';
3
+ /**
4
+ * Returns an audit behavior entry for `pipeline.wrap()` or `createPipeline({ globalBehaviors })`.
5
+ * @param options Per-handler audit options; omitted fields use behavior defaults.
6
+ * @returns The behavior class and options tuple.
7
+ * @example
8
+ * ```ts
9
+ * @pipeline.wrap({ kind: 'command' }, audit({ action: 'user.delete', severity: 'high' }))
10
+ * ```
11
+ */
12
+ export function audit(options = {}) {
13
+ return [AuditBehavior, options];
14
+ }
15
+ //# sourceMappingURL=audit.intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.intent.js","sourceRoot":"","sources":["../../src/helpers/audit.intent.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAKrD;;;;;;;;GAQG;AACH,MAAM,UAAU,KAAK,CACnB,OAAO,GAAuB,EAAE;IAEhC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { IPipelineContext } from '@cqrs-ddd/pipeline';
2
+ import type { AuditBehaviorOptions } from '../interfaces/audit-options.interface.js';
3
+ import type { AuditRecord, AuditStartRecord } from '../interfaces/audit-record.interface.js';
4
+ /** Inputs describing an operation about to run. */
5
+ export interface BuildAuditStartRecordInput {
6
+ /** The pipeline context of the audited request. */
7
+ context: IPipelineContext;
8
+ /** Effective, merged behavior options. */
9
+ options: AuditBehaviorOptions;
10
+ /** Record id. Default: a new UUIDv7. */
11
+ id?: string;
12
+ /** ISO-8601 start timestamp. */
13
+ startedAt: string;
14
+ }
15
+ /** Inputs describing the completed (or failed) pipeline run. */
16
+ export interface BuildAuditRecordInput extends BuildAuditStartRecordInput {
17
+ /** The handler return value (used only when `captureResponse` is on). */
18
+ response?: unknown;
19
+ /** The thrown value, if the handler failed. */
20
+ error?: unknown;
21
+ /** Explicit execution state; thrown `undefined` is still a failure. */
22
+ failed: boolean;
23
+ /** Wall-clock duration in milliseconds. */
24
+ durationMs: number;
25
+ }
26
+ /**
27
+ * Build the pending {@link AuditStartRecord} of an operation about to run:
28
+ * everything known before the handler, with `outcome: 'pending'`.
29
+ *
30
+ * Pass its `id` to {@link buildAuditRecord} so the final record replaces it.
31
+ */
32
+ export declare function buildAuditStartRecord(input: BuildAuditStartRecordInput): AuditStartRecord;
33
+ /**
34
+ * Build an {@link AuditRecord} from a completed pipeline run.
35
+ *
36
+ * Applies redaction to the payload/response, resolves the actor, severity, and
37
+ * action, and normalizes non-`Error` throws to a well-formed error shape. Atomic
38
+ * values are retained, so captured values must still satisfy the selected
39
+ * sink's serialization requirements.
40
+ */
41
+ export declare function buildAuditRecord(input: BuildAuditRecordInput): AuditRecord;
42
+ //# sourceMappingURL=build-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-record.d.ts","sourceRoot":"","sources":["../../src/helpers/build-record.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,KAAK,EAEV,WAAW,EAEX,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAGjD,mDAAmD;AACnD,MAAM,WAAW,0BAA0B;IACzC,mDAAmD;IACnD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,0CAA0C;IAC1C,OAAO,EAAE,oBAAoB,CAAC;IAC9B,wCAAwC;IACxC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAsB,SAAQ,0BAA0B;IACvE,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,0BAA0B,GAChC,gBAAgB,CAElB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAiB1E"}
@@ -0,0 +1,85 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { uuidv7 } from '@cqrs-ddd/uuidv7';
3
+ import { DEFAULT_REDACT_KEYS, redactValue } from './redact.js';
4
+ /**
5
+ * Build the pending {@link AuditStartRecord} of an operation about to run:
6
+ * everything known before the handler, with `outcome: 'pending'`.
7
+ *
8
+ * Pass its `id` to {@link buildAuditRecord} so the final record replaces it.
9
+ */
10
+ export function buildAuditStartRecord(input) {
11
+ return { ...buildCommonFields(input), outcome: 'pending' };
12
+ }
13
+ /**
14
+ * Build an {@link AuditRecord} from a completed pipeline run.
15
+ *
16
+ * Applies redaction to the payload/response, resolves the actor, severity, and
17
+ * action, and normalizes non-`Error` throws to a well-formed error shape. Atomic
18
+ * values are retained, so captured values must still satisfy the selected
19
+ * sink's serialization requirements.
20
+ */
21
+ export function buildAuditRecord(input) {
22
+ const { options, response, error, failed, durationMs } = input;
23
+ const record = {
24
+ ...buildCommonFields(input),
25
+ outcome: failed ? 'failure' : 'success',
26
+ durationMs,
27
+ };
28
+ if ((options.captureResponse ?? false) && !failed) {
29
+ record.response = sanitize(response, options);
30
+ }
31
+ if (failed) {
32
+ record.error = toAuditError(error, options);
33
+ }
34
+ return record;
35
+ }
36
+ /** The fields a start record and a final record share. */
37
+ function buildCommonFields(input) {
38
+ const { context, options, startedAt } = input;
39
+ const userMetadata = options.metadata?.(context);
40
+ return {
41
+ id: input.id ?? uuidv7(),
42
+ correlationId: context.correlationId,
43
+ tenantId: context.tenantId,
44
+ action: options.action ?? context.requestName,
45
+ severity: resolveSeverity(context, options),
46
+ actor: options.actor?.(context),
47
+ requestKind: context.requestKind,
48
+ requestName: context.requestName,
49
+ handlerName: context.handlerName,
50
+ timestamp: startedAt,
51
+ metadata: userMetadata || context.tenantId
52
+ ? {
53
+ ...(userMetadata ?? {}),
54
+ ...(context.tenantId ? { tenantId: context.tenantId } : {}),
55
+ }
56
+ : undefined,
57
+ ...((options.captureRequest ?? true)
58
+ ? { payload: sanitize(context.request, options) }
59
+ : {}),
60
+ };
61
+ }
62
+ /** Redact a value using a custom redactor or the default key-masking. */
63
+ function sanitize(value, options) {
64
+ if (options.redact)
65
+ return options.redact(value);
66
+ const keys = options.redactKeys
67
+ ? [...DEFAULT_REDACT_KEYS, ...options.redactKeys]
68
+ : DEFAULT_REDACT_KEYS;
69
+ return redactValue(value, keys);
70
+ }
71
+ /** Default severity: explicit option, else `'low'` for queries, `'medium'` otherwise. */
72
+ function resolveSeverity(context, options) {
73
+ if (options.severity)
74
+ return options.severity;
75
+ return context.requestKind === 'query' ? 'low' : 'medium';
76
+ }
77
+ function toAuditError(error, options) {
78
+ const isError = error instanceof Error;
79
+ return {
80
+ name: isError ? error.name : 'unknown',
81
+ message: isError ? error.message : String(error),
82
+ stack: options.includeStack === false || !isError ? undefined : error.stack,
83
+ };
84
+ }
85
+ //# sourceMappingURL=build-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-record.js","sourceRoot":"","sources":["../../src/helpers/build-record.ts"],"names":[],"mappings":"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"}