@nest-toolbox/typeorm-audit-log 1.8.1

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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/audit-context.d.ts +67 -0
  4. package/dist/audit-context.js +96 -0
  5. package/dist/audit-context.js.map +1 -0
  6. package/dist/audit-context.middleware.d.ts +76 -0
  7. package/dist/audit-context.middleware.js +91 -0
  8. package/dist/audit-context.middleware.js.map +1 -0
  9. package/dist/audit-log.entity.d.ts +56 -0
  10. package/dist/audit-log.entity.js +89 -0
  11. package/dist/audit-log.entity.js.map +1 -0
  12. package/dist/audit-log.module.d.ts +56 -0
  13. package/dist/audit-log.module.js +130 -0
  14. package/dist/audit-log.module.js.map +1 -0
  15. package/dist/audit-log.service.d.ts +43 -0
  16. package/dist/audit-log.service.js +177 -0
  17. package/dist/audit-log.service.js.map +1 -0
  18. package/dist/audit-log.subscriber.d.ts +66 -0
  19. package/dist/audit-log.subscriber.js +135 -0
  20. package/dist/audit-log.subscriber.js.map +1 -0
  21. package/dist/constants.d.ts +32 -0
  22. package/dist/constants.js +49 -0
  23. package/dist/constants.js.map +1 -0
  24. package/dist/decorators.d.ts +109 -0
  25. package/dist/decorators.js +161 -0
  26. package/dist/decorators.js.map +1 -0
  27. package/dist/index.d.ts +14 -0
  28. package/dist/index.js +31 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/tsconfig.build.tsbuildinfo +1 -0
  31. package/dist/types.d.ts +161 -0
  32. package/dist/types.js +16 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/utils/diff.d.ts +16 -0
  35. package/dist/utils/diff.js +142 -0
  36. package/dist/utils/diff.js.map +1 -0
  37. package/dist/utils/mask.d.ts +15 -0
  38. package/dist/utils/mask.js +69 -0
  39. package/dist/utils/mask.js.map +1 -0
  40. package/package.json +63 -0
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AuditLogSubscriber = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const decorators_1 = require("./decorators");
12
+ const types_1 = require("./types");
13
+ /**
14
+ * TypeORM event subscriber that captures entity changes and logs them.
15
+ * Automatically detects entities decorated with @Auditable() and creates
16
+ * audit log entries for INSERT, UPDATE, and DELETE operations.
17
+ *
18
+ * This subscriber is designed to be instantiated by NestJS DI and then
19
+ * registered with the TypeORM DataSource.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // In your module
24
+ * @Module({
25
+ * providers: [
26
+ * {
27
+ * provide: AuditLogSubscriber,
28
+ * useFactory: (dataSource: DataSource, auditLogService: AuditLogService) => {
29
+ * const subscriber = new AuditLogSubscriber(auditLogService);
30
+ * dataSource.subscribers.push(subscriber);
31
+ * return subscriber;
32
+ * },
33
+ * inject: [DataSource, AuditLogService],
34
+ * },
35
+ * ],
36
+ * })
37
+ * ```
38
+ */
39
+ let AuditLogSubscriber = class AuditLogSubscriber {
40
+ constructor() {
41
+ this.auditLogService = null;
42
+ }
43
+ /**
44
+ * Set the audit log service. Called during module initialization.
45
+ */
46
+ setAuditLogService(service) {
47
+ this.auditLogService = service;
48
+ }
49
+ /**
50
+ * Register this subscriber with a TypeORM DataSource.
51
+ */
52
+ registerWithDataSource(dataSource) {
53
+ if (!dataSource.subscribers.includes(this)) {
54
+ dataSource.subscribers.push(this);
55
+ }
56
+ }
57
+ /**
58
+ * Handle entity insertion events.
59
+ */
60
+ afterInsert(event) {
61
+ this.handleEvent(event, types_1.AuditAction.CREATE);
62
+ }
63
+ /**
64
+ * Handle entity update events.
65
+ */
66
+ afterUpdate(event) {
67
+ this.handleEvent(event, types_1.AuditAction.UPDATE, event.databaseEntity);
68
+ }
69
+ /**
70
+ * Handle entity removal events.
71
+ */
72
+ afterRemove(event) {
73
+ this.handleEvent(event, types_1.AuditAction.DELETE, event.databaseEntity);
74
+ }
75
+ /**
76
+ * Common handler for all entity events.
77
+ */
78
+ handleEvent(event, action,
79
+ // biome-ignore lint/suspicious/noExplicitAny: old values can be any entity type
80
+ oldValues) {
81
+ const entity = event.entity;
82
+ // Skip if no entity or service not initialized
83
+ if (!entity || !this.auditLogService) {
84
+ return;
85
+ }
86
+ // Skip if entity is not marked as auditable
87
+ if (!(0, decorators_1.isAuditable)(entity.constructor)) {
88
+ return;
89
+ }
90
+ // Get entity name (custom or class name)
91
+ const entityName = (0, decorators_1.getAuditEntityName)(entity.constructor);
92
+ // Get entity ID from primary key
93
+ const entityId = this.getEntityId(event, entity);
94
+ // Log the audit entry (fire and forget - don't block the transaction)
95
+ this.auditLogService
96
+ .log({
97
+ action,
98
+ entity,
99
+ entityName,
100
+ entityId,
101
+ oldValues,
102
+ })
103
+ .catch((error) => {
104
+ // Log error but don't throw - audit failures shouldn't break the app
105
+ console.error('[AuditLogSubscriber] Failed to log audit entry:', error);
106
+ });
107
+ }
108
+ /**
109
+ * Extract the entity ID from the event or entity.
110
+ */
111
+ getEntityId(event,
112
+ // biome-ignore lint/suspicious/noExplicitAny: entity can be any type
113
+ entity) {
114
+ var _a, _b, _c;
115
+ // Try to get ID from entity metadata (primary columns)
116
+ const primaryColumns = event.metadata.primaryColumns;
117
+ if (primaryColumns.length === 1) {
118
+ const primaryColumn = primaryColumns[0];
119
+ const value = entity[primaryColumn.propertyName];
120
+ return String(value !== null && value !== void 0 ? value : 'unknown');
121
+ }
122
+ // For composite keys, join them
123
+ if (primaryColumns.length > 1) {
124
+ const ids = primaryColumns.map((col) => entity[col.propertyName]);
125
+ return ids.join(':');
126
+ }
127
+ // Fallback: try common ID property names
128
+ return String((_c = (_b = (_a = entity.id) !== null && _a !== void 0 ? _a : entity.uuid) !== null && _b !== void 0 ? _b : entity._id) !== null && _c !== void 0 ? _c : 'unknown');
129
+ }
130
+ };
131
+ exports.AuditLogSubscriber = AuditLogSubscriber;
132
+ exports.AuditLogSubscriber = AuditLogSubscriber = __decorate([
133
+ (0, common_1.Injectable)()
134
+ ], AuditLogSubscriber);
135
+ //# sourceMappingURL=audit-log.subscriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-log.subscriber.js","sourceRoot":"","sources":["../src/audit-log.subscriber.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAE5C,6CAA+D;AAC/D,mCAAsD;AAUtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAAxB;QACG,oBAAe,GAA4B,IAAI,CAAC;IA0G1D,CAAC;IAxGC;;OAEG;IACH,kBAAkB,CAAC,OAAyB;QAC1C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,sBAAsB,CAAC,UAAsB;QAC3C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,KAA2B;QACrC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,KAA2B;QACrC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAW,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,KAA2B;QACrC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAW,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACK,WAAW,CACjB,KAAyE,EACzE,MAAmB;IACnB,gFAAgF;IAChF,SAAe;QAEf,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAE5B,+CAA+C;QAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,4CAA4C;QAC5C,IAAI,CAAC,IAAA,wBAAW,EAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,yCAAyC;QACzC,MAAM,UAAU,GAAG,IAAA,+BAAkB,EAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAE1D,iCAAiC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEjD,sEAAsE;QACtE,IAAI,CAAC,eAAe;aACjB,GAAG,CAAC;YACH,MAAM;YACN,MAAM;YACN,UAAU;YACV,QAAQ;YACR,SAAS;SACV,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,qEAAqE;YACrE,OAAO,CAAC,KAAK,CAAC,iDAAiD,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACK,WAAW,CACjB,KAAyE;IACzE,qEAAqE;IACrE,MAAW;;QAEX,uDAAuD;QACvD,MAAM,cAAc,GAAG,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QACrD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;YACjD,OAAO,MAAM,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,SAAS,CAAC,CAAC;QACpC,CAAC;QAED,gCAAgC;QAChC,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YAClE,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QAED,yCAAyC;QACzC,OAAO,MAAM,CAAC,MAAA,MAAA,MAAA,MAAM,CAAC,EAAE,mCAAI,MAAM,CAAC,IAAI,mCAAI,MAAM,CAAC,GAAG,mCAAI,SAAS,CAAC,CAAC;IACrE,CAAC;CACF,CAAA;AA3GY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;GACA,kBAAkB,CA2G9B"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Constants for @nest-toolbox/typeorm-audit-log
3
+ */
4
+ /**
5
+ * Metadata key for @Auditable() decorator
6
+ */
7
+ export declare const AUDITABLE_KEY = "audit:auditable";
8
+ /**
9
+ * Metadata key for @AuditIgnore() decorator
10
+ */
11
+ export declare const AUDIT_IGNORE_KEY = "audit:ignore";
12
+ /**
13
+ * Metadata key for @AuditMask() decorator
14
+ */
15
+ export declare const AUDIT_MASK_KEY = "audit:mask";
16
+ /**
17
+ * Metadata key for auditable options
18
+ */
19
+ export declare const AUDITABLE_OPTIONS_KEY = "audit:auditable:options";
20
+ /**
21
+ * Injection token for module options
22
+ */
23
+ export declare const AUDIT_LOG_OPTIONS = "AUDIT_LOG_OPTIONS";
24
+ /**
25
+ * Default table name for audit logs
26
+ */
27
+ export declare const DEFAULT_TABLE_NAME = "audit_logs";
28
+ /**
29
+ * Default mask function - masks middle characters
30
+ * Example: "john@email.com" -> "jo***ail.com"
31
+ */
32
+ export declare const DEFAULT_MASK_FN: (value: any) => string;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * Constants for @nest-toolbox/typeorm-audit-log
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_MASK_FN = exports.DEFAULT_TABLE_NAME = exports.AUDIT_LOG_OPTIONS = exports.AUDITABLE_OPTIONS_KEY = exports.AUDIT_MASK_KEY = exports.AUDIT_IGNORE_KEY = exports.AUDITABLE_KEY = void 0;
7
+ /**
8
+ * Metadata key for @Auditable() decorator
9
+ */
10
+ exports.AUDITABLE_KEY = 'audit:auditable';
11
+ /**
12
+ * Metadata key for @AuditIgnore() decorator
13
+ */
14
+ exports.AUDIT_IGNORE_KEY = 'audit:ignore';
15
+ /**
16
+ * Metadata key for @AuditMask() decorator
17
+ */
18
+ exports.AUDIT_MASK_KEY = 'audit:mask';
19
+ /**
20
+ * Metadata key for auditable options
21
+ */
22
+ exports.AUDITABLE_OPTIONS_KEY = 'audit:auditable:options';
23
+ /**
24
+ * Injection token for module options
25
+ */
26
+ exports.AUDIT_LOG_OPTIONS = 'AUDIT_LOG_OPTIONS';
27
+ /**
28
+ * Default table name for audit logs
29
+ */
30
+ exports.DEFAULT_TABLE_NAME = 'audit_logs';
31
+ /**
32
+ * Default mask function - masks middle characters
33
+ * Example: "john@email.com" -> "jo***ail.com"
34
+ */
35
+ // biome-ignore lint/suspicious/noExplicitAny: mask function accepts any value type
36
+ const DEFAULT_MASK_FN = (value) => {
37
+ if (value === null || value === undefined) {
38
+ return String(value);
39
+ }
40
+ const str = String(value);
41
+ if (str.length <= 4) {
42
+ return '***';
43
+ }
44
+ const visibleStart = Math.ceil(str.length * 0.2);
45
+ const visibleEnd = Math.ceil(str.length * 0.2);
46
+ return `${str.slice(0, visibleStart)}***${str.slice(str.length - visibleEnd)}`;
47
+ };
48
+ exports.DEFAULT_MASK_FN = DEFAULT_MASK_FN;
49
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH;;GAEG;AACU,QAAA,aAAa,GAAG,iBAAiB,CAAC;AAE/C;;GAEG;AACU,QAAA,gBAAgB,GAAG,cAAc,CAAC;AAE/C;;GAEG;AACU,QAAA,cAAc,GAAG,YAAY,CAAC;AAE3C;;GAEG;AACU,QAAA,qBAAqB,GAAG,yBAAyB,CAAC;AAE/D;;GAEG;AACU,QAAA,iBAAiB,GAAG,mBAAmB,CAAC;AAErD;;GAEG;AACU,QAAA,kBAAkB,GAAG,YAAY,CAAC;AAE/C;;;GAGG;AACH,mFAAmF;AAC5E,MAAM,eAAe,GAAG,CAAC,KAAU,EAAU,EAAE;IACpD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAE/C,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC;AACjF,CAAC,CAAC;AAdW,QAAA,eAAe,mBAc1B"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Decorators for @nest-toolbox/typeorm-audit-log
3
+ */
4
+ import 'reflect-metadata';
5
+ import type { AuditableOptions, AuditMaskOptions } from './types';
6
+ type Constructor = Function;
7
+ /**
8
+ * Marks an entity class as auditable.
9
+ * All changes to this entity will be automatically logged.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * @Entity()
14
+ * @Auditable()
15
+ * export class User {
16
+ * @PrimaryGeneratedColumn()
17
+ * id: number;
18
+ *
19
+ * @Column()
20
+ * name: string;
21
+ * }
22
+ * ```
23
+ *
24
+ * @example With options
25
+ * ```typescript
26
+ * @Entity()
27
+ * @Auditable({
28
+ * entityName: 'UserAccount',
29
+ * excludeFields: ['lastLoginAt']
30
+ * })
31
+ * export class User {
32
+ * // ...
33
+ * }
34
+ * ```
35
+ */
36
+ export declare function Auditable(options?: AuditableOptions): ClassDecorator;
37
+ /**
38
+ * Check if an entity class is marked as auditable
39
+ */
40
+ export declare function isAuditable(target: Constructor): boolean;
41
+ /**
42
+ * Get auditable options for an entity class
43
+ */
44
+ export declare function getAuditableOptions(target: Constructor): AuditableOptions | undefined;
45
+ /**
46
+ * Get the entity name for auditing (custom name or class name)
47
+ */
48
+ export declare function getAuditEntityName(target: Constructor): string;
49
+ /**
50
+ * Get fields excluded from auditing for an entity
51
+ */
52
+ export declare function getExcludedFields(target: Constructor): string[];
53
+ /**
54
+ * Marks a property to be excluded from audit logs entirely.
55
+ * Use for sensitive data that should never appear in audit trails.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * @Entity()
60
+ * @Auditable()
61
+ * export class User {
62
+ * @Column()
63
+ * @AuditIgnore()
64
+ * password: string;
65
+ *
66
+ * @Column()
67
+ * @AuditIgnore()
68
+ * refreshToken: string;
69
+ * }
70
+ * ```
71
+ */
72
+ export declare function AuditIgnore(): PropertyDecorator;
73
+ /**
74
+ * Get fields marked with @AuditIgnore for an entity
75
+ */
76
+ export declare function getIgnoredFields(target: Constructor): (string | symbol)[];
77
+ /**
78
+ * Marks a property to be masked in audit logs.
79
+ * The value will be partially hidden (e.g., "jo***@email.com").
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * @Entity()
84
+ * @Auditable()
85
+ * export class User {
86
+ * @Column()
87
+ * @AuditMask()
88
+ * email: string;
89
+ *
90
+ * @Column()
91
+ * @AuditMask({ maskFn: (v) => v ? '****' : null })
92
+ * ssn: string;
93
+ * }
94
+ * ```
95
+ */
96
+ export declare function AuditMask(options?: AuditMaskOptions): PropertyDecorator;
97
+ /**
98
+ * Get masked fields and their mask functions for an entity
99
+ */
100
+ export declare function getMaskedFields(target: Constructor): Record<string | symbol, (value: any) => string>;
101
+ /**
102
+ * Check if a field should be ignored for an entity
103
+ */
104
+ export declare function isFieldIgnored(target: Constructor, field: string | symbol): boolean;
105
+ /**
106
+ * Get the mask function for a field, if any
107
+ */
108
+ export declare function getFieldMaskFn(target: Constructor, field: string | symbol): ((value: any) => string) | undefined;
109
+ export {};
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Decorators for @nest-toolbox/typeorm-audit-log
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Auditable = Auditable;
7
+ exports.isAuditable = isAuditable;
8
+ exports.getAuditableOptions = getAuditableOptions;
9
+ exports.getAuditEntityName = getAuditEntityName;
10
+ exports.getExcludedFields = getExcludedFields;
11
+ exports.AuditIgnore = AuditIgnore;
12
+ exports.getIgnoredFields = getIgnoredFields;
13
+ exports.AuditMask = AuditMask;
14
+ exports.getMaskedFields = getMaskedFields;
15
+ exports.isFieldIgnored = isFieldIgnored;
16
+ exports.getFieldMaskFn = getFieldMaskFn;
17
+ require("reflect-metadata");
18
+ const constants_1 = require("./constants");
19
+ /**
20
+ * Marks an entity class as auditable.
21
+ * All changes to this entity will be automatically logged.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * @Entity()
26
+ * @Auditable()
27
+ * export class User {
28
+ * @PrimaryGeneratedColumn()
29
+ * id: number;
30
+ *
31
+ * @Column()
32
+ * name: string;
33
+ * }
34
+ * ```
35
+ *
36
+ * @example With options
37
+ * ```typescript
38
+ * @Entity()
39
+ * @Auditable({
40
+ * entityName: 'UserAccount',
41
+ * excludeFields: ['lastLoginAt']
42
+ * })
43
+ * export class User {
44
+ * // ...
45
+ * }
46
+ * ```
47
+ */
48
+ function Auditable(options) {
49
+ return (target) => {
50
+ Reflect.defineMetadata(constants_1.AUDITABLE_KEY, true, target);
51
+ if (options) {
52
+ Reflect.defineMetadata(constants_1.AUDITABLE_OPTIONS_KEY, options, target);
53
+ }
54
+ };
55
+ }
56
+ /**
57
+ * Check if an entity class is marked as auditable
58
+ */
59
+ function isAuditable(target) {
60
+ return Reflect.getMetadata(constants_1.AUDITABLE_KEY, target) === true;
61
+ }
62
+ /**
63
+ * Get auditable options for an entity class
64
+ */
65
+ function getAuditableOptions(target) {
66
+ return Reflect.getMetadata(constants_1.AUDITABLE_OPTIONS_KEY, target);
67
+ }
68
+ /**
69
+ * Get the entity name for auditing (custom name or class name)
70
+ */
71
+ function getAuditEntityName(target) {
72
+ const options = getAuditableOptions(target);
73
+ return (options === null || options === void 0 ? void 0 : options.entityName) || target.name;
74
+ }
75
+ /**
76
+ * Get fields excluded from auditing for an entity
77
+ */
78
+ function getExcludedFields(target) {
79
+ const options = getAuditableOptions(target);
80
+ return (options === null || options === void 0 ? void 0 : options.excludeFields) || [];
81
+ }
82
+ /**
83
+ * Marks a property to be excluded from audit logs entirely.
84
+ * Use for sensitive data that should never appear in audit trails.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * @Entity()
89
+ * @Auditable()
90
+ * export class User {
91
+ * @Column()
92
+ * @AuditIgnore()
93
+ * password: string;
94
+ *
95
+ * @Column()
96
+ * @AuditIgnore()
97
+ * refreshToken: string;
98
+ * }
99
+ * ```
100
+ */
101
+ function AuditIgnore() {
102
+ return (target, propertyKey) => {
103
+ const existingIgnored = Reflect.getMetadata(constants_1.AUDIT_IGNORE_KEY, target.constructor) || [];
104
+ Reflect.defineMetadata(constants_1.AUDIT_IGNORE_KEY, [...existingIgnored, propertyKey], target.constructor);
105
+ };
106
+ }
107
+ /**
108
+ * Get fields marked with @AuditIgnore for an entity
109
+ */
110
+ function getIgnoredFields(target) {
111
+ return Reflect.getMetadata(constants_1.AUDIT_IGNORE_KEY, target) || [];
112
+ }
113
+ /**
114
+ * Marks a property to be masked in audit logs.
115
+ * The value will be partially hidden (e.g., "jo***@email.com").
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * @Entity()
120
+ * @Auditable()
121
+ * export class User {
122
+ * @Column()
123
+ * @AuditMask()
124
+ * email: string;
125
+ *
126
+ * @Column()
127
+ * @AuditMask({ maskFn: (v) => v ? '****' : null })
128
+ * ssn: string;
129
+ * }
130
+ * ```
131
+ */
132
+ function AuditMask(options) {
133
+ return (target, propertyKey) => {
134
+ const existingMasked = Reflect.getMetadata(constants_1.AUDIT_MASK_KEY, target.constructor) || {};
135
+ const maskFn = (options === null || options === void 0 ? void 0 : options.maskFn) || constants_1.DEFAULT_MASK_FN;
136
+ Reflect.defineMetadata(constants_1.AUDIT_MASK_KEY, Object.assign(Object.assign({}, existingMasked), { [propertyKey]: maskFn }), target.constructor);
137
+ };
138
+ }
139
+ /**
140
+ * Get masked fields and their mask functions for an entity
141
+ */
142
+ // biome-ignore lint/suspicious/noExplicitAny: mask functions can return any value
143
+ function getMaskedFields(target) {
144
+ return Reflect.getMetadata(constants_1.AUDIT_MASK_KEY, target) || {};
145
+ }
146
+ /**
147
+ * Check if a field should be ignored for an entity
148
+ */
149
+ function isFieldIgnored(target, field) {
150
+ const ignoredFields = getIgnoredFields(target);
151
+ return ignoredFields.includes(field);
152
+ }
153
+ /**
154
+ * Get the mask function for a field, if any
155
+ */
156
+ // biome-ignore lint/suspicious/noExplicitAny: mask functions can return any value
157
+ function getFieldMaskFn(target, field) {
158
+ const maskedFields = getMaskedFields(target);
159
+ return maskedFields[field];
160
+ }
161
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @nest-toolbox/typeorm-audit-log
3
+ *
4
+ * Automatic audit logging for TypeORM entities with user attribution and diff tracking.
5
+ */
6
+ export * from './audit-context';
7
+ export * from './audit-context.middleware';
8
+ export * from './audit-log.entity';
9
+ export * from './audit-log.module';
10
+ export * from './audit-log.service';
11
+ export * from './audit-log.subscriber';
12
+ export * from './constants';
13
+ export * from './decorators';
14
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * @nest-toolbox/typeorm-audit-log
4
+ *
5
+ * Automatic audit logging for TypeORM entities with user attribution and diff tracking.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ __exportStar(require("./audit-context"), exports);
23
+ __exportStar(require("./audit-context.middleware"), exports);
24
+ __exportStar(require("./audit-log.entity"), exports);
25
+ __exportStar(require("./audit-log.module"), exports);
26
+ __exportStar(require("./audit-log.service"), exports);
27
+ __exportStar(require("./audit-log.subscriber"), exports);
28
+ __exportStar(require("./constants"), exports);
29
+ __exportStar(require("./decorators"), exports);
30
+ __exportStar(require("./types"), exports);
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;AAEH,kDAAgC;AAChC,6DAA2C;AAC3C,qDAAmC;AACnC,qDAAmC;AACnC,sDAAoC;AACpC,yDAAuC;AACvC,8CAA4B;AAC5B,+CAA6B;AAC7B,0CAAwB"}