@geekmidas/audit 10.0.0-alpha.2 → 10.0.0-alpha.21

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 (77) hide show
  1. package/dist/{Auditor-CW2NrfHS.d.mts → Auditor-BBXXCD0v.d.cts} +2 -7
  2. package/dist/Auditor-BBXXCD0v.d.cts.map +1 -0
  3. package/dist/{Auditor-DpBHXIJM.d.cts → Auditor-BBXXCD0v.d.mts} +2 -7
  4. package/dist/Auditor-BBXXCD0v.d.mts.map +1 -0
  5. package/dist/Auditor.d.cts +1 -1
  6. package/dist/Auditor.d.mts +1 -1
  7. package/dist/Auditor.mjs +1 -0
  8. package/dist/DefaultAuditor.cjs +95 -2
  9. package/dist/DefaultAuditor.cjs.map +1 -0
  10. package/dist/DefaultAuditor.d.cts +55 -3
  11. package/dist/DefaultAuditor.d.cts.map +1 -0
  12. package/dist/DefaultAuditor.d.mts +55 -3
  13. package/dist/DefaultAuditor.d.mts.map +1 -0
  14. package/dist/DefaultAuditor.mjs +94 -2
  15. package/dist/DefaultAuditor.mjs.map +1 -0
  16. package/dist/cache.cjs +166 -2
  17. package/dist/cache.cjs.map +1 -0
  18. package/dist/cache.d.cts +92 -3
  19. package/dist/cache.d.cts.map +1 -0
  20. package/dist/cache.d.mts +92 -3
  21. package/dist/cache.d.mts.map +1 -0
  22. package/dist/cache.mjs +165 -2
  23. package/dist/cache.mjs.map +1 -0
  24. package/dist/index.cjs +3 -3
  25. package/dist/index.d.cts +3 -3
  26. package/dist/index.d.mts +3 -3
  27. package/dist/index.mjs +2 -3
  28. package/dist/knex.cjs +9 -9
  29. package/dist/knex.cjs.map +1 -1
  30. package/dist/knex.d.cts +9 -11
  31. package/dist/knex.d.cts.map +1 -1
  32. package/dist/knex.d.mts +9 -11
  33. package/dist/knex.d.mts.map +1 -1
  34. package/dist/knex.mjs +7 -7
  35. package/dist/knex.mjs.map +1 -1
  36. package/dist/kysely.cjs +9 -9
  37. package/dist/kysely.cjs.map +1 -1
  38. package/dist/kysely.d.cts +9 -11
  39. package/dist/kysely.d.cts.map +1 -1
  40. package/dist/kysely.d.mts +9 -11
  41. package/dist/kysely.d.mts.map +1 -1
  42. package/dist/kysely.mjs +7 -7
  43. package/dist/kysely.mjs.map +1 -1
  44. package/dist/memory.cjs +6 -7
  45. package/dist/memory.cjs.map +1 -1
  46. package/dist/memory.d.cts +3 -7
  47. package/dist/memory.d.cts.map +1 -1
  48. package/dist/memory.d.mts +3 -7
  49. package/dist/memory.d.mts.map +1 -1
  50. package/dist/memory.mjs +3 -4
  51. package/dist/memory.mjs.map +1 -1
  52. package/dist/storage.d.cts +1 -1
  53. package/dist/storage.d.mts +1 -1
  54. package/dist/storage.mjs +1 -0
  55. package/dist/types.d.cts +1 -1
  56. package/dist/types.d.mts +1 -1
  57. package/dist/types.mjs +1 -0
  58. package/package.json +13 -13
  59. package/dist/Auditor-CW2NrfHS.d.mts.map +0 -1
  60. package/dist/Auditor-DpBHXIJM.d.cts.map +0 -1
  61. package/dist/DefaultAuditor-BAVnNmRh.mjs +0 -96
  62. package/dist/DefaultAuditor-BAVnNmRh.mjs.map +0 -1
  63. package/dist/DefaultAuditor-DlJ1ENfx.d.mts +0 -59
  64. package/dist/DefaultAuditor-DlJ1ENfx.d.mts.map +0 -1
  65. package/dist/DefaultAuditor-GRFI86N_.d.cts +0 -59
  66. package/dist/DefaultAuditor-GRFI86N_.d.cts.map +0 -1
  67. package/dist/DefaultAuditor-JbZ_BQfh.cjs +0 -102
  68. package/dist/DefaultAuditor-JbZ_BQfh.cjs.map +0 -1
  69. package/dist/cache-BbIl31RL.mjs +0 -167
  70. package/dist/cache-BbIl31RL.mjs.map +0 -1
  71. package/dist/cache-Bih6HxY6.d.cts +0 -96
  72. package/dist/cache-Bih6HxY6.d.cts.map +0 -1
  73. package/dist/cache-l7jAptBl.cjs +0 -173
  74. package/dist/cache-l7jAptBl.cjs.map +0 -1
  75. package/dist/cache-wET57t6d.d.mts +0 -96
  76. package/dist/cache-wET57t6d.d.mts.map +0 -1
  77. package/dist/chunk-CUT6urMc.cjs +0 -30
package/dist/knex.d.mts CHANGED
@@ -1,11 +1,10 @@
1
- import { AuditQueryOptions, AuditRecord, AuditStorage, TransactionAwareAuditor } from "./Auditor-CW2NrfHS.mjs";
1
+ import { h as TransactionAwareAuditor, m as AuditStorage, o as AuditRecord, p as AuditQueryOptions } from "./Auditor-BBXXCD0v.mjs";
2
2
  import { Knex } from "knex";
3
-
4
3
  //#region src/knex.d.ts
5
- interface TransactionSettings {
4
+ export interface TransactionSettings {
6
5
  isolationLevel?: Knex.IsolationLevels;
7
6
  }
8
- type DatabaseConnection = Knex | Knex.Transaction;
7
+ export type DatabaseConnection = Knex | Knex.Transaction;
9
8
  /**
10
9
  * Execute a callback within a database transaction with automatic audit handling.
11
10
  *
@@ -45,7 +44,7 @@ type DatabaseConnection = Knex | Knex.Transaction;
45
44
  * // Audits are automatically flushed inside the transaction before commit
46
45
  * ```
47
46
  */
48
- declare function withAuditableTransaction<T>(db: DatabaseConnection, auditor: TransactionAwareAuditor<Knex.Transaction>, cb: (trx: Knex.Transaction) => Promise<T>, settings?: TransactionSettings): Promise<T>;
47
+ export declare function withAuditableTransaction<T>(db: DatabaseConnection, auditor: TransactionAwareAuditor<Knex.Transaction>, cb: (trx: Knex.Transaction) => Promise<T>, settings?: TransactionSettings): Promise<T>;
49
48
  /**
50
49
  * Shape of a row in the audit log table.
51
50
  * Use this to describe your audit table when declaring Knex table types.
@@ -70,7 +69,7 @@ declare function withAuditableTransaction<T>(db: DatabaseConnection, auditor: Tr
70
69
  * }
71
70
  * ```
72
71
  */
73
- interface AuditLogTable {
72
+ export interface AuditLogTable {
74
73
  id: string;
75
74
  type: string;
76
75
  operation: string;
@@ -89,13 +88,13 @@ interface AuditLogTable {
89
88
  * Insertable version of AuditLogTable where id is optional.
90
89
  * Use this when your database auto-generates IDs or when using the autoId option.
91
90
  */
92
- type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
91
+ export type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
93
92
  id?: string;
94
93
  };
95
94
  /**
96
95
  * Configuration for KnexAuditStorage.
97
96
  */
98
- interface KnexAuditStorageConfig {
97
+ export interface KnexAuditStorageConfig {
99
98
  /** Knex database instance */
100
99
  db: Knex;
101
100
  /** Table name for audit logs */
@@ -131,7 +130,7 @@ interface KnexAuditStorageConfig {
131
130
  * });
132
131
  * ```
133
132
  */
134
- declare class KnexAuditStorage implements AuditStorage {
133
+ export declare class KnexAuditStorage implements AuditStorage {
135
134
  private readonly db;
136
135
  private readonly tableName;
137
136
  private readonly autoId;
@@ -172,7 +171,6 @@ declare class KnexAuditStorage implements AuditStorage {
172
171
  private getActorData;
173
172
  private parseEntityId;
174
173
  }
175
- //# sourceMappingURL=knex.d.ts.map
176
174
  //#endregion
177
- export { AuditLogTable, DatabaseConnection, InsertableAuditLogTable, KnexAuditStorage, KnexAuditStorageConfig, TransactionAwareAuditor, TransactionSettings, withAuditableTransaction };
175
+ export type { TransactionAwareAuditor };
178
176
  //# sourceMappingURL=knex.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"knex.d.mts","names":[],"sources":["../src/knex.ts"],"sourcesContent":[],"mappings":";;;;AAWiB,UAAA,mBAAA,CACC;EAGN,cAAA,CAAA,EAHM,IAAA,CAAK,eAGO;;AAAG,KAArB,kBAAA,GAAqB,IAAA,GAAO,IAAA,CAAK,WAAZ;;AAAuB;AAyDxD;;;;;;;;;;;AAKU;AAgDV;AAoBA;;;;AAA0C;AAO1C;AAqCA;;;;;;;;;;;;;;;;;;AAgFI,iBArMkB,wBAqMlB,CAAA,CAAA,CAAA,CAAA,EAAA,EApMC,kBAoMD,EAAA,OAAA,EAnMM,uBAmMN,CAnM8B,IAAA,CAAK,WAmMnC,CAAA,EAAA,EAAA,EAAA,CAAA,GAAA,EAlMO,IAAA,CAAK,WAkMZ,EAAA,GAlM4B,OAkM5B,CAlMoC,CAkMpC,CAAA,EAAA,QAAA,CAAA,EAjMQ,mBAiMR,CAAA,EAhMD,OAgMC,CAhMO,CAgMP,CAAA;;AAhFiD;;;;;;;;;;;;;;;;;;;;;;;UAhEpC,aAAA;;;;;;;;;aASL;;;;;;;;;;KAWA,uBAAA,GAA0B,KAAK;;;;;;UAO1B,sBAAA;;MAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmCQ,gBAAA,YAA4B;;;;;sBAMpB;iBAOC,+BAA+B;iBAW/B,oBAAoB,QAAQ;iBA0BvC,KAAK,yCACZ;;;;;iBAaY;;;;;;;;;8BAaL,wBAAwB,IAAA,CAAK,8BACtB,QAAQ,SACnB,qBACH,QAAQ"}
1
+ {"version":3,"file":"knex.d.mts","names":[],"sources":["../src/knex.ts"],"mappings":";;;iBAWiB;EAChB,iBAAiB,KAAK;;YAGX,qBAAqB,OAAO,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAyDvB,yBAAyB,GAC9C,IAAI,oBACJ,SAAS,wBAAwB,KAAK,cACtC,KAAK,KAAK,KAAK,gBAAgB,QAAQ,IACvC,WAAW,sBACT,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDM;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA;EACA;EACA;;;;;;YAOW,0BAA0B,KAAK;EAC1C;;;;;iBAMgB;;EAEhB,IAAI;;EAEJ;;;;;;EAMA;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;qBAoBY,4BAA4B;mBACvB;mBACA;mBACA;WACR;EAET,YAAY,QAAQ;EAOd,MAAM,SAAS,eAAe,gBAAgB;EAW9C,MAAM,SAAS,oBAAoB,QAAQ;EAyB3C,MACL,SAAS,KAAK,yCACZ;;;;;EAaH,eAAe;;;;;;;;;EAYT,gBAAgB,GACrB,SAAS,wBAAwB,KAAK,cACtC,gBAAgB,QAAQ,IACxB,KAAK,qBACH,QAAQ;UAoBH;UAwCA;UA8BA;;;;;;;;UAgCA;;;;UAUA;UAUA;UAOA"}
package/dist/knex.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { nanoid } from "nanoid";
2
-
3
2
  //#region src/knex.ts
4
3
  /**
5
4
  * Type guard for an active Knex transaction.
@@ -102,8 +101,7 @@ var KnexAuditStorage = class {
102
101
  query = query.orderBy(orderBy === "timestamp" ? "timestamp" : "type", orderDirection);
103
102
  if (options.limit !== void 0) query = query.limit(options.limit);
104
103
  if (options.offset !== void 0) query = query.offset(options.offset);
105
- const rows = await query;
106
- return rows.map((row) => this.fromRow(row));
104
+ return (await query).map((row) => this.fromRow(row));
107
105
  }
108
106
  async count(options) {
109
107
  let query = this.db(this.tableName).count({ count: "id" });
@@ -142,8 +140,10 @@ var KnexAuditStorage = class {
142
140
  });
143
141
  }
144
142
  applyFilters(query, options) {
145
- if (options.type !== void 0) if (Array.isArray(options.type)) query = query.whereIn("type", options.type);
146
- else query = query.where("type", options.type);
143
+ if (options.type !== void 0) {
144
+ if (Array.isArray(options.type)) query = query.whereIn("type", options.type);
145
+ else query = query.where("type", options.type);
146
+ }
147
147
  if (options.entityId !== void 0) {
148
148
  const entityId = typeof options.entityId === "string" ? options.entityId : JSON.stringify(options.entityId);
149
149
  query = query.where("entityId", entityId);
@@ -212,7 +212,7 @@ var KnexAuditStorage = class {
212
212
  return {};
213
213
  }
214
214
  getActorData(actor) {
215
- const { id, type,...rest } = actor;
215
+ const { id, type, ...rest } = actor;
216
216
  return rest;
217
217
  }
218
218
  parseEntityId(entityId) {
@@ -225,7 +225,7 @@ var KnexAuditStorage = class {
225
225
  }
226
226
  }
227
227
  };
228
-
229
228
  //#endregion
230
229
  export { KnexAuditStorage, withAuditableTransaction };
230
+
231
231
  //# sourceMappingURL=knex.mjs.map
package/dist/knex.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"knex.mjs","names":["connection: DatabaseConnection","db: DatabaseConnection","auditor: TransactionAwareAuditor<Knex.Transaction>","cb: (trx: Knex.Transaction) => Promise<T>","settings?: TransactionSettings","trx: Knex.Transaction","config: KnexAuditStorageConfig","records: AuditRecord[]","trx?: unknown","options: AuditQueryOptions","row: AuditLogTable","options: Omit<AuditQueryOptions, 'limit' | 'offset'>","callback: () => Promise<T>","db?: DatabaseConnection","query: any","record: AuditRecord","value: unknown","actor: NonNullable<AuditRecord['actor']>","entityId: string"],"sources":["../src/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { nanoid } from 'nanoid';\nimport type {\n\tAuditQueryOptions,\n\tAuditStorage,\n\tTransactionAwareAuditor,\n} from './storage';\nimport type { AuditRecord } from './types';\n\nexport type { TransactionAwareAuditor };\n\nexport interface TransactionSettings {\n\tisolationLevel?: Knex.IsolationLevels;\n}\n\nexport type DatabaseConnection = Knex | Knex.Transaction;\n\n/**\n * Type guard for an active Knex transaction.\n *\n * Knex marks transaction instances with `isTransaction`, and a completed\n * transaction can no longer be used, so a settled transaction is treated as a\n * plain connection.\n */\nfunction isActiveTransaction(\n\tconnection: DatabaseConnection,\n): connection is Knex.Transaction {\n\treturn (\n\t\tconnection.isTransaction === true &&\n\t\t!(connection as Knex.Transaction).isCompleted()\n\t);\n}\n\n/**\n * Execute a callback within a database transaction with automatic audit handling.\n *\n * This wrapper ensures that:\n * 1. The transaction is automatically registered with the auditor\n * 2. Manual audits (via `auditor.audit()`) are flushed BEFORE the transaction commits\n * 3. If audit flush fails, the entire transaction rolls back\n * 4. If the callback fails, audits are NOT written (atomic consistency)\n *\n * **Note:** Declarative audits (defined via `.audit([...])` on the endpoint builder)\n * are processed AFTER the handler returns, so they run outside this transaction.\n * If you need all audits to be atomic with your database operations, use manual\n * audits via `auditor.audit()` inside this wrapper.\n *\n * @param db - Database connection (Knex instance or Transaction)\n * @param auditor - Auditor instance that will receive the transaction\n * @param cb - Callback to execute within the transaction\n * @param settings - Optional transaction settings (isolation level)\n * @returns The result of the callback\n *\n * @example\n * ```typescript\n * import { withAuditableTransaction } from '@geekmidas/audit/knex';\n *\n * const result = await withAuditableTransaction(\n * services.database,\n * auditor,\n * async (trx) => {\n * const [user] = await trx('users').insert(data).returning('*');\n *\n * // Manual audits are atomic with the transaction\n * auditor.audit('user.created', { userId: user.id, email: user.email });\n *\n * return user;\n * },\n * );\n * // Audits are automatically flushed inside the transaction before commit\n * ```\n */\nexport async function withAuditableTransaction<T>(\n\tdb: DatabaseConnection,\n\tauditor: TransactionAwareAuditor<Knex.Transaction>,\n\tcb: (trx: Knex.Transaction) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tconst execute = async (trx: Knex.Transaction): Promise<T> => {\n\t\t// Register transaction with auditor\n\t\tauditor.setTransaction(trx);\n\n\t\t// Execute the callback\n\t\tconst result = await cb(trx);\n\n\t\t// Flush audits BEFORE transaction commits\n\t\t// If this fails, the transaction will roll back\n\t\tawait auditor.flush(trx);\n\n\t\treturn result;\n\t};\n\n\t// If already in a transaction, just run with it.\n\t// Knex would otherwise open a savepoint, which commits independently.\n\tif (isActiveTransaction(db)) {\n\t\treturn execute(db);\n\t}\n\n\treturn db.transaction(execute, settings);\n}\n\n/**\n * Shape of a row in the audit log table.\n * Use this to describe your audit table when declaring Knex table types.\n *\n * Property names are camelCase to match the Kysely storage. Pair this with\n * `knexSnakeCaseMappers()` if your database columns are snake_case.\n *\n * @example\n * ```typescript\n * import { knexSnakeCaseMappers } from 'objection';\n *\n * const db = knex({\n * client: 'pg',\n * connection: process.env.DATABASE_URL,\n * ...knexSnakeCaseMappers(),\n * });\n *\n * declare module 'knex/types/tables' {\n * interface Tables {\n * audit_logs: AuditLogTable;\n * }\n * }\n * ```\n */\nexport interface AuditLogTable {\n\tid: string;\n\ttype: string;\n\toperation: string;\n\ttable: string | null;\n\tentityId: string | null;\n\toldValues: unknown | null;\n\tnewValues: unknown | null;\n\tpayload: unknown | null;\n\ttimestamp: Date;\n\tactorId: string | null;\n\tactorType: string | null;\n\tactorData: unknown | null;\n\tmetadata: unknown | null;\n}\n\n/**\n * Insertable version of AuditLogTable where id is optional.\n * Use this when your database auto-generates IDs or when using the autoId option.\n */\nexport type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {\n\tid?: string;\n};\n\n/**\n * Configuration for KnexAuditStorage.\n */\nexport interface KnexAuditStorageConfig {\n\t/** Knex database instance */\n\tdb: Knex;\n\t/** Table name for audit logs */\n\ttableName: string;\n\t/**\n\t * Service name of the database service.\n\t * When set, endpoint adaptors will automatically use the audit transaction as `db`\n\t * in the handler context if the endpoint's database service has the same name.\n\t */\n\tdatabaseServiceName?: string;\n\t/**\n\t * Let the database auto-generate IDs (e.g., via DEFAULT gen_random_uuid()).\n\t * When true, the ID field is omitted from inserts if not provided.\n\t * When false (default), IDs are generated using nanoid if not provided.\n\t * @default false\n\t */\n\tautoId?: boolean;\n}\n\n/**\n * Knex-based audit storage implementation.\n * Stores audit records in a database table using Knex.\n *\n * @example\n * ```typescript\n * const storage = new KnexAuditStorage({\n * db: knexDb,\n * tableName: 'audit_logs',\n * });\n *\n * const auditor = new DefaultAuditor({\n * actor: { id: 'user-123', type: 'user' },\n * storage,\n * });\n * ```\n */\nexport class KnexAuditStorage implements AuditStorage {\n\tprivate readonly db: Knex;\n\tprivate readonly tableName: string;\n\tprivate readonly autoId: boolean;\n\treadonly databaseServiceName?: string;\n\n\tconstructor(config: KnexAuditStorageConfig) {\n\t\tthis.db = config.db;\n\t\tthis.tableName = config.tableName;\n\t\tthis.databaseServiceName = config.databaseServiceName;\n\t\tthis.autoId = config.autoId ?? false;\n\t}\n\n\tasync write(records: AuditRecord[], trx?: unknown): Promise<void> {\n\t\tif (records.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst db = (trx as Knex.Transaction) ?? this.db;\n\t\tconst rows = records.map((record) => this.toRow(record));\n\n\t\tawait db(this.tableName).insert(rows);\n\t}\n\n\tasync query(options: AuditQueryOptions): Promise<AuditRecord[]> {\n\t\tlet query = this.db(this.tableName).select('*');\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\t// Ordering\n\t\tconst orderBy = options.orderBy ?? 'timestamp';\n\t\tconst orderDirection = options.orderDirection ?? 'desc';\n\t\tquery = query.orderBy(\n\t\t\torderBy === 'timestamp' ? 'timestamp' : 'type',\n\t\t\torderDirection,\n\t\t);\n\n\t\t// Pagination\n\t\tif (options.limit !== undefined) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\t\tif (options.offset !== undefined) {\n\t\t\tquery = query.offset(options.offset);\n\t\t}\n\n\t\tconst rows = await query;\n\t\treturn rows.map((row: AuditLogTable) => this.fromRow(row));\n\t}\n\n\tasync count(\n\t\toptions: Omit<AuditQueryOptions, 'limit' | 'offset'>,\n\t): Promise<number> {\n\t\tlet query = this.db(this.tableName).count({ count: 'id' });\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\tconst result = await query.first();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Get the Knex database instance for transactional operations.\n\t * Used by endpoint adaptors to automatically wrap handlers in transactions.\n\t */\n\tgetDatabase(): Knex {\n\t\treturn this.db;\n\t}\n\n\t/**\n\t * Execute a callback within a Knex transaction with automatic audit handling.\n\t * The auditor is registered with the transaction and audits are flushed\n\t * before the transaction commits.\n\t *\n\t * If the provided db connection is already a transaction, it will be reused\n\t * instead of opening a savepoint.\n\t */\n\tasync withTransaction<T>(\n\t\tauditor: TransactionAwareAuditor<Knex.Transaction>,\n\t\tcallback: () => Promise<T>,\n\t\tdb?: DatabaseConnection,\n\t): Promise<T> {\n\t\tconst connection = db ?? this.db;\n\n\t\t// If already in a transaction, reuse it\n\t\tif (isActiveTransaction(connection)) {\n\t\t\tauditor.setTransaction(connection);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(connection);\n\t\t\treturn result;\n\t\t}\n\n\t\t// Create new transaction\n\t\treturn connection.transaction(async (trx) => {\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tprivate applyFilters(query: any, options: AuditQueryOptions): any {\n\t\t// Type filter\n\t\tif (options.type !== undefined) {\n\t\t\tif (Array.isArray(options.type)) {\n\t\t\t\tquery = query.whereIn('type', options.type);\n\t\t\t} else {\n\t\t\t\tquery = query.where('type', options.type);\n\t\t\t}\n\t\t}\n\n\t\t// Entity ID filter\n\t\tif (options.entityId !== undefined) {\n\t\t\tconst entityId =\n\t\t\t\ttypeof options.entityId === 'string'\n\t\t\t\t\t? options.entityId\n\t\t\t\t\t: JSON.stringify(options.entityId);\n\t\t\tquery = query.where('entityId', entityId);\n\t\t}\n\n\t\t// Table filter\n\t\tif (options.table !== undefined) {\n\t\t\tquery = query.where('table', options.table);\n\t\t}\n\n\t\t// Actor ID filter\n\t\tif (options.actorId !== undefined) {\n\t\t\tquery = query.where('actorId', options.actorId);\n\t\t}\n\n\t\t// Date range filters\n\t\tif (options.from !== undefined) {\n\t\t\tquery = query.where('timestamp', '>=', options.from);\n\t\t}\n\t\tif (options.to !== undefined) {\n\t\t\tquery = query.where('timestamp', '<=', options.to);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tprivate toRow(record: AuditRecord): AuditLogTable {\n\t\t// If autoId is true, let database generate ID (ignore record.id)\n\t\t// If autoId is false (default), use record.id or generate with nanoid\n\t\tconst id = this.autoId ? undefined : record.id || nanoid();\n\n\t\treturn {\n\t\t\t...(id && { id }),\n\t\t\ttype: record.type,\n\t\t\toperation: record.operation,\n\t\t\ttable: record.table ?? null,\n\t\t\tentityId:\n\t\t\t\trecord.entityId === undefined\n\t\t\t\t\t? null\n\t\t\t\t\t: typeof record.entityId === 'string'\n\t\t\t\t\t\t? record.entityId\n\t\t\t\t\t\t: JSON.stringify(record.entityId),\n\t\t\toldValues: this.toJsonColumn(record.oldValues),\n\t\t\tnewValues: this.toJsonColumn(record.newValues),\n\t\t\tpayload: this.toJsonColumn(record.payload),\n\t\t\ttimestamp: record.timestamp,\n\t\t\tactorId: record.actor?.id ?? null,\n\t\t\tactorType: record.actor?.type ?? null,\n\t\t\tactorData:\n\t\t\t\trecord.actor !== undefined\n\t\t\t\t\t? this.toJsonColumn(this.getActorData(record.actor))\n\t\t\t\t\t: null,\n\t\t\tmetadata: this.toJsonColumn(record.metadata),\n\t\t} as AuditLogTable;\n\t}\n\n\tprivate fromRow(row: AuditLogTable): AuditRecord {\n\t\tconst actor =\n\t\t\trow.actorId !== null || row.actorType !== null\n\t\t\t\t? {\n\t\t\t\t\t\tid: row.actorId ?? undefined,\n\t\t\t\t\t\ttype: row.actorType ?? undefined,\n\t\t\t\t\t\t...(row.actorData ? this.parseJson(row.actorData) : {}),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\ttype: row.type,\n\t\t\toperation: row.operation as AuditRecord['operation'],\n\t\t\ttable: row.table ?? undefined,\n\t\t\tentityId: row.entityId ? this.parseEntityId(row.entityId) : undefined,\n\t\t\toldValues: row.oldValues ? this.parseJson(row.oldValues) : undefined,\n\t\t\tnewValues: row.newValues ? this.parseJson(row.newValues) : undefined,\n\t\t\tpayload: row.payload ? this.parseJson(row.payload) : undefined,\n\t\t\ttimestamp: row.timestamp,\n\t\t\tactor,\n\t\t\tmetadata: row.metadata ? this.parseJson(row.metadata) : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Serialize a JSON value for insertion.\n\t *\n\t * Unlike Kysely, Knex has no per-column type information to tell a json/jsonb\n\t * column from an object it should bind as a composite value, so plain objects\n\t * are stringified before they reach the driver.\n\t */\n\tprivate toJsonColumn(value: unknown): string | null {\n\t\tif (value === undefined || value === null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn JSON.stringify(value);\n\t}\n\n\t/**\n\t * Parse a JSON value that may already be parsed (e.g., from jsonb columns).\n\t */\n\tprivate parseJson(value: unknown): Record<string, unknown> {\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn value as Record<string, unknown>;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\treturn JSON.parse(value);\n\t\t}\n\t\treturn {};\n\t}\n\n\tprivate getActorData(\n\t\tactor: NonNullable<AuditRecord['actor']>,\n\t): Record<string, unknown> {\n\t\tconst { id, type, ...rest } = actor;\n\t\treturn rest;\n\t}\n\n\tprivate parseEntityId(entityId: string): string | Record<string, unknown> {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(entityId);\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t\treturn entityId;\n\t\t} catch {\n\t\t\treturn entityId;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"knex.mjs","names":[],"sources":["../src/knex.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { nanoid } from 'nanoid';\nimport type {\n\tAuditQueryOptions,\n\tAuditStorage,\n\tTransactionAwareAuditor,\n} from './storage';\nimport type { AuditRecord } from './types';\n\nexport type { TransactionAwareAuditor };\n\nexport interface TransactionSettings {\n\tisolationLevel?: Knex.IsolationLevels;\n}\n\nexport type DatabaseConnection = Knex | Knex.Transaction;\n\n/**\n * Type guard for an active Knex transaction.\n *\n * Knex marks transaction instances with `isTransaction`, and a completed\n * transaction can no longer be used, so a settled transaction is treated as a\n * plain connection.\n */\nfunction isActiveTransaction(\n\tconnection: DatabaseConnection,\n): connection is Knex.Transaction {\n\treturn (\n\t\tconnection.isTransaction === true &&\n\t\t!(connection as Knex.Transaction).isCompleted()\n\t);\n}\n\n/**\n * Execute a callback within a database transaction with automatic audit handling.\n *\n * This wrapper ensures that:\n * 1. The transaction is automatically registered with the auditor\n * 2. Manual audits (via `auditor.audit()`) are flushed BEFORE the transaction commits\n * 3. If audit flush fails, the entire transaction rolls back\n * 4. If the callback fails, audits are NOT written (atomic consistency)\n *\n * **Note:** Declarative audits (defined via `.audit([...])` on the endpoint builder)\n * are processed AFTER the handler returns, so they run outside this transaction.\n * If you need all audits to be atomic with your database operations, use manual\n * audits via `auditor.audit()` inside this wrapper.\n *\n * @param db - Database connection (Knex instance or Transaction)\n * @param auditor - Auditor instance that will receive the transaction\n * @param cb - Callback to execute within the transaction\n * @param settings - Optional transaction settings (isolation level)\n * @returns The result of the callback\n *\n * @example\n * ```typescript\n * import { withAuditableTransaction } from '@geekmidas/audit/knex';\n *\n * const result = await withAuditableTransaction(\n * services.database,\n * auditor,\n * async (trx) => {\n * const [user] = await trx('users').insert(data).returning('*');\n *\n * // Manual audits are atomic with the transaction\n * auditor.audit('user.created', { userId: user.id, email: user.email });\n *\n * return user;\n * },\n * );\n * // Audits are automatically flushed inside the transaction before commit\n * ```\n */\nexport async function withAuditableTransaction<T>(\n\tdb: DatabaseConnection,\n\tauditor: TransactionAwareAuditor<Knex.Transaction>,\n\tcb: (trx: Knex.Transaction) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tconst execute = async (trx: Knex.Transaction): Promise<T> => {\n\t\t// Register transaction with auditor\n\t\tauditor.setTransaction(trx);\n\n\t\t// Execute the callback\n\t\tconst result = await cb(trx);\n\n\t\t// Flush audits BEFORE transaction commits\n\t\t// If this fails, the transaction will roll back\n\t\tawait auditor.flush(trx);\n\n\t\treturn result;\n\t};\n\n\t// If already in a transaction, just run with it.\n\t// Knex would otherwise open a savepoint, which commits independently.\n\tif (isActiveTransaction(db)) {\n\t\treturn execute(db);\n\t}\n\n\treturn db.transaction(execute, settings);\n}\n\n/**\n * Shape of a row in the audit log table.\n * Use this to describe your audit table when declaring Knex table types.\n *\n * Property names are camelCase to match the Kysely storage. Pair this with\n * `knexSnakeCaseMappers()` if your database columns are snake_case.\n *\n * @example\n * ```typescript\n * import { knexSnakeCaseMappers } from 'objection';\n *\n * const db = knex({\n * client: 'pg',\n * connection: process.env.DATABASE_URL,\n * ...knexSnakeCaseMappers(),\n * });\n *\n * declare module 'knex/types/tables' {\n * interface Tables {\n * audit_logs: AuditLogTable;\n * }\n * }\n * ```\n */\nexport interface AuditLogTable {\n\tid: string;\n\ttype: string;\n\toperation: string;\n\ttable: string | null;\n\tentityId: string | null;\n\toldValues: unknown | null;\n\tnewValues: unknown | null;\n\tpayload: unknown | null;\n\ttimestamp: Date;\n\tactorId: string | null;\n\tactorType: string | null;\n\tactorData: unknown | null;\n\tmetadata: unknown | null;\n}\n\n/**\n * Insertable version of AuditLogTable where id is optional.\n * Use this when your database auto-generates IDs or when using the autoId option.\n */\nexport type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {\n\tid?: string;\n};\n\n/**\n * Configuration for KnexAuditStorage.\n */\nexport interface KnexAuditStorageConfig {\n\t/** Knex database instance */\n\tdb: Knex;\n\t/** Table name for audit logs */\n\ttableName: string;\n\t/**\n\t * Service name of the database service.\n\t * When set, endpoint adaptors will automatically use the audit transaction as `db`\n\t * in the handler context if the endpoint's database service has the same name.\n\t */\n\tdatabaseServiceName?: string;\n\t/**\n\t * Let the database auto-generate IDs (e.g., via DEFAULT gen_random_uuid()).\n\t * When true, the ID field is omitted from inserts if not provided.\n\t * When false (default), IDs are generated using nanoid if not provided.\n\t * @default false\n\t */\n\tautoId?: boolean;\n}\n\n/**\n * Knex-based audit storage implementation.\n * Stores audit records in a database table using Knex.\n *\n * @example\n * ```typescript\n * const storage = new KnexAuditStorage({\n * db: knexDb,\n * tableName: 'audit_logs',\n * });\n *\n * const auditor = new DefaultAuditor({\n * actor: { id: 'user-123', type: 'user' },\n * storage,\n * });\n * ```\n */\nexport class KnexAuditStorage implements AuditStorage {\n\tprivate readonly db: Knex;\n\tprivate readonly tableName: string;\n\tprivate readonly autoId: boolean;\n\treadonly databaseServiceName?: string;\n\n\tconstructor(config: KnexAuditStorageConfig) {\n\t\tthis.db = config.db;\n\t\tthis.tableName = config.tableName;\n\t\tthis.databaseServiceName = config.databaseServiceName;\n\t\tthis.autoId = config.autoId ?? false;\n\t}\n\n\tasync write(records: AuditRecord[], trx?: unknown): Promise<void> {\n\t\tif (records.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst db = (trx as Knex.Transaction) ?? this.db;\n\t\tconst rows = records.map((record) => this.toRow(record));\n\n\t\tawait db(this.tableName).insert(rows);\n\t}\n\n\tasync query(options: AuditQueryOptions): Promise<AuditRecord[]> {\n\t\tlet query = this.db(this.tableName).select('*');\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\t// Ordering\n\t\tconst orderBy = options.orderBy ?? 'timestamp';\n\t\tconst orderDirection = options.orderDirection ?? 'desc';\n\t\tquery = query.orderBy(\n\t\t\torderBy === 'timestamp' ? 'timestamp' : 'type',\n\t\t\torderDirection,\n\t\t);\n\n\t\t// Pagination\n\t\tif (options.limit !== undefined) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\t\tif (options.offset !== undefined) {\n\t\t\tquery = query.offset(options.offset);\n\t\t}\n\n\t\tconst rows = await query;\n\t\treturn rows.map((row: AuditLogTable) => this.fromRow(row));\n\t}\n\n\tasync count(\n\t\toptions: Omit<AuditQueryOptions, 'limit' | 'offset'>,\n\t): Promise<number> {\n\t\tlet query = this.db(this.tableName).count({ count: 'id' });\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\tconst result = await query.first();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Get the Knex database instance for transactional operations.\n\t * Used by endpoint adaptors to automatically wrap handlers in transactions.\n\t */\n\tgetDatabase(): Knex {\n\t\treturn this.db;\n\t}\n\n\t/**\n\t * Execute a callback within a Knex transaction with automatic audit handling.\n\t * The auditor is registered with the transaction and audits are flushed\n\t * before the transaction commits.\n\t *\n\t * If the provided db connection is already a transaction, it will be reused\n\t * instead of opening a savepoint.\n\t */\n\tasync withTransaction<T>(\n\t\tauditor: TransactionAwareAuditor<Knex.Transaction>,\n\t\tcallback: () => Promise<T>,\n\t\tdb?: DatabaseConnection,\n\t): Promise<T> {\n\t\tconst connection = db ?? this.db;\n\n\t\t// If already in a transaction, reuse it\n\t\tif (isActiveTransaction(connection)) {\n\t\t\tauditor.setTransaction(connection);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(connection);\n\t\t\treturn result;\n\t\t}\n\n\t\t// Create new transaction\n\t\treturn connection.transaction(async (trx) => {\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tprivate applyFilters(query: any, options: AuditQueryOptions): any {\n\t\t// Type filter\n\t\tif (options.type !== undefined) {\n\t\t\tif (Array.isArray(options.type)) {\n\t\t\t\tquery = query.whereIn('type', options.type);\n\t\t\t} else {\n\t\t\t\tquery = query.where('type', options.type);\n\t\t\t}\n\t\t}\n\n\t\t// Entity ID filter\n\t\tif (options.entityId !== undefined) {\n\t\t\tconst entityId =\n\t\t\t\ttypeof options.entityId === 'string'\n\t\t\t\t\t? options.entityId\n\t\t\t\t\t: JSON.stringify(options.entityId);\n\t\t\tquery = query.where('entityId', entityId);\n\t\t}\n\n\t\t// Table filter\n\t\tif (options.table !== undefined) {\n\t\t\tquery = query.where('table', options.table);\n\t\t}\n\n\t\t// Actor ID filter\n\t\tif (options.actorId !== undefined) {\n\t\t\tquery = query.where('actorId', options.actorId);\n\t\t}\n\n\t\t// Date range filters\n\t\tif (options.from !== undefined) {\n\t\t\tquery = query.where('timestamp', '>=', options.from);\n\t\t}\n\t\tif (options.to !== undefined) {\n\t\t\tquery = query.where('timestamp', '<=', options.to);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tprivate toRow(record: AuditRecord): AuditLogTable {\n\t\t// If autoId is true, let database generate ID (ignore record.id)\n\t\t// If autoId is false (default), use record.id or generate with nanoid\n\t\tconst id = this.autoId ? undefined : record.id || nanoid();\n\n\t\treturn {\n\t\t\t...(id && { id }),\n\t\t\ttype: record.type,\n\t\t\toperation: record.operation,\n\t\t\ttable: record.table ?? null,\n\t\t\tentityId:\n\t\t\t\trecord.entityId === undefined\n\t\t\t\t\t? null\n\t\t\t\t\t: typeof record.entityId === 'string'\n\t\t\t\t\t\t? record.entityId\n\t\t\t\t\t\t: JSON.stringify(record.entityId),\n\t\t\toldValues: this.toJsonColumn(record.oldValues),\n\t\t\tnewValues: this.toJsonColumn(record.newValues),\n\t\t\tpayload: this.toJsonColumn(record.payload),\n\t\t\ttimestamp: record.timestamp,\n\t\t\tactorId: record.actor?.id ?? null,\n\t\t\tactorType: record.actor?.type ?? null,\n\t\t\tactorData:\n\t\t\t\trecord.actor !== undefined\n\t\t\t\t\t? this.toJsonColumn(this.getActorData(record.actor))\n\t\t\t\t\t: null,\n\t\t\tmetadata: this.toJsonColumn(record.metadata),\n\t\t} as AuditLogTable;\n\t}\n\n\tprivate fromRow(row: AuditLogTable): AuditRecord {\n\t\tconst actor =\n\t\t\trow.actorId !== null || row.actorType !== null\n\t\t\t\t? {\n\t\t\t\t\t\tid: row.actorId ?? undefined,\n\t\t\t\t\t\ttype: row.actorType ?? undefined,\n\t\t\t\t\t\t...(row.actorData ? this.parseJson(row.actorData) : {}),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\ttype: row.type,\n\t\t\toperation: row.operation as AuditRecord['operation'],\n\t\t\ttable: row.table ?? undefined,\n\t\t\tentityId: row.entityId ? this.parseEntityId(row.entityId) : undefined,\n\t\t\toldValues: row.oldValues ? this.parseJson(row.oldValues) : undefined,\n\t\t\tnewValues: row.newValues ? this.parseJson(row.newValues) : undefined,\n\t\t\tpayload: row.payload ? this.parseJson(row.payload) : undefined,\n\t\t\ttimestamp: row.timestamp,\n\t\t\tactor,\n\t\t\tmetadata: row.metadata ? this.parseJson(row.metadata) : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Serialize a JSON value for insertion.\n\t *\n\t * Unlike Kysely, Knex has no per-column type information to tell a json/jsonb\n\t * column from an object it should bind as a composite value, so plain objects\n\t * are stringified before they reach the driver.\n\t */\n\tprivate toJsonColumn(value: unknown): string | null {\n\t\tif (value === undefined || value === null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn JSON.stringify(value);\n\t}\n\n\t/**\n\t * Parse a JSON value that may already be parsed (e.g., from jsonb columns).\n\t */\n\tprivate parseJson(value: unknown): Record<string, unknown> {\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn value as Record<string, unknown>;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\treturn JSON.parse(value);\n\t\t}\n\t\treturn {};\n\t}\n\n\tprivate getActorData(\n\t\tactor: NonNullable<AuditRecord['actor']>,\n\t): Record<string, unknown> {\n\t\tconst { id, type, ...rest } = actor;\n\t\treturn rest;\n\t}\n\n\tprivate parseEntityId(entityId: string): string | Record<string, unknown> {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(entityId);\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t\treturn entityId;\n\t\t} catch {\n\t\t\treturn entityId;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
package/dist/kysely.cjs CHANGED
@@ -1,6 +1,5 @@
1
- const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const nanoid = require_chunk.__toESM(require("nanoid"));
3
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let nanoid = require("nanoid");
4
3
  //#region src/kysely.ts
5
4
  /**
6
5
  * Execute a callback within a database transaction with automatic audit handling.
@@ -105,8 +104,7 @@ var KyselyAuditStorage = class {
105
104
  query = query.orderBy(orderBy === "timestamp" ? "timestamp" : "type", orderDirection);
106
105
  if (options.limit !== void 0) query = query.limit(options.limit);
107
106
  if (options.offset !== void 0) query = query.offset(options.offset);
108
- const rows = await query.execute();
109
- return rows.map((row) => this.fromRow(row));
107
+ return (await query.execute()).map((row) => this.fromRow(row));
110
108
  }
111
109
  async count(options) {
112
110
  let query = this.db.selectFrom(this.tableName).select((eb) => eb.fn.count("id").as("count"));
@@ -146,8 +144,10 @@ var KyselyAuditStorage = class {
146
144
  });
147
145
  }
148
146
  applyFilters(query, options) {
149
- if (options.type !== void 0) if (Array.isArray(options.type)) query = query.where("type", "in", options.type);
150
- else query = query.where("type", "=", options.type);
147
+ if (options.type !== void 0) {
148
+ if (Array.isArray(options.type)) query = query.where("type", "in", options.type);
149
+ else query = query.where("type", "=", options.type);
150
+ }
151
151
  if (options.entityId !== void 0) {
152
152
  const entityId = typeof options.entityId === "string" ? options.entityId : JSON.stringify(options.entityId);
153
153
  query = query.where("entityId", "=", entityId);
@@ -205,7 +205,7 @@ var KyselyAuditStorage = class {
205
205
  return {};
206
206
  }
207
207
  getActorData(actor) {
208
- const { id, type,...rest } = actor;
208
+ const { id, type, ...rest } = actor;
209
209
  return rest;
210
210
  }
211
211
  parseEntityId(entityId) {
@@ -218,8 +218,8 @@ var KyselyAuditStorage = class {
218
218
  }
219
219
  }
220
220
  };
221
-
222
221
  //#endregion
223
222
  exports.KyselyAuditStorage = KyselyAuditStorage;
224
223
  exports.withAuditableTransaction = withAuditableTransaction;
224
+
225
225
  //# sourceMappingURL=kysely.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"kysely.cjs","names":["db: DatabaseConnection<DB>","auditor: TransactionAwareAuditor<Transaction<DB>>","cb: (trx: Transaction<DB>) => Promise<T>","settings?: TransactionSettings","trx: Transaction<DB>","config: KyselyAuditStorageConfig<DB>","records: AuditRecord[]","trx?: unknown","options: AuditQueryOptions","row: AuditLogTable","options: Omit<AuditQueryOptions, 'limit' | 'offset'>","eb: any","callback: () => Promise<T>","db?: DatabaseConnection<DB>","query: any","record: AuditRecord","value: unknown","actor: NonNullable<AuditRecord['actor']>","entityId: string"],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n\tControlledTransaction,\n\tIsolationLevel,\n\tKysely,\n\tTransaction,\n} from 'kysely';\nimport { nanoid } from 'nanoid';\nimport type {\n\tAuditQueryOptions,\n\tAuditStorage,\n\tTransactionAwareAuditor,\n} from './storage';\nimport type { AuditRecord } from './types';\n\nexport type { TransactionAwareAuditor };\n\nexport interface TransactionSettings {\n\tisolationLevel?: IsolationLevel;\n}\n\nexport type DatabaseConnection<T> =\n\t| ControlledTransaction<T>\n\t| Kysely<T>\n\t| Transaction<T>;\n\n/**\n * Execute a callback within a database transaction with automatic audit handling.\n *\n * This wrapper ensures that:\n * 1. The transaction is automatically registered with the auditor\n * 2. Manual audits (via `auditor.audit()`) are flushed BEFORE the transaction commits\n * 3. If audit flush fails, the entire transaction rolls back\n * 4. If the callback fails, audits are NOT written (atomic consistency)\n *\n * **Note:** Declarative audits (defined via `.audit([...])` on the endpoint builder)\n * are processed AFTER the handler returns, so they run outside this transaction.\n * If you need all audits to be atomic with your database operations, use manual\n * audits via `auditor.audit()` inside this wrapper.\n *\n * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)\n * @param auditor - Auditor instance that will receive the transaction\n * @param cb - Callback to execute within the transaction\n * @param settings - Optional transaction settings (isolation level)\n * @returns The result of the callback\n *\n * @example\n * ```typescript\n * import { withAuditableTransaction } from '@geekmidas/audit/kysely';\n *\n * const result = await withAuditableTransaction(\n * services.database,\n * auditor,\n * async (trx) => {\n * const user = await trx\n * .insertInto('users')\n * .values(data)\n * .returningAll()\n * .executeTakeFirstOrThrow();\n *\n * // Manual audits are atomic with the transaction\n * auditor.audit('user.created', { userId: user.id, email: user.email });\n *\n * return user;\n * },\n * );\n * // Audits are automatically flushed inside the transaction before commit\n * ```\n */\nexport async function withAuditableTransaction<DB, T>(\n\tdb: DatabaseConnection<DB>,\n\tauditor: TransactionAwareAuditor<Transaction<DB>>,\n\tcb: (trx: Transaction<DB>) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tconst execute = async (trx: Transaction<DB>): Promise<T> => {\n\t\t// Register transaction with auditor\n\t\tauditor.setTransaction(trx);\n\n\t\t// Execute the callback\n\t\tconst result = await cb(trx);\n\n\t\t// Flush audits BEFORE transaction commits\n\t\t// If this fails, the transaction will roll back\n\t\tawait auditor.flush(trx);\n\n\t\treturn result;\n\t};\n\n\t// If already in a transaction, just run with it\n\tif (db.isTransaction) {\n\t\treturn execute(db as Transaction<DB>);\n\t}\n\n\tconst builder = db.transaction();\n\n\tif (settings?.isolationLevel) {\n\t\treturn builder.setIsolationLevel(settings.isolationLevel).execute(execute);\n\t}\n\n\treturn builder.execute(execute);\n}\n\n/**\n * Database table interface for audit records.\n * Use this to define your audit_logs table in your Kysely database schema.\n *\n * Column names use snake_case to match standard PostgreSQL conventions.\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * // ... other tables\n * }\n * ```\n */\nexport interface AuditLogTable {\n\tid: string;\n\ttype: string;\n\toperation: string;\n\ttable: string | null;\n\tentityId: string | null;\n\toldValues: unknown | null;\n\tnewValues: unknown | null;\n\tpayload: unknown | null;\n\ttimestamp: Date;\n\tactorId: string | null;\n\tactorType: string | null;\n\tactorData: unknown | null;\n\tmetadata: unknown | null;\n}\n\n/**\n * Insertable version of AuditLogTable where id is optional.\n * Use this when your database auto-generates IDs or when using autoId option.\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * }\n *\n * // For insertions where id is auto-generated\n * type NewAuditLog = InsertableAuditLogTable;\n * ```\n */\nexport type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {\n\tid?: string;\n};\n\n/**\n * Configuration for KyselyAuditStorage.\n */\nexport interface KyselyAuditStorageConfig<DB> {\n\t/** Kysely database instance */\n\tdb: Kysely<DB>;\n\t/** Table name for audit logs (must be a key in DB that extends AuditLogTable) */\n\ttableName: keyof DB & string;\n\t/**\n\t * Service name of the database service.\n\t * When set, endpoint adaptors will automatically use the audit transaction as `db`\n\t * in the handler context if the endpoint's database service has the same name.\n\t */\n\tdatabaseServiceName?: string;\n\t/**\n\t * Let the database auto-generate IDs (e.g., via DEFAULT gen_random_uuid()).\n\t * When true, the ID field is omitted from inserts if not provided.\n\t * When false (default), IDs are generated using nanoid if not provided.\n\t * @default false\n\t */\n\tautoId?: boolean;\n}\n\n/**\n * Kysely-based audit storage implementation.\n * Stores audit records in a database table using Kysely.\n *\n * @template DB - Your Kysely database schema\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * }\n *\n * const storage = new KyselyAuditStorage({\n * db: kyselyDb,\n * tableName: 'audit_logs',\n * });\n *\n * const auditor = new DefaultAuditor({\n * actor: { id: 'user-123', type: 'user' },\n * storage,\n * });\n * ```\n */\nexport class KyselyAuditStorage<DB> implements AuditStorage {\n\tprivate readonly db: Kysely<DB>;\n\tprivate readonly tableName: keyof DB & string;\n\tprivate readonly autoId: boolean;\n\treadonly databaseServiceName?: string;\n\n\tconstructor(config: KyselyAuditStorageConfig<DB>) {\n\t\tthis.db = config.db;\n\t\tthis.tableName = config.tableName;\n\t\tthis.databaseServiceName = config.databaseServiceName;\n\t\tthis.autoId = config.autoId ?? false;\n\t}\n\n\tasync write(records: AuditRecord[], trx?: unknown): Promise<void> {\n\t\tif (records.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst db = (trx as Transaction<DB>) ?? this.db;\n\t\tconst rows = records.map((record) => this.toRow(record));\n\n\t\tawait (db as any).insertInto(this.tableName).values(rows).execute();\n\t}\n\n\tasync query(options: AuditQueryOptions): Promise<AuditRecord[]> {\n\t\tlet query = (this.db as any).selectFrom(this.tableName).selectAll();\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\t// Ordering\n\t\tconst orderBy = options.orderBy ?? 'timestamp';\n\t\tconst orderDirection = options.orderDirection ?? 'desc';\n\t\tquery = query.orderBy(\n\t\t\torderBy === 'timestamp' ? 'timestamp' : 'type',\n\t\t\torderDirection,\n\t\t);\n\n\t\t// Pagination\n\t\tif (options.limit !== undefined) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\t\tif (options.offset !== undefined) {\n\t\t\tquery = query.offset(options.offset);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row: AuditLogTable) => this.fromRow(row));\n\t}\n\n\tasync count(\n\t\toptions: Omit<AuditQueryOptions, 'limit' | 'offset'>,\n\t): Promise<number> {\n\t\tlet query = (this.db as any)\n\t\t\t.selectFrom(this.tableName)\n\t\t\t.select((eb: any) => eb.fn.count('id').as('count'));\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Get the Kysely database instance for transactional operations.\n\t * Used by endpoint adaptors to automatically wrap handlers in transactions.\n\t */\n\tgetDatabase(): Kysely<DB> {\n\t\treturn this.db;\n\t}\n\n\t/**\n\t * Execute a callback within a Kysely transaction with automatic audit handling.\n\t * The auditor is registered with the transaction and audits are flushed\n\t * before the transaction commits.\n\t *\n\t * If the provided db connection is already a transaction, it will be reused\n\t * instead of creating a nested transaction.\n\t */\n\tasync withTransaction<T>(\n\t\tauditor: TransactionAwareAuditor<Transaction<DB>>,\n\t\tcallback: () => Promise<T>,\n\t\tdb?: DatabaseConnection<DB>,\n\t): Promise<T> {\n\t\tconst connection = db ?? this.db;\n\n\t\t// If already in a transaction, reuse it\n\t\tif (connection.isTransaction) {\n\t\t\tconst trx = connection as Transaction<DB>;\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t}\n\n\t\t// Create new transaction\n\t\treturn connection.transaction().execute(async (trx) => {\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tprivate applyFilters(query: any, options: AuditQueryOptions): any {\n\t\t// Type filter\n\t\tif (options.type !== undefined) {\n\t\t\tif (Array.isArray(options.type)) {\n\t\t\t\tquery = query.where('type', 'in', options.type);\n\t\t\t} else {\n\t\t\t\tquery = query.where('type', '=', options.type);\n\t\t\t}\n\t\t}\n\n\t\t// Entity ID filter\n\t\tif (options.entityId !== undefined) {\n\t\t\tconst entityId =\n\t\t\t\ttypeof options.entityId === 'string'\n\t\t\t\t\t? options.entityId\n\t\t\t\t\t: JSON.stringify(options.entityId);\n\t\t\tquery = query.where('entityId', '=', entityId);\n\t\t}\n\n\t\t// Table filter\n\t\tif (options.table !== undefined) {\n\t\t\tquery = query.where('table', '=', options.table);\n\t\t}\n\n\t\t// Actor ID filter\n\t\tif (options.actorId !== undefined) {\n\t\t\tquery = query.where('actorId', '=', options.actorId);\n\t\t}\n\n\t\t// Date range filters\n\t\tif (options.from !== undefined) {\n\t\t\tquery = query.where('timestamp', '>=', options.from);\n\t\t}\n\t\tif (options.to !== undefined) {\n\t\t\tquery = query.where('timestamp', '<=', options.to);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tprivate toRow(record: AuditRecord): AuditLogTable {\n\t\t// If autoId is true, let database generate ID (ignore record.id)\n\t\t// If autoId is false (default), use record.id or generate with nanoid\n\t\tconst id = this.autoId ? undefined : record.id || nanoid();\n\n\t\treturn {\n\t\t\t...(id && { id }),\n\t\t\ttype: record.type,\n\t\t\toperation: record.operation,\n\t\t\ttable: record.table ?? null,\n\t\t\tentityId:\n\t\t\t\trecord.entityId === undefined\n\t\t\t\t\t? null\n\t\t\t\t\t: typeof record.entityId === 'string'\n\t\t\t\t\t\t? record.entityId\n\t\t\t\t\t\t: JSON.stringify(record.entityId),\n\t\t\toldValues: record.oldValues ?? null,\n\t\t\tnewValues: record.newValues ?? null,\n\t\t\tpayload: record.payload ?? null,\n\t\t\ttimestamp: record.timestamp,\n\t\t\tactorId: record.actor?.id ?? null,\n\t\t\tactorType: record.actor?.type ?? null,\n\t\t\tactorData:\n\t\t\t\trecord.actor !== undefined ? this.getActorData(record.actor) : null,\n\t\t\tmetadata: record.metadata ?? null,\n\t\t} as AuditLogTable;\n\t}\n\n\tprivate fromRow(row: AuditLogTable): AuditRecord {\n\t\tconst actor =\n\t\t\trow.actorId !== null || row.actorType !== null\n\t\t\t\t? {\n\t\t\t\t\t\tid: row.actorId ?? undefined,\n\t\t\t\t\t\ttype: row.actorType ?? undefined,\n\t\t\t\t\t\t...(row.actorData ? this.parseJson(row.actorData) : {}),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\ttype: row.type,\n\t\t\toperation: row.operation as AuditRecord['operation'],\n\t\t\ttable: row.table ?? undefined,\n\t\t\tentityId: row.entityId ? this.parseEntityId(row.entityId) : undefined,\n\t\t\toldValues: row.oldValues ? this.parseJson(row.oldValues) : undefined,\n\t\t\tnewValues: row.newValues ? this.parseJson(row.newValues) : undefined,\n\t\t\tpayload: row.payload ? this.parseJson(row.payload) : undefined,\n\t\t\ttimestamp: row.timestamp,\n\t\t\tactor,\n\t\t\tmetadata: row.metadata ? this.parseJson(row.metadata) : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Parse a JSON value that may already be parsed (e.g., from jsonb columns).\n\t */\n\tprivate parseJson(value: unknown): Record<string, unknown> {\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn value as Record<string, unknown>;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\treturn JSON.parse(value);\n\t\t}\n\t\treturn {};\n\t}\n\n\tprivate getActorData(\n\t\tactor: NonNullable<AuditRecord['actor']>,\n\t): Record<string, unknown> {\n\t\tconst { id, type, ...rest } = actor;\n\t\treturn rest;\n\t}\n\n\tprivate parseEntityId(entityId: string): string | Record<string, unknown> {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(entityId);\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t\treturn entityId;\n\t\t} catch {\n\t\t\treturn entityId;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"kysely.cjs","names":[],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n\tControlledTransaction,\n\tIsolationLevel,\n\tKysely,\n\tTransaction,\n} from 'kysely';\nimport { nanoid } from 'nanoid';\nimport type {\n\tAuditQueryOptions,\n\tAuditStorage,\n\tTransactionAwareAuditor,\n} from './storage';\nimport type { AuditRecord } from './types';\n\nexport type { TransactionAwareAuditor };\n\nexport interface TransactionSettings {\n\tisolationLevel?: IsolationLevel;\n}\n\nexport type DatabaseConnection<T> =\n\t| ControlledTransaction<T>\n\t| Kysely<T>\n\t| Transaction<T>;\n\n/**\n * Execute a callback within a database transaction with automatic audit handling.\n *\n * This wrapper ensures that:\n * 1. The transaction is automatically registered with the auditor\n * 2. Manual audits (via `auditor.audit()`) are flushed BEFORE the transaction commits\n * 3. If audit flush fails, the entire transaction rolls back\n * 4. If the callback fails, audits are NOT written (atomic consistency)\n *\n * **Note:** Declarative audits (defined via `.audit([...])` on the endpoint builder)\n * are processed AFTER the handler returns, so they run outside this transaction.\n * If you need all audits to be atomic with your database operations, use manual\n * audits via `auditor.audit()` inside this wrapper.\n *\n * @param db - Database connection (Kysely, Transaction, or ControlledTransaction)\n * @param auditor - Auditor instance that will receive the transaction\n * @param cb - Callback to execute within the transaction\n * @param settings - Optional transaction settings (isolation level)\n * @returns The result of the callback\n *\n * @example\n * ```typescript\n * import { withAuditableTransaction } from '@geekmidas/audit/kysely';\n *\n * const result = await withAuditableTransaction(\n * services.database,\n * auditor,\n * async (trx) => {\n * const user = await trx\n * .insertInto('users')\n * .values(data)\n * .returningAll()\n * .executeTakeFirstOrThrow();\n *\n * // Manual audits are atomic with the transaction\n * auditor.audit('user.created', { userId: user.id, email: user.email });\n *\n * return user;\n * },\n * );\n * // Audits are automatically flushed inside the transaction before commit\n * ```\n */\nexport async function withAuditableTransaction<DB, T>(\n\tdb: DatabaseConnection<DB>,\n\tauditor: TransactionAwareAuditor<Transaction<DB>>,\n\tcb: (trx: Transaction<DB>) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tconst execute = async (trx: Transaction<DB>): Promise<T> => {\n\t\t// Register transaction with auditor\n\t\tauditor.setTransaction(trx);\n\n\t\t// Execute the callback\n\t\tconst result = await cb(trx);\n\n\t\t// Flush audits BEFORE transaction commits\n\t\t// If this fails, the transaction will roll back\n\t\tawait auditor.flush(trx);\n\n\t\treturn result;\n\t};\n\n\t// If already in a transaction, just run with it\n\tif (db.isTransaction) {\n\t\treturn execute(db as Transaction<DB>);\n\t}\n\n\tconst builder = db.transaction();\n\n\tif (settings?.isolationLevel) {\n\t\treturn builder.setIsolationLevel(settings.isolationLevel).execute(execute);\n\t}\n\n\treturn builder.execute(execute);\n}\n\n/**\n * Database table interface for audit records.\n * Use this to define your audit_logs table in your Kysely database schema.\n *\n * Column names use snake_case to match standard PostgreSQL conventions.\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * // ... other tables\n * }\n * ```\n */\nexport interface AuditLogTable {\n\tid: string;\n\ttype: string;\n\toperation: string;\n\ttable: string | null;\n\tentityId: string | null;\n\toldValues: unknown | null;\n\tnewValues: unknown | null;\n\tpayload: unknown | null;\n\ttimestamp: Date;\n\tactorId: string | null;\n\tactorType: string | null;\n\tactorData: unknown | null;\n\tmetadata: unknown | null;\n}\n\n/**\n * Insertable version of AuditLogTable where id is optional.\n * Use this when your database auto-generates IDs or when using autoId option.\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * }\n *\n * // For insertions where id is auto-generated\n * type NewAuditLog = InsertableAuditLogTable;\n * ```\n */\nexport type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {\n\tid?: string;\n};\n\n/**\n * Configuration for KyselyAuditStorage.\n */\nexport interface KyselyAuditStorageConfig<DB> {\n\t/** Kysely database instance */\n\tdb: Kysely<DB>;\n\t/** Table name for audit logs (must be a key in DB that extends AuditLogTable) */\n\ttableName: keyof DB & string;\n\t/**\n\t * Service name of the database service.\n\t * When set, endpoint adaptors will automatically use the audit transaction as `db`\n\t * in the handler context if the endpoint's database service has the same name.\n\t */\n\tdatabaseServiceName?: string;\n\t/**\n\t * Let the database auto-generate IDs (e.g., via DEFAULT gen_random_uuid()).\n\t * When true, the ID field is omitted from inserts if not provided.\n\t * When false (default), IDs are generated using nanoid if not provided.\n\t * @default false\n\t */\n\tautoId?: boolean;\n}\n\n/**\n * Kysely-based audit storage implementation.\n * Stores audit records in a database table using Kysely.\n *\n * @template DB - Your Kysely database schema\n *\n * @example\n * ```typescript\n * interface Database {\n * audit_logs: AuditLogTable;\n * }\n *\n * const storage = new KyselyAuditStorage({\n * db: kyselyDb,\n * tableName: 'audit_logs',\n * });\n *\n * const auditor = new DefaultAuditor({\n * actor: { id: 'user-123', type: 'user' },\n * storage,\n * });\n * ```\n */\nexport class KyselyAuditStorage<DB> implements AuditStorage {\n\tprivate readonly db: Kysely<DB>;\n\tprivate readonly tableName: keyof DB & string;\n\tprivate readonly autoId: boolean;\n\treadonly databaseServiceName?: string;\n\n\tconstructor(config: KyselyAuditStorageConfig<DB>) {\n\t\tthis.db = config.db;\n\t\tthis.tableName = config.tableName;\n\t\tthis.databaseServiceName = config.databaseServiceName;\n\t\tthis.autoId = config.autoId ?? false;\n\t}\n\n\tasync write(records: AuditRecord[], trx?: unknown): Promise<void> {\n\t\tif (records.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst db = (trx as Transaction<DB>) ?? this.db;\n\t\tconst rows = records.map((record) => this.toRow(record));\n\n\t\tawait (db as any).insertInto(this.tableName).values(rows).execute();\n\t}\n\n\tasync query(options: AuditQueryOptions): Promise<AuditRecord[]> {\n\t\tlet query = (this.db as any).selectFrom(this.tableName).selectAll();\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\t// Ordering\n\t\tconst orderBy = options.orderBy ?? 'timestamp';\n\t\tconst orderDirection = options.orderDirection ?? 'desc';\n\t\tquery = query.orderBy(\n\t\t\torderBy === 'timestamp' ? 'timestamp' : 'type',\n\t\t\torderDirection,\n\t\t);\n\n\t\t// Pagination\n\t\tif (options.limit !== undefined) {\n\t\t\tquery = query.limit(options.limit);\n\t\t}\n\t\tif (options.offset !== undefined) {\n\t\t\tquery = query.offset(options.offset);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row: AuditLogTable) => this.fromRow(row));\n\t}\n\n\tasync count(\n\t\toptions: Omit<AuditQueryOptions, 'limit' | 'offset'>,\n\t): Promise<number> {\n\t\tlet query = (this.db as any)\n\t\t\t.selectFrom(this.tableName)\n\t\t\t.select((eb: any) => eb.fn.count('id').as('count'));\n\n\t\tquery = this.applyFilters(query, options);\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Get the Kysely database instance for transactional operations.\n\t * Used by endpoint adaptors to automatically wrap handlers in transactions.\n\t */\n\tgetDatabase(): Kysely<DB> {\n\t\treturn this.db;\n\t}\n\n\t/**\n\t * Execute a callback within a Kysely transaction with automatic audit handling.\n\t * The auditor is registered with the transaction and audits are flushed\n\t * before the transaction commits.\n\t *\n\t * If the provided db connection is already a transaction, it will be reused\n\t * instead of creating a nested transaction.\n\t */\n\tasync withTransaction<T>(\n\t\tauditor: TransactionAwareAuditor<Transaction<DB>>,\n\t\tcallback: () => Promise<T>,\n\t\tdb?: DatabaseConnection<DB>,\n\t): Promise<T> {\n\t\tconst connection = db ?? this.db;\n\n\t\t// If already in a transaction, reuse it\n\t\tif (connection.isTransaction) {\n\t\t\tconst trx = connection as Transaction<DB>;\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t}\n\n\t\t// Create new transaction\n\t\treturn connection.transaction().execute(async (trx) => {\n\t\t\tauditor.setTransaction(trx);\n\t\t\tconst result = await callback();\n\t\t\tawait auditor.flush(trx);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tprivate applyFilters(query: any, options: AuditQueryOptions): any {\n\t\t// Type filter\n\t\tif (options.type !== undefined) {\n\t\t\tif (Array.isArray(options.type)) {\n\t\t\t\tquery = query.where('type', 'in', options.type);\n\t\t\t} else {\n\t\t\t\tquery = query.where('type', '=', options.type);\n\t\t\t}\n\t\t}\n\n\t\t// Entity ID filter\n\t\tif (options.entityId !== undefined) {\n\t\t\tconst entityId =\n\t\t\t\ttypeof options.entityId === 'string'\n\t\t\t\t\t? options.entityId\n\t\t\t\t\t: JSON.stringify(options.entityId);\n\t\t\tquery = query.where('entityId', '=', entityId);\n\t\t}\n\n\t\t// Table filter\n\t\tif (options.table !== undefined) {\n\t\t\tquery = query.where('table', '=', options.table);\n\t\t}\n\n\t\t// Actor ID filter\n\t\tif (options.actorId !== undefined) {\n\t\t\tquery = query.where('actorId', '=', options.actorId);\n\t\t}\n\n\t\t// Date range filters\n\t\tif (options.from !== undefined) {\n\t\t\tquery = query.where('timestamp', '>=', options.from);\n\t\t}\n\t\tif (options.to !== undefined) {\n\t\t\tquery = query.where('timestamp', '<=', options.to);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tprivate toRow(record: AuditRecord): AuditLogTable {\n\t\t// If autoId is true, let database generate ID (ignore record.id)\n\t\t// If autoId is false (default), use record.id or generate with nanoid\n\t\tconst id = this.autoId ? undefined : record.id || nanoid();\n\n\t\treturn {\n\t\t\t...(id && { id }),\n\t\t\ttype: record.type,\n\t\t\toperation: record.operation,\n\t\t\ttable: record.table ?? null,\n\t\t\tentityId:\n\t\t\t\trecord.entityId === undefined\n\t\t\t\t\t? null\n\t\t\t\t\t: typeof record.entityId === 'string'\n\t\t\t\t\t\t? record.entityId\n\t\t\t\t\t\t: JSON.stringify(record.entityId),\n\t\t\toldValues: record.oldValues ?? null,\n\t\t\tnewValues: record.newValues ?? null,\n\t\t\tpayload: record.payload ?? null,\n\t\t\ttimestamp: record.timestamp,\n\t\t\tactorId: record.actor?.id ?? null,\n\t\t\tactorType: record.actor?.type ?? null,\n\t\t\tactorData:\n\t\t\t\trecord.actor !== undefined ? this.getActorData(record.actor) : null,\n\t\t\tmetadata: record.metadata ?? null,\n\t\t} as AuditLogTable;\n\t}\n\n\tprivate fromRow(row: AuditLogTable): AuditRecord {\n\t\tconst actor =\n\t\t\trow.actorId !== null || row.actorType !== null\n\t\t\t\t? {\n\t\t\t\t\t\tid: row.actorId ?? undefined,\n\t\t\t\t\t\ttype: row.actorType ?? undefined,\n\t\t\t\t\t\t...(row.actorData ? this.parseJson(row.actorData) : {}),\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\ttype: row.type,\n\t\t\toperation: row.operation as AuditRecord['operation'],\n\t\t\ttable: row.table ?? undefined,\n\t\t\tentityId: row.entityId ? this.parseEntityId(row.entityId) : undefined,\n\t\t\toldValues: row.oldValues ? this.parseJson(row.oldValues) : undefined,\n\t\t\tnewValues: row.newValues ? this.parseJson(row.newValues) : undefined,\n\t\t\tpayload: row.payload ? this.parseJson(row.payload) : undefined,\n\t\t\ttimestamp: row.timestamp,\n\t\t\tactor,\n\t\t\tmetadata: row.metadata ? this.parseJson(row.metadata) : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Parse a JSON value that may already be parsed (e.g., from jsonb columns).\n\t */\n\tprivate parseJson(value: unknown): Record<string, unknown> {\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn value as Record<string, unknown>;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\treturn JSON.parse(value);\n\t\t}\n\t\treturn {};\n\t}\n\n\tprivate getActorData(\n\t\tactor: NonNullable<AuditRecord['actor']>,\n\t): Record<string, unknown> {\n\t\tconst { id, type, ...rest } = actor;\n\t\treturn rest;\n\t}\n\n\tprivate parseEntityId(entityId: string): string | Record<string, unknown> {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(entityId);\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t\treturn entityId;\n\t\t} catch {\n\t\t\treturn entityId;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
package/dist/kysely.d.cts CHANGED
@@ -1,11 +1,10 @@
1
- import { AuditQueryOptions, AuditRecord, AuditStorage, TransactionAwareAuditor } from "./Auditor-DpBHXIJM.cjs";
1
+ import { h as TransactionAwareAuditor, m as AuditStorage, o as AuditRecord, p as AuditQueryOptions } from "./Auditor-BBXXCD0v.cjs";
2
2
  import { ControlledTransaction, IsolationLevel, Kysely, Transaction } from "kysely";
3
-
4
3
  //#region src/kysely.d.ts
5
- interface TransactionSettings {
4
+ export interface TransactionSettings {
6
5
  isolationLevel?: IsolationLevel;
7
6
  }
8
- type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
7
+ export type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
9
8
  /**
10
9
  * Execute a callback within a database transaction with automatic audit handling.
11
10
  *
@@ -49,7 +48,7 @@ type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<
49
48
  * // Audits are automatically flushed inside the transaction before commit
50
49
  * ```
51
50
  */
52
- declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, auditor: TransactionAwareAuditor<Transaction<DB>>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
51
+ export declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, auditor: TransactionAwareAuditor<Transaction<DB>>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
53
52
  /**
54
53
  * Database table interface for audit records.
55
54
  * Use this to define your audit_logs table in your Kysely database schema.
@@ -64,7 +63,7 @@ declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, aud
64
63
  * }
65
64
  * ```
66
65
  */
67
- interface AuditLogTable {
66
+ export interface AuditLogTable {
68
67
  id: string;
69
68
  type: string;
70
69
  operation: string;
@@ -93,13 +92,13 @@ interface AuditLogTable {
93
92
  * type NewAuditLog = InsertableAuditLogTable;
94
93
  * ```
95
94
  */
96
- type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
95
+ export type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
97
96
  id?: string;
98
97
  };
99
98
  /**
100
99
  * Configuration for KyselyAuditStorage.
101
100
  */
102
- interface KyselyAuditStorageConfig<DB> {
101
+ export interface KyselyAuditStorageConfig<DB> {
103
102
  /** Kysely database instance */
104
103
  db: Kysely<DB>;
105
104
  /** Table name for audit logs (must be a key in DB that extends AuditLogTable) */
@@ -141,7 +140,7 @@ interface KyselyAuditStorageConfig<DB> {
141
140
  * });
142
141
  * ```
143
142
  */
144
- declare class KyselyAuditStorage<DB> implements AuditStorage {
143
+ export declare class KyselyAuditStorage<DB> implements AuditStorage {
145
144
  private readonly db;
146
145
  private readonly tableName;
147
146
  private readonly autoId;
@@ -174,7 +173,6 @@ declare class KyselyAuditStorage<DB> implements AuditStorage {
174
173
  private getActorData;
175
174
  private parseEntityId;
176
175
  }
177
- //# sourceMappingURL=kysely.d.ts.map
178
176
  //#endregion
179
- export { AuditLogTable, DatabaseConnection, InsertableAuditLogTable, KyselyAuditStorage, KyselyAuditStorageConfig, TransactionAwareAuditor, TransactionSettings, withAuditableTransaction };
177
+ export type { TransactionAwareAuditor };
180
178
  //# sourceMappingURL=kysely.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kysely.d.cts","names":[],"sources":["../src/kysely.ts"],"sourcesContent":[],"mappings":";;;;AAgBiB,UAAA,mBAAA,CACC;EAGN,cAAA,CAAA,EAHM,cAGY;;AACL,KADb,kBACa,CAAA,CAAA,CAAA,GAAtB,qBAAsB,CAAA,CAAA,CAAA,GACtB,MADsB,CACf,CADe,CAAA,GAEtB,WAFsB,CAEV,CAFU,CAAA;;;;;;AAEX;AA6Cd;;;;;;;;;;;;;;AAKU;AA2CV;AA8BA;;;;AAA0C;AAO1C;;;;;AAIoB;AAuCpB;;;;;;;;;;AAkDW,iBAlLW,wBAkLX,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,EAjLN,kBAiLM,CAjLa,EAiLb,CAAA,EAAA,OAAA,EAhLD,uBAgLC,CAhLuB,WAgLvB,CAhLmC,EAgLnC,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,GAAA,EA/KA,WA+KA,CA/KY,EA+KZ,CAAA,EAAA,GA/KoB,OA+KpB,CA/K4B,CA+K5B,CAAA,EAAA,QAAA,CAAA,EA9KC,mBA8KD,CAAA,EA7KR,OA6KQ,CA7KA,CA6KA,CAAA;;;;;;;;;;;;;;AAlDgD;UAhF1C,aAAA;;;;;;;;;aASL;;;;;;;;;;;;;;;;;;;;KAqBA,uBAAA,GAA0B,KAAK;;;;;;UAO1B;;MAEZ,OAAO;;mBAEM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuCL,kCAAkC;;;;;sBAM1B,yBAAyB;iBAOxB,+BAA+B;iBAW/B,oBAAoB,QAAQ;iBA0BvC,KAAK,yCACZ;;;;;iBAeY,OAAO;;;;;;;;;8BAaZ,wBAAwB,YAAY,sBAC7B,QAAQ,SACnB,mBAAmB,MACtB,QAAQ"}
1
+ {"version":3,"file":"kysely.d.cts","names":[],"sources":["../src/kysely.ts"],"mappings":";;;iBAgBiB;EAChB,iBAAiB;;YAGN,mBAAmB,KAC5B,sBAAsB,KACtB,OAAO,KACP,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6CO,yBAAyB,IAAI,GAClD,IAAI,mBAAmB,KACvB,SAAS,wBAAwB,YAAY,MAC7C,KAAK,KAAK,YAAY,QAAQ,QAAQ,IACtC,WAAW,sBACT,QAAQ;;;;;;;;;;;;;;;iBA2CM;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA;EACA;EACA;;;;;;;;;;;;;;;;YAiBW,0BAA0B,KAAK;EAC1C;;;;;iBAMgB,yBAAyB;;EAEzC,IAAI,OAAO;;EAEX,iBAAiB;;;;;;EAMjB;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;;;;;;;qBA0BY,mBAAmB,eAAe;mBAC7B;mBACA;mBACA;WACR;EAET,YAAY,QAAQ,yBAAyB;EAOvC,MAAM,SAAS,eAAe,gBAAgB;EAW9C,MAAM,SAAS,oBAAoB,QAAQ;EAyB3C,MACL,SAAS,KAAK,yCACZ;;;;;EAeH,eAAe,OAAO;;;;;;;;;EAYhB,gBAAgB,GACrB,SAAS,wBAAwB,YAAY,MAC7C,gBAAgB,QAAQ,IACxB,KAAK,mBAAmB,MACtB,QAAQ;UAqBH;UAwCA;UA4BA;;;;UA4BA;UAUA;UAOA"}
package/dist/kysely.d.mts CHANGED
@@ -1,11 +1,10 @@
1
- import { AuditQueryOptions, AuditRecord, AuditStorage, TransactionAwareAuditor } from "./Auditor-CW2NrfHS.mjs";
1
+ import { h as TransactionAwareAuditor, m as AuditStorage, o as AuditRecord, p as AuditQueryOptions } from "./Auditor-BBXXCD0v.mjs";
2
2
  import { ControlledTransaction, IsolationLevel, Kysely, Transaction } from "kysely";
3
-
4
3
  //#region src/kysely.d.ts
5
- interface TransactionSettings {
4
+ export interface TransactionSettings {
6
5
  isolationLevel?: IsolationLevel;
7
6
  }
8
- type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
7
+ export type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<T>;
9
8
  /**
10
9
  * Execute a callback within a database transaction with automatic audit handling.
11
10
  *
@@ -49,7 +48,7 @@ type DatabaseConnection<T> = ControlledTransaction<T> | Kysely<T> | Transaction<
49
48
  * // Audits are automatically flushed inside the transaction before commit
50
49
  * ```
51
50
  */
52
- declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, auditor: TransactionAwareAuditor<Transaction<DB>>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
51
+ export declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, auditor: TransactionAwareAuditor<Transaction<DB>>, cb: (trx: Transaction<DB>) => Promise<T>, settings?: TransactionSettings): Promise<T>;
53
52
  /**
54
53
  * Database table interface for audit records.
55
54
  * Use this to define your audit_logs table in your Kysely database schema.
@@ -64,7 +63,7 @@ declare function withAuditableTransaction<DB, T>(db: DatabaseConnection<DB>, aud
64
63
  * }
65
64
  * ```
66
65
  */
67
- interface AuditLogTable {
66
+ export interface AuditLogTable {
68
67
  id: string;
69
68
  type: string;
70
69
  operation: string;
@@ -93,13 +92,13 @@ interface AuditLogTable {
93
92
  * type NewAuditLog = InsertableAuditLogTable;
94
93
  * ```
95
94
  */
96
- type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
95
+ export type InsertableAuditLogTable = Omit<AuditLogTable, 'id'> & {
97
96
  id?: string;
98
97
  };
99
98
  /**
100
99
  * Configuration for KyselyAuditStorage.
101
100
  */
102
- interface KyselyAuditStorageConfig<DB> {
101
+ export interface KyselyAuditStorageConfig<DB> {
103
102
  /** Kysely database instance */
104
103
  db: Kysely<DB>;
105
104
  /** Table name for audit logs (must be a key in DB that extends AuditLogTable) */
@@ -141,7 +140,7 @@ interface KyselyAuditStorageConfig<DB> {
141
140
  * });
142
141
  * ```
143
142
  */
144
- declare class KyselyAuditStorage<DB> implements AuditStorage {
143
+ export declare class KyselyAuditStorage<DB> implements AuditStorage {
145
144
  private readonly db;
146
145
  private readonly tableName;
147
146
  private readonly autoId;
@@ -174,7 +173,6 @@ declare class KyselyAuditStorage<DB> implements AuditStorage {
174
173
  private getActorData;
175
174
  private parseEntityId;
176
175
  }
177
- //# sourceMappingURL=kysely.d.ts.map
178
176
  //#endregion
179
- export { AuditLogTable, DatabaseConnection, InsertableAuditLogTable, KyselyAuditStorage, KyselyAuditStorageConfig, TransactionAwareAuditor, TransactionSettings, withAuditableTransaction };
177
+ export type { TransactionAwareAuditor };
180
178
  //# sourceMappingURL=kysely.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/kysely.ts"],"sourcesContent":[],"mappings":";;;;AAgBiB,UAAA,mBAAA,CACC;EAGN,cAAA,CAAA,EAHM,cAGY;;AACL,KADb,kBACa,CAAA,CAAA,CAAA,GAAtB,qBAAsB,CAAA,CAAA,CAAA,GACtB,MADsB,CACf,CADe,CAAA,GAEtB,WAFsB,CAEV,CAFU,CAAA;;;;;;AAEX;AA6Cd;;;;;;;;;;;;;;AAKU;AA2CV;AA8BA;;;;AAA0C;AAO1C;;;;;AAIoB;AAuCpB;;;;;;;;;;AAkDW,iBAlLW,wBAkLX,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,EAjLN,kBAiLM,CAjLa,EAiLb,CAAA,EAAA,OAAA,EAhLD,uBAgLC,CAhLuB,WAgLvB,CAhLmC,EAgLnC,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,GAAA,EA/KA,WA+KA,CA/KY,EA+KZ,CAAA,EAAA,GA/KoB,OA+KpB,CA/K4B,CA+K5B,CAAA,EAAA,QAAA,CAAA,EA9KC,mBA8KD,CAAA,EA7KR,OA6KQ,CA7KA,CA6KA,CAAA;;;;;;;;;;;;;;AAlDgD;UAhF1C,aAAA;;;;;;;;;aASL;;;;;;;;;;;;;;;;;;;;KAqBA,uBAAA,GAA0B,KAAK;;;;;;UAO1B;;MAEZ,OAAO;;mBAEM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuCL,kCAAkC;;;;;sBAM1B,yBAAyB;iBAOxB,+BAA+B;iBAW/B,oBAAoB,QAAQ;iBA0BvC,KAAK,yCACZ;;;;;iBAeY,OAAO;;;;;;;;;8BAaZ,wBAAwB,YAAY,sBAC7B,QAAQ,SACnB,mBAAmB,MACtB,QAAQ"}
1
+ {"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/kysely.ts"],"mappings":";;;iBAgBiB;EAChB,iBAAiB;;YAGN,mBAAmB,KAC5B,sBAAsB,KACtB,OAAO,KACP,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6CO,yBAAyB,IAAI,GAClD,IAAI,mBAAmB,KACvB,SAAS,wBAAwB,YAAY,MAC7C,KAAK,KAAK,YAAY,QAAQ,QAAQ,IACtC,WAAW,sBACT,QAAQ;;;;;;;;;;;;;;;iBA2CM;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EACX;EACA;EACA;EACA;;;;;;;;;;;;;;;;YAiBW,0BAA0B,KAAK;EAC1C;;;;;iBAMgB,yBAAyB;;EAEzC,IAAI,OAAO;;EAEX,iBAAiB;;;;;;EAMjB;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;;;;;;;qBA0BY,mBAAmB,eAAe;mBAC7B;mBACA;mBACA;WACR;EAET,YAAY,QAAQ,yBAAyB;EAOvC,MAAM,SAAS,eAAe,gBAAgB;EAW9C,MAAM,SAAS,oBAAoB,QAAQ;EAyB3C,MACL,SAAS,KAAK,yCACZ;;;;;EAeH,eAAe,OAAO;;;;;;;;;EAYhB,gBAAgB,GACrB,SAAS,wBAAwB,YAAY,MAC7C,gBAAgB,QAAQ,IACxB,KAAK,mBAAmB,MACtB,QAAQ;UAqBH;UAwCA;UA4BA;;;;UA4BA;UAUA;UAOA"}
package/dist/kysely.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { nanoid } from "nanoid";
2
-
3
2
  //#region src/kysely.ts
4
3
  /**
5
4
  * Execute a callback within a database transaction with automatic audit handling.
@@ -104,8 +103,7 @@ var KyselyAuditStorage = class {
104
103
  query = query.orderBy(orderBy === "timestamp" ? "timestamp" : "type", orderDirection);
105
104
  if (options.limit !== void 0) query = query.limit(options.limit);
106
105
  if (options.offset !== void 0) query = query.offset(options.offset);
107
- const rows = await query.execute();
108
- return rows.map((row) => this.fromRow(row));
106
+ return (await query.execute()).map((row) => this.fromRow(row));
109
107
  }
110
108
  async count(options) {
111
109
  let query = this.db.selectFrom(this.tableName).select((eb) => eb.fn.count("id").as("count"));
@@ -145,8 +143,10 @@ var KyselyAuditStorage = class {
145
143
  });
146
144
  }
147
145
  applyFilters(query, options) {
148
- if (options.type !== void 0) if (Array.isArray(options.type)) query = query.where("type", "in", options.type);
149
- else query = query.where("type", "=", options.type);
146
+ if (options.type !== void 0) {
147
+ if (Array.isArray(options.type)) query = query.where("type", "in", options.type);
148
+ else query = query.where("type", "=", options.type);
149
+ }
150
150
  if (options.entityId !== void 0) {
151
151
  const entityId = typeof options.entityId === "string" ? options.entityId : JSON.stringify(options.entityId);
152
152
  query = query.where("entityId", "=", entityId);
@@ -204,7 +204,7 @@ var KyselyAuditStorage = class {
204
204
  return {};
205
205
  }
206
206
  getActorData(actor) {
207
- const { id, type,...rest } = actor;
207
+ const { id, type, ...rest } = actor;
208
208
  return rest;
209
209
  }
210
210
  parseEntityId(entityId) {
@@ -217,7 +217,7 @@ var KyselyAuditStorage = class {
217
217
  }
218
218
  }
219
219
  };
220
-
221
220
  //#endregion
222
221
  export { KyselyAuditStorage, withAuditableTransaction };
222
+
223
223
  //# sourceMappingURL=kysely.mjs.map