@geekmidas/audit 0.1.0 → 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 (55) hide show
  1. package/dist/{Auditor-DrR0ImvJ.d.cts → Auditor-_Dn2dp8d.d.mts} +4 -1
  2. package/dist/Auditor-_Dn2dp8d.d.mts.map +1 -0
  3. package/dist/{Auditor-Xf4Tp63p.d.mts → Auditor-sW7YAJIA.d.cts} +4 -1
  4. package/dist/Auditor-sW7YAJIA.d.cts.map +1 -0
  5. package/dist/Auditor.d.cts +1 -1
  6. package/dist/Auditor.d.mts +1 -1
  7. package/dist/DefaultAuditor-BAVnNmRh.mjs.map +1 -1
  8. package/dist/{DefaultAuditor-3Q1zVd1b.d.mts → DefaultAuditor-BSYMwojG.d.cts} +3 -2
  9. package/dist/DefaultAuditor-BSYMwojG.d.cts.map +1 -0
  10. package/dist/{DefaultAuditor-BaZa4u_m.d.cts → DefaultAuditor-CYE-uAry.d.mts} +3 -2
  11. package/dist/DefaultAuditor-CYE-uAry.d.mts.map +1 -0
  12. package/dist/DefaultAuditor-JbZ_BQfh.cjs.map +1 -1
  13. package/dist/DefaultAuditor.d.cts +2 -2
  14. package/dist/DefaultAuditor.d.mts +2 -2
  15. package/dist/{cache-0AjJ0zis.d.cts → cache-2VI80Vao.d.mts} +3 -2
  16. package/dist/cache-2VI80Vao.d.mts.map +1 -0
  17. package/dist/cache-BbIl31RL.mjs.map +1 -1
  18. package/dist/{cache-DBbGcCEq.d.mts → cache-CDHqXqcl.d.cts} +3 -2
  19. package/dist/cache-CDHqXqcl.d.cts.map +1 -0
  20. package/dist/cache-l7jAptBl.cjs.map +1 -1
  21. package/dist/cache.d.cts +2 -2
  22. package/dist/cache.d.mts +2 -2
  23. package/dist/index.d.cts +2 -2
  24. package/dist/index.d.mts +2 -2
  25. package/dist/kysely.cjs.map +1 -1
  26. package/dist/kysely.d.cts +2 -1
  27. package/dist/kysely.d.cts.map +1 -0
  28. package/dist/kysely.d.mts +2 -1
  29. package/dist/kysely.d.mts.map +1 -0
  30. package/dist/kysely.mjs.map +1 -1
  31. package/dist/memory.cjs.map +1 -1
  32. package/dist/memory.d.cts +3 -2
  33. package/dist/memory.d.cts.map +1 -0
  34. package/dist/memory.d.mts +3 -2
  35. package/dist/memory.d.mts.map +1 -0
  36. package/dist/memory.mjs.map +1 -1
  37. package/dist/storage.d.cts +1 -1
  38. package/dist/storage.d.mts +1 -1
  39. package/dist/types.d.cts +1 -1
  40. package/dist/types.d.mts +1 -1
  41. package/package.json +3 -3
  42. package/src/Auditor.ts +121 -121
  43. package/src/DefaultAuditor.ts +91 -91
  44. package/src/__tests__/CacheAuditStorage.spec.ts +375 -375
  45. package/src/__tests__/DefaultAuditor.spec.ts +520 -462
  46. package/src/__tests__/InMemoryAuditStorage.spec.ts +311 -311
  47. package/src/__tests__/KyselyAuditStorage.integration.spec.ts +496 -496
  48. package/src/__tests__/KyselyAuditStorage.spec.ts +433 -433
  49. package/src/cache.ts +179 -179
  50. package/src/index.ts +14 -15
  51. package/src/kysely.ts +292 -292
  52. package/src/memory.ts +11 -11
  53. package/src/storage.ts +131 -131
  54. package/src/types.ts +73 -74
  55. package/tsconfig.json +9 -0
package/src/cache.ts CHANGED
@@ -1,24 +1,24 @@
1
1
  import type { Cache } from '@geekmidas/cache';
2
2
  import type { AuditQueryOptions, AuditStorage } from './storage';
3
- import type { AuditRecord, AuditableAction } from './types';
3
+ import type { AuditableAction, AuditRecord } from './types';
4
4
 
5
5
  /**
6
6
  * Configuration for CacheAuditStorage.
7
7
  */
8
8
  export interface CacheAuditStorageConfig {
9
- /** Cache instance to use for storage */
10
- cache: Cache;
11
- /**
12
- * Key prefix for audit records.
13
- * Records are stored as `${prefix}:${id}`.
14
- * @default 'audit'
15
- */
16
- prefix?: string;
17
- /**
18
- * TTL (time-to-live) in seconds for audit records.
19
- * If not set, records use the cache's default TTL.
20
- */
21
- ttl?: number;
9
+ /** Cache instance to use for storage */
10
+ cache: Cache;
11
+ /**
12
+ * Key prefix for audit records.
13
+ * Records are stored as `${prefix}:${id}`.
14
+ * @default 'audit'
15
+ */
16
+ prefix?: string;
17
+ /**
18
+ * TTL (time-to-live) in seconds for audit records.
19
+ * If not set, records use the cache's default TTL.
20
+ */
21
+ ttl?: number;
22
22
  }
23
23
 
24
24
  /**
@@ -59,199 +59,199 @@ export interface CacheAuditStorageConfig {
59
59
  * ```
60
60
  */
61
61
  export class CacheAuditStorage<
62
- TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
63
- string,
64
- unknown
65
- >,
62
+ TAuditAction extends AuditableAction<string, unknown> = AuditableAction<
63
+ string,
64
+ unknown
65
+ >,
66
66
  > implements AuditStorage<TAuditAction>
67
67
  {
68
- private readonly cache: Cache;
69
- private readonly prefix: string;
70
- private readonly ttl?: number;
71
- private readonly indexKey: string;
68
+ private readonly cache: Cache;
69
+ private readonly prefix: string;
70
+ private readonly ttl?: number;
71
+ private readonly indexKey: string;
72
72
 
73
- constructor(config: CacheAuditStorageConfig) {
74
- this.cache = config.cache;
75
- this.prefix = config.prefix ?? 'audit';
76
- this.ttl = config.ttl;
77
- this.indexKey = `${this.prefix}:__index__`;
78
- }
73
+ constructor(config: CacheAuditStorageConfig) {
74
+ this.cache = config.cache;
75
+ this.prefix = config.prefix ?? 'audit';
76
+ this.ttl = config.ttl;
77
+ this.indexKey = `${this.prefix}:__index__`;
78
+ }
79
79
 
80
- /**
81
- * Write audit records to cache.
82
- */
83
- async write(records: AuditRecord[]): Promise<void> {
84
- if (records.length === 0) {
85
- return;
86
- }
80
+ /**
81
+ * Write audit records to cache.
82
+ */
83
+ async write(records: AuditRecord[]): Promise<void> {
84
+ if (records.length === 0) {
85
+ return;
86
+ }
87
87
 
88
- // Get existing index
89
- const existingIds = (await this.cache.get<string[]>(this.indexKey)) ?? [];
90
- const newIds: string[] = [];
88
+ // Get existing index
89
+ const existingIds = (await this.cache.get<string[]>(this.indexKey)) ?? [];
90
+ const newIds: string[] = [];
91
91
 
92
- // Write each record
93
- for (const record of records) {
94
- const key = this.getRecordKey(record.id);
95
- // Serialize Date to ISO string for cache storage
96
- const serialized = this.serializeRecord(record);
97
- await this.cache.set(key, serialized, this.ttl);
98
- newIds.push(record.id);
99
- }
92
+ // Write each record
93
+ for (const record of records) {
94
+ const key = this.getRecordKey(record.id);
95
+ // Serialize Date to ISO string for cache storage
96
+ const serialized = this.serializeRecord(record);
97
+ await this.cache.set(key, serialized, this.ttl);
98
+ newIds.push(record.id);
99
+ }
100
100
 
101
- // Update index with new IDs
102
- const updatedIds = [...existingIds, ...newIds];
103
- await this.cache.set(this.indexKey, updatedIds, this.ttl);
104
- }
101
+ // Update index with new IDs
102
+ const updatedIds = [...existingIds, ...newIds];
103
+ await this.cache.set(this.indexKey, updatedIds, this.ttl);
104
+ }
105
105
 
106
- /**
107
- * Query audit records from cache.
108
- */
109
- async query(options: AuditQueryOptions): Promise<AuditRecord[]> {
110
- const allRecords = await this.getAllRecords();
111
- let results = this.applyFilters(allRecords, options);
106
+ /**
107
+ * Query audit records from cache.
108
+ */
109
+ async query(options: AuditQueryOptions): Promise<AuditRecord[]> {
110
+ const allRecords = await this.getAllRecords();
111
+ let results = this.applyFilters(allRecords, options);
112
112
 
113
- // Ordering
114
- const orderBy = options.orderBy ?? 'timestamp';
115
- const orderDirection = options.orderDirection ?? 'desc';
116
- results.sort((a, b) => {
117
- const aValue = orderBy === 'timestamp' ? a.timestamp.getTime() : a.type;
118
- const bValue = orderBy === 'timestamp' ? b.timestamp.getTime() : b.type;
119
- if (aValue < bValue) return orderDirection === 'asc' ? -1 : 1;
120
- if (aValue > bValue) return orderDirection === 'asc' ? 1 : -1;
121
- return 0;
122
- });
113
+ // Ordering
114
+ const orderBy = options.orderBy ?? 'timestamp';
115
+ const orderDirection = options.orderDirection ?? 'desc';
116
+ results.sort((a, b) => {
117
+ const aValue = orderBy === 'timestamp' ? a.timestamp.getTime() : a.type;
118
+ const bValue = orderBy === 'timestamp' ? b.timestamp.getTime() : b.type;
119
+ if (aValue < bValue) return orderDirection === 'asc' ? -1 : 1;
120
+ if (aValue > bValue) return orderDirection === 'asc' ? 1 : -1;
121
+ return 0;
122
+ });
123
123
 
124
- // Pagination
125
- if (options.offset !== undefined) {
126
- results = results.slice(options.offset);
127
- }
128
- if (options.limit !== undefined) {
129
- results = results.slice(0, options.limit);
130
- }
124
+ // Pagination
125
+ if (options.offset !== undefined) {
126
+ results = results.slice(options.offset);
127
+ }
128
+ if (options.limit !== undefined) {
129
+ results = results.slice(0, options.limit);
130
+ }
131
131
 
132
- return results;
133
- }
132
+ return results;
133
+ }
134
134
 
135
- /**
136
- * Count audit records matching filters.
137
- */
138
- async count(
139
- options: Omit<AuditQueryOptions, 'limit' | 'offset'>,
140
- ): Promise<number> {
141
- const allRecords = await this.getAllRecords();
142
- return this.applyFilters(allRecords, options).length;
143
- }
135
+ /**
136
+ * Count audit records matching filters.
137
+ */
138
+ async count(
139
+ options: Omit<AuditQueryOptions, 'limit' | 'offset'>,
140
+ ): Promise<number> {
141
+ const allRecords = await this.getAllRecords();
142
+ return this.applyFilters(allRecords, options).length;
143
+ }
144
144
 
145
- /**
146
- * Get all stored records (for testing/debugging).
147
- */
148
- async getRecords(): Promise<AuditRecord[]> {
149
- return this.getAllRecords();
150
- }
145
+ /**
146
+ * Get all stored records (for testing/debugging).
147
+ */
148
+ async getRecords(): Promise<AuditRecord[]> {
149
+ return this.getAllRecords();
150
+ }
151
151
 
152
- /**
153
- * Clear all stored records.
154
- */
155
- async clear(): Promise<void> {
156
- const ids = (await this.cache.get<string[]>(this.indexKey)) ?? [];
152
+ /**
153
+ * Clear all stored records.
154
+ */
155
+ async clear(): Promise<void> {
156
+ const ids = (await this.cache.get<string[]>(this.indexKey)) ?? [];
157
157
 
158
- // Delete all records
159
- for (const id of ids) {
160
- await this.cache.delete(this.getRecordKey(id));
161
- }
158
+ // Delete all records
159
+ for (const id of ids) {
160
+ await this.cache.delete(this.getRecordKey(id));
161
+ }
162
162
 
163
- // Delete index
164
- await this.cache.delete(this.indexKey);
165
- }
163
+ // Delete index
164
+ await this.cache.delete(this.indexKey);
165
+ }
166
166
 
167
- private getRecordKey(id: string): string {
168
- return `${this.prefix}:${id}`;
169
- }
167
+ private getRecordKey(id: string): string {
168
+ return `${this.prefix}:${id}`;
169
+ }
170
170
 
171
- private async getAllRecords(): Promise<AuditRecord[]> {
172
- const ids = (await this.cache.get<string[]>(this.indexKey)) ?? [];
173
- const records: AuditRecord[] = [];
174
- const validIds: string[] = [];
171
+ private async getAllRecords(): Promise<AuditRecord[]> {
172
+ const ids = (await this.cache.get<string[]>(this.indexKey)) ?? [];
173
+ const records: AuditRecord[] = [];
174
+ const validIds: string[] = [];
175
175
 
176
- for (const id of ids) {
177
- const key = this.getRecordKey(id);
178
- const serialized = await this.cache.get<SerializedAuditRecord>(key);
179
- if (serialized) {
180
- records.push(this.deserializeRecord(serialized));
181
- validIds.push(id);
182
- }
183
- }
176
+ for (const id of ids) {
177
+ const key = this.getRecordKey(id);
178
+ const serialized = await this.cache.get<SerializedAuditRecord>(key);
179
+ if (serialized) {
180
+ records.push(this.deserializeRecord(serialized));
181
+ validIds.push(id);
182
+ }
183
+ }
184
184
 
185
- // Clean up index if some records expired
186
- if (validIds.length !== ids.length) {
187
- await this.cache.set(this.indexKey, validIds, this.ttl);
188
- }
185
+ // Clean up index if some records expired
186
+ if (validIds.length !== ids.length) {
187
+ await this.cache.set(this.indexKey, validIds, this.ttl);
188
+ }
189
189
 
190
- return records;
191
- }
190
+ return records;
191
+ }
192
192
 
193
- private serializeRecord(record: AuditRecord): SerializedAuditRecord {
194
- return {
195
- ...record,
196
- timestamp: record.timestamp.toISOString(),
197
- };
198
- }
193
+ private serializeRecord(record: AuditRecord): SerializedAuditRecord {
194
+ return {
195
+ ...record,
196
+ timestamp: record.timestamp.toISOString(),
197
+ };
198
+ }
199
199
 
200
- private deserializeRecord(serialized: SerializedAuditRecord): AuditRecord {
201
- return {
202
- ...serialized,
203
- timestamp: new Date(serialized.timestamp),
204
- };
205
- }
200
+ private deserializeRecord(serialized: SerializedAuditRecord): AuditRecord {
201
+ return {
202
+ ...serialized,
203
+ timestamp: new Date(serialized.timestamp),
204
+ };
205
+ }
206
206
 
207
- private applyFilters(
208
- records: AuditRecord[],
209
- options: AuditQueryOptions,
210
- ): AuditRecord[] {
211
- return records.filter((record) => {
212
- // Type filter
213
- if (options.type !== undefined) {
214
- if (Array.isArray(options.type)) {
215
- if (!options.type.includes(record.type)) return false;
216
- } else {
217
- if (record.type !== options.type) return false;
218
- }
219
- }
207
+ private applyFilters(
208
+ records: AuditRecord[],
209
+ options: AuditQueryOptions,
210
+ ): AuditRecord[] {
211
+ return records.filter((record) => {
212
+ // Type filter
213
+ if (options.type !== undefined) {
214
+ if (Array.isArray(options.type)) {
215
+ if (!options.type.includes(record.type)) return false;
216
+ } else {
217
+ if (record.type !== options.type) return false;
218
+ }
219
+ }
220
220
 
221
- // Entity ID filter
222
- if (options.entityId !== undefined) {
223
- const entityId =
224
- typeof options.entityId === 'string'
225
- ? options.entityId
226
- : JSON.stringify(options.entityId);
227
- const recordEntityId =
228
- typeof record.entityId === 'string'
229
- ? record.entityId
230
- : JSON.stringify(record.entityId);
231
- if (recordEntityId !== entityId) return false;
232
- }
221
+ // Entity ID filter
222
+ if (options.entityId !== undefined) {
223
+ const entityId =
224
+ typeof options.entityId === 'string'
225
+ ? options.entityId
226
+ : JSON.stringify(options.entityId);
227
+ const recordEntityId =
228
+ typeof record.entityId === 'string'
229
+ ? record.entityId
230
+ : JSON.stringify(record.entityId);
231
+ if (recordEntityId !== entityId) return false;
232
+ }
233
233
 
234
- // Table filter
235
- if (options.table !== undefined) {
236
- if (record.table !== options.table) return false;
237
- }
234
+ // Table filter
235
+ if (options.table !== undefined) {
236
+ if (record.table !== options.table) return false;
237
+ }
238
238
 
239
- // Actor ID filter
240
- if (options.actorId !== undefined) {
241
- if (record.actor?.id !== options.actorId) return false;
242
- }
239
+ // Actor ID filter
240
+ if (options.actorId !== undefined) {
241
+ if (record.actor?.id !== options.actorId) return false;
242
+ }
243
243
 
244
- // Date range filters
245
- if (options.from !== undefined) {
246
- if (record.timestamp < options.from) return false;
247
- }
248
- if (options.to !== undefined) {
249
- if (record.timestamp > options.to) return false;
250
- }
244
+ // Date range filters
245
+ if (options.from !== undefined) {
246
+ if (record.timestamp < options.from) return false;
247
+ }
248
+ if (options.to !== undefined) {
249
+ if (record.timestamp > options.to) return false;
250
+ }
251
251
 
252
- return true;
253
- });
254
- }
252
+ return true;
253
+ });
254
+ }
255
255
  }
256
256
 
257
257
  /**
@@ -259,5 +259,5 @@ export class CacheAuditStorage<
259
259
  * Dates are stored as ISO strings.
260
260
  */
261
261
  type SerializedAuditRecord = Omit<AuditRecord, 'timestamp'> & {
262
- timestamp: string;
262
+ timestamp: string;
263
263
  };
package/src/index.ts CHANGED
@@ -1,24 +1,23 @@
1
1
  // Core types
2
- export type {
3
- AuditableAction,
4
- AuditActor,
5
- AuditMetadata,
6
- AuditOperation,
7
- AuditOptions,
8
- AuditRecord,
9
- ExtractAuditPayload,
10
- ExtractAuditType,
11
- ExtractAuditorAction,
12
- ExtractStorageAuditAction,
13
- MappedAudit,
14
- } from './types';
15
2
 
16
3
  // Auditor interface
17
4
  export type { Auditor } from './Auditor';
5
+ export type { DefaultAuditorConfig } from './DefaultAuditor';
18
6
 
19
7
  // Default implementation
20
8
  export { DefaultAuditor } from './DefaultAuditor';
21
- export type { DefaultAuditorConfig } from './DefaultAuditor';
22
-
23
9
  // Storage interface
24
10
  export type { AuditQueryOptions, AuditStorage } from './storage';
11
+ export type {
12
+ AuditActor,
13
+ AuditableAction,
14
+ AuditMetadata,
15
+ AuditOperation,
16
+ AuditOptions,
17
+ AuditRecord,
18
+ ExtractAuditorAction,
19
+ ExtractAuditPayload,
20
+ ExtractAuditType,
21
+ ExtractStorageAuditAction,
22
+ MappedAudit,
23
+ } from './types';