@geekmidas/audit 0.0.8 → 0.2.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 (64) hide show
  1. package/README.md +397 -0
  2. package/dist/{Auditor-D3me-qKX.d.mts → Auditor-_Dn2dp8d.d.mts} +46 -1
  3. package/dist/Auditor-_Dn2dp8d.d.mts.map +1 -0
  4. package/dist/{Auditor-QYUMGJCH.d.cts → Auditor-sW7YAJIA.d.cts} +46 -1
  5. package/dist/Auditor-sW7YAJIA.d.cts.map +1 -0
  6. package/dist/Auditor.d.cts +1 -1
  7. package/dist/Auditor.d.mts +1 -1
  8. package/dist/DefaultAuditor-BAVnNmRh.mjs.map +1 -1
  9. package/dist/{DefaultAuditor-BTuMMiWh.d.cts → DefaultAuditor-BSYMwojG.d.cts} +3 -2
  10. package/dist/DefaultAuditor-BSYMwojG.d.cts.map +1 -0
  11. package/dist/{DefaultAuditor-C1FWrJg6.d.mts → DefaultAuditor-CYE-uAry.d.mts} +3 -2
  12. package/dist/DefaultAuditor-CYE-uAry.d.mts.map +1 -0
  13. package/dist/DefaultAuditor-JbZ_BQfh.cjs.map +1 -1
  14. package/dist/DefaultAuditor.d.cts +2 -2
  15. package/dist/DefaultAuditor.d.mts +2 -2
  16. package/dist/cache-2VI80Vao.d.mts +96 -0
  17. package/dist/cache-2VI80Vao.d.mts.map +1 -0
  18. package/dist/cache-BbIl31RL.mjs +167 -0
  19. package/dist/cache-BbIl31RL.mjs.map +1 -0
  20. package/dist/cache-CDHqXqcl.d.cts +96 -0
  21. package/dist/cache-CDHqXqcl.d.cts.map +1 -0
  22. package/dist/cache-l7jAptBl.cjs +173 -0
  23. package/dist/cache-l7jAptBl.cjs.map +1 -0
  24. package/dist/cache.cjs +3 -0
  25. package/dist/cache.d.cts +3 -0
  26. package/dist/cache.d.mts +3 -0
  27. package/dist/cache.mjs +3 -0
  28. package/dist/index.d.cts +2 -2
  29. package/dist/index.d.mts +2 -2
  30. package/dist/kysely.cjs +24 -0
  31. package/dist/kysely.cjs.map +1 -1
  32. package/dist/kysely.d.cts +11 -1
  33. package/dist/kysely.d.cts.map +1 -0
  34. package/dist/kysely.d.mts +11 -1
  35. package/dist/kysely.d.mts.map +1 -0
  36. package/dist/kysely.mjs +24 -0
  37. package/dist/kysely.mjs.map +1 -1
  38. package/dist/memory.cjs +49 -0
  39. package/dist/memory.cjs.map +1 -0
  40. package/dist/memory.d.cts +44 -0
  41. package/dist/memory.d.cts.map +1 -0
  42. package/dist/memory.d.mts +44 -0
  43. package/dist/memory.d.mts.map +1 -0
  44. package/dist/memory.mjs +48 -0
  45. package/dist/memory.mjs.map +1 -0
  46. package/dist/storage.d.cts +1 -1
  47. package/dist/storage.d.mts +1 -1
  48. package/dist/types.d.cts +1 -1
  49. package/dist/types.d.mts +1 -1
  50. package/package.json +33 -5
  51. package/src/Auditor.ts +121 -121
  52. package/src/DefaultAuditor.ts +91 -91
  53. package/src/__tests__/CacheAuditStorage.spec.ts +382 -0
  54. package/src/__tests__/DefaultAuditor.spec.ts +520 -462
  55. package/src/__tests__/InMemoryAuditStorage.spec.ts +317 -0
  56. package/src/__tests__/KyselyAuditStorage.integration.spec.ts +500 -496
  57. package/src/__tests__/KyselyAuditStorage.spec.ts +433 -433
  58. package/src/cache.ts +263 -0
  59. package/src/index.ts +14 -15
  60. package/src/kysely.ts +292 -259
  61. package/src/memory.ts +50 -0
  62. package/src/storage.ts +132 -85
  63. package/src/types.ts +73 -74
  64. package/tsconfig.json +9 -0
package/src/Auditor.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type {
2
- AuditActor,
3
- AuditMetadata,
4
- AuditOptions,
5
- AuditRecord,
6
- AuditableAction,
7
- ExtractAuditPayload,
8
- ExtractAuditType,
2
+ AuditActor,
3
+ AuditableAction,
4
+ AuditMetadata,
5
+ AuditOptions,
6
+ AuditRecord,
7
+ ExtractAuditPayload,
8
+ ExtractAuditType,
9
9
  } from './types';
10
10
 
11
11
  /**
@@ -31,127 +31,127 @@ import type {
31
31
  * ```
32
32
  */
33
33
  export interface Auditor<
34
- TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
35
- string,
36
- unknown
37
- >,
38
- TTransaction = unknown,
34
+ TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
35
+ string,
36
+ unknown
37
+ >,
38
+ TTransaction = unknown,
39
39
  > {
40
- /**
41
- * The actor for all audits in this context.
42
- * Set at construction time, immutable throughout the request.
43
- */
44
- readonly actor: AuditActor;
40
+ /**
41
+ * The actor for all audits in this context.
42
+ * Set at construction time, immutable throughout the request.
43
+ */
44
+ readonly actor: AuditActor;
45
45
 
46
- /**
47
- * Record a type-safe audit entry.
48
- * The payload type is inferred from the audit type.
49
- *
50
- * @param type - The audit type (must be a valid type from TAuditAction)
51
- * @param payload - The audit payload (shape enforced by type)
52
- * @param options - Optional audit metadata
53
- *
54
- * @example
55
- * ```typescript
56
- * auditor.audit('user.created', {
57
- * userId: '123',
58
- * email: 'test@example.com',
59
- * });
60
- *
61
- * auditor.audit('order.placed', {
62
- * orderId: 'order-456',
63
- * total: 99.99,
64
- * }, {
65
- * entityId: 'order-456',
66
- * table: 'orders',
67
- * });
68
- * ```
69
- */
70
- audit<TType extends ExtractAuditType<TAuditAction>>(
71
- type: TType,
72
- payload: ExtractAuditPayload<TAuditAction, TType>,
73
- options?: AuditOptions,
74
- ): void;
46
+ /**
47
+ * Record a type-safe audit entry.
48
+ * The payload type is inferred from the audit type.
49
+ *
50
+ * @param type - The audit type (must be a valid type from TAuditAction)
51
+ * @param payload - The audit payload (shape enforced by type)
52
+ * @param options - Optional audit metadata
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * auditor.audit('user.created', {
57
+ * userId: '123',
58
+ * email: 'test@example.com',
59
+ * });
60
+ *
61
+ * auditor.audit('order.placed', {
62
+ * orderId: 'order-456',
63
+ * total: 99.99,
64
+ * }, {
65
+ * entityId: 'order-456',
66
+ * table: 'orders',
67
+ * });
68
+ * ```
69
+ */
70
+ audit<TType extends ExtractAuditType<TAuditAction>>(
71
+ type: TType,
72
+ payload: ExtractAuditPayload<TAuditAction, TType>,
73
+ options?: AuditOptions,
74
+ ): void;
75
75
 
76
- /**
77
- * Record a raw audit record.
78
- * Use this when you need full control over the audit structure,
79
- * bypassing type safety.
80
- *
81
- * @param record - The audit record (id, timestamp, actor added automatically)
82
- */
83
- record(record: Omit<AuditRecord, 'id' | 'timestamp' | 'actor'>): void;
76
+ /**
77
+ * Record a raw audit record.
78
+ * Use this when you need full control over the audit structure,
79
+ * bypassing type safety.
80
+ *
81
+ * @param record - The audit record (id, timestamp, actor added automatically)
82
+ */
83
+ record(record: Omit<AuditRecord, 'id' | 'timestamp' | 'actor'>): void;
84
84
 
85
- /**
86
- * Get all collected audit records.
87
- * Useful for inspection or custom processing.
88
- */
89
- getRecords(): AuditRecord[];
85
+ /**
86
+ * Get all collected audit records.
87
+ * Useful for inspection or custom processing.
88
+ */
89
+ getRecords(): AuditRecord[];
90
90
 
91
- /**
92
- * Flush all collected audits to storage.
93
- * Called automatically by the endpoint adaptor inside the transaction.
94
- *
95
- * @param trx - Optional transaction context for atomic writes
96
- */
97
- flush(trx?: TTransaction): Promise<void>;
91
+ /**
92
+ * Flush all collected audits to storage.
93
+ * Called automatically by the endpoint adaptor inside the transaction.
94
+ *
95
+ * @param trx - Optional transaction context for atomic writes
96
+ */
97
+ flush(trx?: TTransaction): Promise<void>;
98
98
 
99
- /**
100
- * Clear all collected audit records without flushing.
101
- * Use with caution - collected audits will be lost.
102
- */
103
- clear(): void;
99
+ /**
100
+ * Clear all collected audit records without flushing.
101
+ * Use with caution - collected audits will be lost.
102
+ */
103
+ clear(): void;
104
104
 
105
- /**
106
- * Add metadata to all future audit records.
107
- * Merges with existing metadata (new values override existing).
108
- * Typically called by adaptors to add request context.
109
- *
110
- * @param metadata - Metadata to add (requestId, endpoint, method, ip, etc.)
111
- *
112
- * @example
113
- * ```typescript
114
- * // In endpoint adaptor
115
- * auditor.addMetadata({
116
- * requestId: 'req-123',
117
- * endpoint: '/users',
118
- * method: 'POST',
119
- * ip: '192.168.1.1',
120
- * });
121
- * ```
122
- */
123
- addMetadata(metadata: AuditMetadata): void;
105
+ /**
106
+ * Add metadata to all future audit records.
107
+ * Merges with existing metadata (new values override existing).
108
+ * Typically called by adaptors to add request context.
109
+ *
110
+ * @param metadata - Metadata to add (requestId, endpoint, method, ip, etc.)
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * // In endpoint adaptor
115
+ * auditor.addMetadata({
116
+ * requestId: 'req-123',
117
+ * endpoint: '/users',
118
+ * method: 'POST',
119
+ * ip: '192.168.1.1',
120
+ * });
121
+ * ```
122
+ */
123
+ addMetadata(metadata: AuditMetadata): void;
124
124
 
125
- /**
126
- * Set the transaction context for audit flushing.
127
- * When set, flush() will use this transaction instead of requiring
128
- * it to be passed explicitly. This enables declarative audits to
129
- * participate in the same transaction as the handler's database operations.
130
- *
131
- * @param trx - The transaction context (e.g., Kysely Transaction)
132
- *
133
- * @example
134
- * ```typescript
135
- * // In handler with explicit transaction management
136
- * const result = await withTransaction(services.database.raw, async (trx) => {
137
- * // Register transaction with auditor so declarative audits use it
138
- * auditor.setTransaction(trx);
139
- *
140
- * const user = await trx.insertInto('users').values(data).returningAll().executeTakeFirstOrThrow();
141
- *
142
- * // Manual audits will also use this transaction when flush() is called
143
- * auditor.audit('user.created', { userId: user.id });
144
- *
145
- * return user;
146
- * });
147
- * // After handler, adaptor calls auditor.flush() which uses the stored transaction
148
- * ```
149
- */
150
- setTransaction(trx: TTransaction): void;
125
+ /**
126
+ * Set the transaction context for audit flushing.
127
+ * When set, flush() will use this transaction instead of requiring
128
+ * it to be passed explicitly. This enables declarative audits to
129
+ * participate in the same transaction as the handler's database operations.
130
+ *
131
+ * @param trx - The transaction context (e.g., Kysely Transaction)
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // In handler with explicit transaction management
136
+ * const result = await withTransaction(services.database.raw, async (trx) => {
137
+ * // Register transaction with auditor so declarative audits use it
138
+ * auditor.setTransaction(trx);
139
+ *
140
+ * const user = await trx.insertInto('users').values(data).returningAll().executeTakeFirstOrThrow();
141
+ *
142
+ * // Manual audits will also use this transaction when flush() is called
143
+ * auditor.audit('user.created', { userId: user.id });
144
+ *
145
+ * return user;
146
+ * });
147
+ * // After handler, adaptor calls auditor.flush() which uses the stored transaction
148
+ * ```
149
+ */
150
+ setTransaction(trx: TTransaction): void;
151
151
 
152
- /**
153
- * Get the currently set transaction context.
154
- * Returns undefined if no transaction has been set.
155
- */
156
- getTransaction(): TTransaction | undefined;
152
+ /**
153
+ * Get the currently set transaction context.
154
+ * Returns undefined if no transaction has been set.
155
+ */
156
+ getTransaction(): TTransaction | undefined;
157
157
  }
@@ -2,27 +2,27 @@ import { nanoid } from 'nanoid';
2
2
  import type { Auditor } from './Auditor';
3
3
  import type { AuditStorage } from './storage';
4
4
  import type {
5
- AuditActor,
6
- AuditMetadata,
7
- AuditOptions,
8
- AuditRecord,
9
- AuditableAction,
10
- ExtractAuditPayload,
11
- ExtractAuditType,
5
+ AuditActor,
6
+ AuditableAction,
7
+ AuditMetadata,
8
+ AuditOptions,
9
+ AuditRecord,
10
+ ExtractAuditPayload,
11
+ ExtractAuditType,
12
12
  } from './types';
13
13
 
14
14
  /**
15
15
  * Configuration for DefaultAuditor.
16
16
  */
17
17
  export interface DefaultAuditorConfig {
18
- /** The actor performing audits (set at construction, immutable) */
19
- actor: AuditActor;
20
- /** Storage backend for persisting audits */
21
- storage: AuditStorage;
22
- /** Optional metadata to attach to all audits */
23
- metadata?: AuditMetadata;
24
- /** Optional custom ID generator (defaults to nanoid) */
25
- generateId?: () => string;
18
+ /** The actor performing audits (set at construction, immutable) */
19
+ actor: AuditActor;
20
+ /** Storage backend for persisting audits */
21
+ storage: AuditStorage;
22
+ /** Optional metadata to attach to all audits */
23
+ metadata?: AuditMetadata;
24
+ /** Optional custom ID generator (defaults to nanoid) */
25
+ generateId?: () => string;
26
26
  }
27
27
 
28
28
  /**
@@ -47,95 +47,95 @@ export interface DefaultAuditorConfig {
47
47
  * ```
48
48
  */
49
49
  export class DefaultAuditor<
50
- TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
51
- string,
52
- unknown
53
- >,
54
- TTransaction = unknown,
50
+ TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
51
+ string,
52
+ unknown
53
+ >,
54
+ TTransaction = unknown,
55
55
  > implements Auditor<TAuditAction, TTransaction>
56
56
  {
57
- readonly actor: AuditActor;
58
- private readonly storage: AuditStorage;
59
- private metadata?: AuditMetadata;
60
- private readonly generateId: () => string;
61
- private records: AuditRecord[] = [];
62
- private transaction?: TTransaction;
57
+ readonly actor: AuditActor;
58
+ private readonly storage: AuditStorage;
59
+ private metadata?: AuditMetadata;
60
+ private readonly generateId: () => string;
61
+ private records: AuditRecord[] = [];
62
+ private transaction?: TTransaction;
63
63
 
64
- constructor(config: DefaultAuditorConfig) {
65
- this.actor = config.actor;
66
- this.storage = config.storage;
67
- this.metadata = config.metadata;
68
- this.generateId = config.generateId ?? (() => nanoid());
69
- }
64
+ constructor(config: DefaultAuditorConfig) {
65
+ this.actor = config.actor;
66
+ this.storage = config.storage;
67
+ this.metadata = config.metadata;
68
+ this.generateId = config.generateId ?? (() => nanoid());
69
+ }
70
70
 
71
- audit<TType extends ExtractAuditType<TAuditAction>>(
72
- type: TType,
73
- payload: ExtractAuditPayload<TAuditAction, TType>,
74
- options?: AuditOptions,
75
- ): void {
76
- const record: AuditRecord = {
77
- id: this.generateId(),
78
- type,
79
- operation: options?.operation ?? 'CUSTOM',
80
- table: options?.table,
81
- entityId: options?.entityId,
82
- oldValues: options?.oldValues,
83
- newValues: options?.newValues,
84
- payload,
85
- timestamp: new Date(),
86
- actor: this.actor,
87
- metadata: this.metadata,
88
- };
71
+ audit<TType extends ExtractAuditType<TAuditAction>>(
72
+ type: TType,
73
+ payload: ExtractAuditPayload<TAuditAction, TType>,
74
+ options?: AuditOptions,
75
+ ): void {
76
+ const record: AuditRecord = {
77
+ id: this.generateId(),
78
+ type,
79
+ operation: options?.operation ?? 'CUSTOM',
80
+ table: options?.table,
81
+ entityId: options?.entityId,
82
+ oldValues: options?.oldValues,
83
+ newValues: options?.newValues,
84
+ payload,
85
+ timestamp: new Date(),
86
+ actor: this.actor,
87
+ metadata: this.metadata,
88
+ };
89
89
 
90
- this.records.push(record);
91
- }
90
+ this.records.push(record);
91
+ }
92
92
 
93
- record(record: Omit<AuditRecord, 'id' | 'timestamp' | 'actor'>): void {
94
- const fullRecord: AuditRecord = {
95
- ...record,
96
- id: this.generateId(),
97
- timestamp: new Date(),
98
- actor: this.actor,
99
- metadata: this.metadata
100
- ? { ...this.metadata, ...record.metadata }
101
- : record.metadata,
102
- };
93
+ record(record: Omit<AuditRecord, 'id' | 'timestamp' | 'actor'>): void {
94
+ const fullRecord: AuditRecord = {
95
+ ...record,
96
+ id: this.generateId(),
97
+ timestamp: new Date(),
98
+ actor: this.actor,
99
+ metadata: this.metadata
100
+ ? { ...this.metadata, ...record.metadata }
101
+ : record.metadata,
102
+ };
103
103
 
104
- this.records.push(fullRecord);
105
- }
104
+ this.records.push(fullRecord);
105
+ }
106
106
 
107
- getRecords(): AuditRecord[] {
108
- return [...this.records];
109
- }
107
+ getRecords(): AuditRecord[] {
108
+ return [...this.records];
109
+ }
110
110
 
111
- async flush(trx?: TTransaction): Promise<void> {
112
- if (this.records.length === 0) {
113
- return;
114
- }
111
+ async flush(trx?: TTransaction): Promise<void> {
112
+ if (this.records.length === 0) {
113
+ return;
114
+ }
115
115
 
116
- const recordsToFlush = [...this.records];
117
- this.records = [];
116
+ const recordsToFlush = [...this.records];
117
+ this.records = [];
118
118
 
119
- // Use explicitly passed transaction, or fall back to stored transaction
120
- const transactionToUse = trx ?? this.transaction;
121
- await this.storage.write(recordsToFlush, transactionToUse);
122
- }
119
+ // Use explicitly passed transaction, or fall back to stored transaction
120
+ const transactionToUse = trx ?? this.transaction;
121
+ await this.storage.write(recordsToFlush, transactionToUse);
122
+ }
123
123
 
124
- setTransaction(trx: TTransaction): void {
125
- this.transaction = trx;
126
- }
124
+ setTransaction(trx: TTransaction): void {
125
+ this.transaction = trx;
126
+ }
127
127
 
128
- getTransaction(): TTransaction | undefined {
129
- return this.transaction;
130
- }
128
+ getTransaction(): TTransaction | undefined {
129
+ return this.transaction;
130
+ }
131
131
 
132
- clear(): void {
133
- this.records = [];
134
- }
132
+ clear(): void {
133
+ this.records = [];
134
+ }
135
135
 
136
- addMetadata(metadata: AuditMetadata): void {
137
- this.metadata = this.metadata
138
- ? { ...this.metadata, ...metadata }
139
- : metadata;
140
- }
136
+ addMetadata(metadata: AuditMetadata): void {
137
+ this.metadata = this.metadata
138
+ ? { ...this.metadata, ...metadata }
139
+ : metadata;
140
+ }
141
141
  }