@usebetterdev/audit-drizzle 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -308,30 +308,32 @@ function drizzleAuditAdapter(db) {
308
308
  },
309
309
  async getStats(options) {
310
310
  const sinceCondition = options?.since !== void 0 ? (0, import_drizzle_orm2.gte)(auditLogs.timestamp, options.since) : void 0;
311
+ const untilCondition = options?.until !== void 0 ? (0, import_drizzle_orm2.lt)(auditLogs.timestamp, options.until) : void 0;
312
+ const timeCondition = (0, import_drizzle_orm2.and)(sinceCondition, untilCondition);
311
313
  const summaryQuery = db.select({
312
314
  totalLogs: import_drizzle_orm2.sql`count(*)`,
313
315
  tablesAudited: import_drizzle_orm2.sql`count(DISTINCT ${auditLogs.tableName})`
314
- }).from(auditLogs).where(sinceCondition);
316
+ }).from(auditLogs).where(timeCondition);
315
317
  const eventsPerDayQuery = db.select({
316
318
  date: import_drizzle_orm2.sql`date_trunc('day', ${auditLogs.timestamp})::date`,
317
319
  count: import_drizzle_orm2.sql`count(*)`
318
- }).from(auditLogs).where(sinceCondition).groupBy(import_drizzle_orm2.sql`date_trunc('day', ${auditLogs.timestamp})`).orderBy(import_drizzle_orm2.sql`date_trunc('day', ${auditLogs.timestamp})`).limit(365);
320
+ }).from(auditLogs).where(timeCondition).groupBy(import_drizzle_orm2.sql`date_trunc('day', ${auditLogs.timestamp})`).orderBy(import_drizzle_orm2.sql`date_trunc('day', ${auditLogs.timestamp})`).limit(365);
319
321
  const topActorsQuery = db.select({
320
322
  actorId: auditLogs.actorId,
321
323
  count: import_drizzle_orm2.sql`count(*)`
322
- }).from(auditLogs).where((0, import_drizzle_orm2.and)(sinceCondition, (0, import_drizzle_orm2.isNotNull)(auditLogs.actorId))).groupBy(auditLogs.actorId).orderBy((0, import_drizzle_orm2.desc)(import_drizzle_orm2.sql`count(*)`)).limit(10);
324
+ }).from(auditLogs).where((0, import_drizzle_orm2.and)(timeCondition, (0, import_drizzle_orm2.isNotNull)(auditLogs.actorId))).groupBy(auditLogs.actorId).orderBy((0, import_drizzle_orm2.desc)(import_drizzle_orm2.sql`count(*)`)).limit(10);
323
325
  const topTablesQuery = db.select({
324
326
  tableName: auditLogs.tableName,
325
327
  count: import_drizzle_orm2.sql`count(*)`
326
- }).from(auditLogs).where(sinceCondition).groupBy(auditLogs.tableName).orderBy((0, import_drizzle_orm2.desc)(import_drizzle_orm2.sql`count(*)`)).limit(10);
328
+ }).from(auditLogs).where(timeCondition).groupBy(auditLogs.tableName).orderBy((0, import_drizzle_orm2.desc)(import_drizzle_orm2.sql`count(*)`)).limit(10);
327
329
  const operationQuery = db.select({
328
330
  operation: auditLogs.operation,
329
331
  count: import_drizzle_orm2.sql`count(*)`
330
- }).from(auditLogs).where(sinceCondition).groupBy(auditLogs.operation);
332
+ }).from(auditLogs).where(timeCondition).groupBy(auditLogs.operation);
331
333
  const severityQuery = db.select({
332
334
  severity: auditLogs.severity,
333
335
  count: import_drizzle_orm2.sql`count(*)`
334
- }).from(auditLogs).where((0, import_drizzle_orm2.and)(sinceCondition, (0, import_drizzle_orm2.isNotNull)(auditLogs.severity))).groupBy(auditLogs.severity);
336
+ }).from(auditLogs).where((0, import_drizzle_orm2.and)(timeCondition, (0, import_drizzle_orm2.isNotNull)(auditLogs.severity))).groupBy(auditLogs.severity);
335
337
  const results = await Promise.all([
336
338
  summaryQuery,
337
339
  eventsPerDayQuery,
@@ -636,30 +638,32 @@ function drizzleSqliteAuditAdapter(db) {
636
638
  },
637
639
  async getStats(options) {
638
640
  const sinceCondition = options?.since !== void 0 ? (0, import_drizzle_orm4.gte)(sqliteAuditLogs.timestamp, options.since) : void 0;
641
+ const untilCondition = options?.until !== void 0 ? (0, import_drizzle_orm4.lt)(sqliteAuditLogs.timestamp, options.until) : void 0;
642
+ const timeCondition = (0, import_drizzle_orm4.and)(sinceCondition, untilCondition);
639
643
  const summaryQuery = db.select({
640
644
  totalLogs: import_drizzle_orm4.sql`count(*)`,
641
645
  tablesAudited: import_drizzle_orm4.sql`count(DISTINCT ${sqliteAuditLogs.tableName})`
642
- }).from(sqliteAuditLogs).where(sinceCondition);
646
+ }).from(sqliteAuditLogs).where(timeCondition);
643
647
  const eventsPerDayQuery = db.select({
644
648
  date: import_drizzle_orm4.sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,
645
649
  count: import_drizzle_orm4.sql`count(*)`
646
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(import_drizzle_orm4.sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).orderBy(import_drizzle_orm4.sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).limit(365);
650
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(import_drizzle_orm4.sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).orderBy(import_drizzle_orm4.sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).limit(365);
647
651
  const topActorsQuery = db.select({
648
652
  actorId: sqliteAuditLogs.actorId,
649
653
  count: import_drizzle_orm4.sql`count(*)`
650
- }).from(sqliteAuditLogs).where((0, import_drizzle_orm4.and)(sinceCondition, (0, import_drizzle_orm4.isNotNull)(sqliteAuditLogs.actorId))).groupBy(sqliteAuditLogs.actorId).orderBy((0, import_drizzle_orm4.desc)(import_drizzle_orm4.sql`count(*)`)).limit(10);
654
+ }).from(sqliteAuditLogs).where((0, import_drizzle_orm4.and)(timeCondition, (0, import_drizzle_orm4.isNotNull)(sqliteAuditLogs.actorId))).groupBy(sqliteAuditLogs.actorId).orderBy((0, import_drizzle_orm4.desc)(import_drizzle_orm4.sql`count(*)`)).limit(10);
651
655
  const topTablesQuery = db.select({
652
656
  tableName: sqliteAuditLogs.tableName,
653
657
  count: import_drizzle_orm4.sql`count(*)`
654
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(sqliteAuditLogs.tableName).orderBy((0, import_drizzle_orm4.desc)(import_drizzle_orm4.sql`count(*)`)).limit(10);
658
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(sqliteAuditLogs.tableName).orderBy((0, import_drizzle_orm4.desc)(import_drizzle_orm4.sql`count(*)`)).limit(10);
655
659
  const operationQuery = db.select({
656
660
  operation: sqliteAuditLogs.operation,
657
661
  count: import_drizzle_orm4.sql`count(*)`
658
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(sqliteAuditLogs.operation);
662
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(sqliteAuditLogs.operation);
659
663
  const severityQuery = db.select({
660
664
  severity: sqliteAuditLogs.severity,
661
665
  count: import_drizzle_orm4.sql`count(*)`
662
- }).from(sqliteAuditLogs).where((0, import_drizzle_orm4.and)(sinceCondition, (0, import_drizzle_orm4.isNotNull)(sqliteAuditLogs.severity))).groupBy(sqliteAuditLogs.severity);
666
+ }).from(sqliteAuditLogs).where((0, import_drizzle_orm4.and)(timeCondition, (0, import_drizzle_orm4.isNotNull)(sqliteAuditLogs.severity))).groupBy(sqliteAuditLogs.severity);
663
667
  const results = await Promise.all([
664
668
  summaryQuery,
665
669
  eventsPerDayQuery,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/schema.ts","../src/column-map.ts","../src/query.ts","../src/sqlite-adapter.ts","../src/sqlite-schema.ts","../src/sqlite-column-map.ts","../src/sqlite-query.ts","../src/proxy.ts","../src/operation-map.ts"],"sourcesContent":["export { drizzleAuditAdapter } from \"./adapter.js\";\nexport type { DrizzlePgDatabase } from \"./adapter.js\";\nexport { drizzleSqliteAuditAdapter } from \"./sqlite-adapter.js\";\nexport type { DrizzleSqliteDatabase } from \"./sqlite-adapter.js\";\nexport { withAuditProxy } from \"./proxy.js\";\nexport type { AuditProxyOptions, MissingRecordIdBehavior } from \"./proxy.js\";\nexport { auditLogs } from \"./schema.js\";\nexport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\nexport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nexport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\nexport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\nexport { encodeCursor, decodeCursor } from \"@usebetterdev/audit-core\";\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\nimport { auditLogToRow, rowToAuditLog } from \"./column-map.js\";\nimport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\n\n/**\n * Minimal shape for a Drizzle pg database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzlePgDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle pg database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleAuditAdapter(\n db: DrizzlePgDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = auditLogToRow(log);\n await db.insert(auditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildOrderBy(sortOrder);\n\n // The duck-typed interface returns `unknown` from select().\n // We build the full Drizzle query chain and cast at the boundary.\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(rowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(eq(auditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return rowToAuditLog(row);\n },\n\n /**\n * Delete audit log entries older than `before`.\n *\n * **Warning:** Large deletes may hold a row-level lock on the `audit_logs`\n * table for an extended period. Run during low-traffic windows when purging\n * millions of rows.\n */\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(auditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(auditLogs.tableName, options.tableName));\n }\n\n const result = await (\n db.delete(auditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n const rowCount = (result as { rowCount?: number | null }).rowCount;\n return { deletedCount: rowCount ?? 0 };\n },\n\n async getStats(options?: { since?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(auditLogs.timestamp, options.since)\n : undefined;\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${auditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition);\n\n // Query 2: eventsPerDay\n const eventsPerDayQuery = (\n db.select({\n date: sql`date_trunc('day', ${auditLogs.timestamp})::date`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .orderBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: auditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(sinceCondition, isNotNull(auditLogs.actorId)))\n .groupBy(auditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: auditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(auditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: auditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(auditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: auditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(sinceCondition, isNotNull(auditLogs.severity)))\n .groupBy(auditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n * Not exported; exists to avoid `any`.\n */\nfunction buildSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n where(condition: unknown): Promise<{ rowCount: number }>;\n };\n}\n","import {\n pgTable,\n uuid,\n timestamp,\n text,\n jsonb,\n boolean,\n index,\n} from \"drizzle-orm/pg-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const auditLogs = pgTable(\n \"audit_logs\",\n {\n id: uuid().primaryKey().defaultRandom(),\n timestamp: timestamp({ withTimezone: true }).notNull().defaultNow(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: jsonb(\"before_data\"),\n afterData: jsonb(\"after_data\"),\n diff: jsonb(),\n label: text(),\n description: text(),\n severity: text(),\n compliance: jsonb(),\n notify: boolean(),\n reason: text(),\n metadata: jsonb(),\n redactedFields: jsonb(\"redacted_fields\"),\n },\n (table) => [\n index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n index(\"audit_logs_actor_id_idx\").on(table.actorId),\n index(\"audit_logs_record_id_idx\").on(table.recordId),\n index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n index(\"audit_logs_operation_idx\").on(table.operation),\n index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n ],\n);\n\nexport type AuditLogRow = InferSelectModel<typeof auditLogs>;\nexport type NewAuditLogRow = InferInsertModel<typeof auditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a snake_case DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function auditLogToRow(log: AuditLog): NewAuditLogRow {\n const row: NewAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a snake_case DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function rowToAuditLog(row: AuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n ilike,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle column reference. */\nconst FIELD_COLUMNS = {\n tableName: auditLogs.tableName,\n recordId: auditLogs.recordId,\n actorId: auditLogs.actorId,\n severity: auditLogs.severity,\n operation: auditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (PostgreSQL dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(auditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(auditLogs.timestamp, condition.value);\n }\n case \"search\": {\n return or(\n ilike(auditLogs.label, condition.pattern),\n ilike(auditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n return sql`${auditLogs.compliance} @> ${JSON.stringify(condition.tags)}::jsonb`;\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(auditLogs.timestamp, condition.timestamp),\n and(\n eq(auditLogs.timestamp, condition.timestamp),\n idCompare(auditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition (combined with AND). Returns `undefined` when no filters are active.\n */\nexport function buildWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction.\n * Default is descending (newest first).\n */\nexport function buildOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(auditLogs.timestamp), asc(auditLogs.id)];\n }\n return [desc(auditLogs.timestamp), desc(auditLogs.id)];\n}\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, toCount, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nimport { sqliteAuditLogToRow, sqliteRowToAuditLog } from \"./sqlite-column-map.js\";\nimport {\n buildSqliteWhereConditions,\n buildSqliteOrderBy,\n} from \"./sqlite-query.js\";\n\n/**\n * Minimal shape for a Drizzle SQLite database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzleSqliteDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle SQLite database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleSqliteAuditAdapter(\n db: DrizzleSqliteDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = sqliteAuditLogToRow(log);\n await db.insert(sqliteAuditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildSqliteWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildSqliteOrderBy(sortOrder);\n\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(sqliteRowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(eq(sqliteAuditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return sqliteRowToAuditLog(row);\n },\n\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(sqliteAuditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(sqliteAuditLogs.tableName, options.tableName));\n }\n\n await (\n db.delete(sqliteAuditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n // SQLite: use changes() to get the number of rows affected\n const changesResult = (await (\n db.select({ count: sql`changes()` }) as ReturnType<typeof buildAggregateSelectChain>\n ).from(sqliteAuditLogs)) as Array<{ count: unknown }>;\n\n const deletedCount = changesResult[0] !== undefined ? toCount(changesResult[0].count) : 0;\n return { deletedCount };\n },\n\n async getStats(options?: { since?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(sqliteAuditLogs.timestamp, options.since)\n : undefined;\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${sqliteAuditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition);\n\n // Query 2: eventsPerDay — use strftime for SQLite\n const eventsPerDayQuery = (\n db.select({\n date: sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .orderBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: sqliteAuditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(sinceCondition, isNotNull(sqliteAuditLogs.actorId)))\n .groupBy(sqliteAuditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: sqliteAuditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sqliteAuditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: sqliteAuditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sqliteAuditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: sqliteAuditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(sinceCondition, isNotNull(sqliteAuditLogs.severity)))\n .groupBy(sqliteAuditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n */\nfunction buildSelectChain() {\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n return undefined as unknown as {\n from(table: unknown): AggregateFiltered & {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n return undefined as unknown as {\n where(condition: unknown): Promise<unknown>;\n };\n}\n","import {\n sqliteTable,\n text,\n integer,\n index,\n} from \"drizzle-orm/sqlite-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const sqliteAuditLogs = sqliteTable(\n \"audit_logs\",\n {\n id: text().primaryKey(),\n timestamp: integer({ mode: \"timestamp\" }).notNull(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: text(\"before_data\", { mode: \"json\" }),\n afterData: text(\"after_data\", { mode: \"json\" }),\n diff: text({ mode: \"json\" }),\n label: text(),\n description: text(),\n severity: text(),\n compliance: text({ mode: \"json\" }),\n notify: integer({ mode: \"boolean\" }),\n reason: text(),\n metadata: text({ mode: \"json\" }),\n redactedFields: text(\"redacted_fields\", { mode: \"json\" }),\n },\n (table) => ({\n tableNameTimestampIdx: index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n actorIdIdx: index(\"audit_logs_actor_id_idx\").on(table.actorId),\n recordIdIdx: index(\"audit_logs_record_id_idx\").on(table.recordId),\n tableNameRecordIdIdx: index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n operationIdx: index(\"audit_logs_operation_idx\").on(table.operation),\n timestampIdx: index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n timestampIdIdx: index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n }),\n);\n\nexport type SqliteAuditLogRow = InferSelectModel<typeof sqliteAuditLogs>;\nexport type NewSqliteAuditLogRow = InferInsertModel<typeof sqliteAuditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a SQLite DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function sqliteAuditLogToRow(log: AuditLog): NewSqliteAuditLogRow {\n const row: NewSqliteAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a SQLite DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function sqliteRowToAuditLog(row: SqliteAuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n like,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle SQLite column reference. */\nconst FIELD_COLUMNS = {\n tableName: sqliteAuditLogs.tableName,\n recordId: sqliteAuditLogs.recordId,\n actorId: sqliteAuditLogs.actorId,\n severity: sqliteAuditLogs.severity,\n operation: sqliteAuditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (SQLite dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"search\": {\n // SQLite LIKE is case-insensitive for ASCII by default\n return or(\n like(sqliteAuditLogs.label, condition.pattern),\n like(sqliteAuditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n // Per-tag EXISTS with json_each (AND semantics)\n const tagConditions: SQL[] = [];\n for (const tag of condition.tags) {\n tagConditions.push(\n sql`EXISTS (SELECT 1 FROM json_each(${sqliteAuditLogs.compliance}) WHERE value = ${tag})`,\n );\n }\n return and(...tagConditions);\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(sqliteAuditLogs.timestamp, condition.timestamp),\n and(\n eq(sqliteAuditLogs.timestamp, condition.timestamp),\n idCompare(sqliteAuditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition for SQLite. Returns `undefined` when no filters are active.\n */\nexport function buildSqliteWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction (SQLite schema).\n */\nexport function buildSqliteOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(sqliteAuditLogs.timestamp), asc(sqliteAuditLogs.id)];\n }\n return [desc(sqliteAuditLogs.timestamp), desc(sqliteAuditLogs.id)];\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getTableName } from \"drizzle-orm\";\nimport type { PgTable } from \"drizzle-orm/pg-core\";\nimport { OPERATION_MAP, type DrizzleMutationMethod } from \"./operation-map.js\";\n\nexport type MissingRecordIdBehavior = \"warn\" | \"skip\" | \"throw\";\n\nexport interface AuditProxyOptions {\n /**\n * Fallback column name for extracting `recordId` when the primary key\n * cannot be auto-detected from the Drizzle table schema. Defaults to `\"id\"`.\n *\n * In most cases this is not needed — the proxy reads `.primaryKey()` metadata\n * from Drizzle column objects at runtime.\n */\n primaryKey?: string;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n */\n onError?: (error: unknown) => void;\n /**\n * What to do when the `recordId` cannot be determined.\n *\n * - `\"warn\"` (default) — log via `onError`, proceed with `recordId: \"unknown\"`\n * - `\"skip\"` — silently drop the audit entry\n * - `\"throw\"` — throw an error (caught by the proxy's outer error handler)\n */\n onMissingRecordId?: MissingRecordIdBehavior;\n /**\n * Table names for which the pre-mutation SELECT should be skipped.\n * Use this for high-throughput tables where the extra query is too expensive.\n */\n skipBeforeState?: string[];\n /**\n * Safety limit: if the pre-mutation SELECT returns more rows than this,\n * skip the before-state capture and warn. Defaults to 1000.\n */\n maxBeforeStateRows?: number;\n}\n\ninterface BuilderContext {\n tableName: string;\n operation: AuditOperation;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n primaryKey: string;\n handleError: (error: unknown, table: string, op: string) => void;\n onMissingRecordId: MissingRecordIdBehavior;\n auditedSet: WeakSet<object>;\n dbTarget: Record<string, unknown>;\n table: PgTable;\n skipBeforeState: Set<string>;\n maxBeforeStateRows: number;\n}\n\n/**\n * Wraps a Drizzle database (or transaction) with a transparent proxy that\n * intercepts `db.insert()`, `db.update()`, `db.delete()` and calls\n * `captureLog()` after each successful mutation.\n *\n * The proxy also intercepts `db.transaction()` so that the `tx` handle\n * passed to the callback is itself proxied — this is what makes it work\n * with `better-tenant`, where all user code runs inside a transaction.\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`, so there is\n * a single source of truth for which tables are audited.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport function withAuditProxy<TDb extends {}>(\n db: TDb,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options?: AuditProxyOptions,\n): TDb {\n const primaryKey = options?.primaryKey ?? \"id\";\n const missingRecordIdPolicy = options?.onMissingRecordId ?? \"warn\";\n const skipBeforeState = new Set(options?.skipBeforeState ?? []);\n const maxBeforeStateRows = options?.maxBeforeStateRows ?? 1000;\n\n const handleError = (error: unknown, table: string, op: string): void => {\n try {\n if (options?.onError !== undefined) {\n options.onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(\n `audit-drizzle: capture failed for ${op} on ${table} — ${msg}`,\n );\n }\n } catch {\n // onError callback itself threw — swallow to prevent audit from breaking mutations.\n }\n };\n\n return new Proxy(db, {\n get(target, prop, receiver) {\n // Intercept insert / update / delete\n if (\n typeof prop === \"string\" &&\n (prop === \"insert\" || prop === \"update\" || prop === \"delete\")\n ) {\n const method = prop as DrizzleMutationMethod;\n const originalMethod = Reflect.get(target, prop, receiver) as (\n table: PgTable,\n ) => Record<string, unknown>;\n\n return (table: PgTable) => {\n const tableName = getTableName(table);\n const detectedPk = getPrimaryKeyColumnName(table);\n const effectivePk = detectedPk ?? primaryKey;\n const originalBuilder = originalMethod.call(target, table);\n\n const ctx: BuilderContext = {\n tableName,\n operation: OPERATION_MAP[method],\n captureLog,\n primaryKey: effectivePk,\n handleError,\n onMissingRecordId: missingRecordIdPolicy,\n auditedSet: new WeakSet<object>(),\n dbTarget: target as Record<string, unknown>,\n table,\n skipBeforeState,\n maxBeforeStateRows,\n };\n\n return wrapBuilder(originalBuilder, ctx);\n };\n }\n\n // Intercept transaction() so the tx handle is also proxied\n if (prop === \"transaction\") {\n const originalTransaction = Reflect.get(\n target,\n prop,\n receiver,\n ) as Function;\n\n return (...args: unknown[]) => {\n const callback = args[0] as (tx: unknown) => Promise<unknown>;\n const rest = args.slice(1);\n const wrappedCallback = (tx: unknown) => {\n const proxiedTx = withAuditProxy(\n tx as TDb,\n captureLog,\n options,\n );\n return callback(proxiedTx);\n };\n return originalTransaction.call(target, wrappedCallback, ...rest);\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\ninterface TrackedState {\n trackedValues?: Record<string, unknown>[];\n trackedSet?: Record<string, unknown>;\n trackedWhere?: unknown;\n hasReturning?: boolean;\n}\n\nfunction wrapBuilder(\n builder: Record<string, unknown>,\n ctx: BuilderContext,\n state: TrackedState = {},\n): Record<string, unknown> {\n return new Proxy(builder, {\n get(target, prop, receiver) {\n // Track .values() data for INSERT\n if (prop === \"values\") {\n return (...args: unknown[]) => {\n const data = args[0] as\n | Record<string, unknown>\n | Record<string, unknown>[];\n const newTrackedValues = Array.isArray(data) ? data : [data];\n const result = (\n target.values as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedValues: newTrackedValues,\n });\n };\n }\n\n // Track .set() data for UPDATE\n if (prop === \"set\") {\n return (...args: unknown[]) => {\n const newTrackedSet = args[0] as Record<string, unknown>;\n const result = (\n target.set as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedSet: newTrackedSet,\n });\n };\n }\n\n // Track .where() condition for UPDATE/DELETE pre-SELECT\n if (prop === \"where\") {\n return (...args: unknown[]) => {\n const condition = args[0];\n const result = (\n target.where as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedWhere: condition,\n });\n };\n }\n\n // Track .returning() so we know the result contains full rows\n if (prop === \"returning\") {\n return (...args: unknown[]) => {\n const result = (\n target.returning as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n hasReturning: true,\n });\n };\n }\n\n // Intercept .then() — the terminal await point\n if (prop === \"then\") {\n return (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => {\n const thenFn = Reflect.get(target, \"then\", receiver) as (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown;\n\n const needsBeforeState =\n (ctx.operation === \"UPDATE\" || ctx.operation === \"DELETE\") &&\n state.trackedWhere !== undefined &&\n !ctx.skipBeforeState.has(ctx.tableName) &&\n // For DELETE with .returning(), returned rows ARE the before-state\n !(ctx.operation === \"DELETE\" && state.hasReturning === true);\n\n if (needsBeforeState) {\n return executeWithBeforeState(\n target,\n thenFn,\n ctx,\n state,\n onFulfilled,\n onRejected,\n );\n }\n\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n };\n }\n\n // All other methods: forward and re-wrap\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return (...args: unknown[]) => {\n const result = (value as (...a: unknown[]) => unknown).apply(\n target,\n args,\n );\n if (result !== null && typeof result === \"object\") {\n return wrapBuilder(\n result as Record<string, unknown>,\n ctx,\n state,\n );\n }\n return result;\n };\n }\n\n return value;\n },\n });\n}\n\n/**\n * Executes a pre-mutation SELECT, then the original mutation, then fires\n * captureLog with matched before/after pairs.\n */\nfunction executeWithBeforeState(\n target: Record<string, unknown>,\n thenFn: (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown,\n ctx: BuilderContext,\n state: TrackedState,\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n): unknown {\n // Issue the pre-mutation SELECT, then chain the original mutation\n const beforePromise = fetchBeforeState(ctx, state);\n\n return beforePromise.then(\n (beforeRows) => {\n // Now execute the original mutation\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n if (beforeRows !== undefined) {\n await fireCaptureLogWithBeforeState(\n result,\n beforeRows,\n ctx,\n state,\n );\n } else {\n // Fallback: before-state unavailable, use original behavior\n await fireCaptureLog(result, ctx, state);\n }\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n (error) => {\n // Pre-SELECT failed — log error, fall back to original behavior.\n ctx.handleError(error, ctx.tableName, ctx.operation);\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (captureError) {\n ctx.handleError(captureError, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n );\n}\n\n/**\n * Issues a SELECT to fetch the rows that will be affected by the mutation.\n * Returns `undefined` if the fetch should be skipped (too many rows).\n */\nasync function fetchBeforeState(\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<Record<string, unknown>[] | undefined> {\n const selectFn = ctx.dbTarget.select as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (selectFn === undefined) {\n return undefined;\n }\n\n const selectBuilder = selectFn.call(ctx.dbTarget);\n const fromFn = selectBuilder.from as\n | ((table: PgTable) => Record<string, unknown>)\n | undefined;\n if (fromFn === undefined) {\n return undefined;\n }\n\n const fromBuilder = fromFn.call(selectBuilder, ctx.table);\n const whereFn = fromBuilder.where as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (whereFn === undefined) {\n return undefined;\n }\n\n const whereBuilder = whereFn.call(fromBuilder, state.trackedWhere);\n\n // Apply LIMIT to avoid fetching unbounded rows into memory.\n // Fetch limit + 1 so we can detect when the limit is exceeded.\n const limitFn = whereBuilder.limit as\n | ((n: number) => Record<string, unknown>)\n | undefined;\n const fetchLimit = ctx.maxBeforeStateRows + 1;\n const queryBuilder =\n limitFn !== undefined\n ? limitFn.call(whereBuilder, fetchLimit)\n : whereBuilder;\n\n const rows = (await queryBuilder) as unknown as Record<string, unknown>[];\n\n if (rows.length > ctx.maxBeforeStateRows) {\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state SELECT returned more than ${ctx.maxBeforeStateRows} rows, skipping before-state capture`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return undefined;\n }\n\n return rows;\n}\n\nfunction getPrimaryKeyColumnName(table: PgTable): string | undefined {\n for (const [key, value] of Object.entries(table)) {\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"primary\" in value &&\n value.primary === true\n ) {\n return key;\n }\n }\n return undefined;\n}\n\nfunction extractRecordId(\n row: Record<string, unknown>,\n primaryKey: string,\n): string {\n const value = row[primaryKey];\n if (value !== undefined && value !== null) {\n return String(value);\n }\n return \"\";\n}\n\n/**\n * Applies the missing-record-id policy.\n * Returns `true` if the audit entry should still be written (with \"unknown\"),\n * or `false` if it should be skipped.\n */\nfunction applyMissingRecordIdPolicy(\n ctx: BuilderContext,\n detail: string,\n): boolean {\n const policy = ctx.onMissingRecordId;\n if (policy === \"skip\") {\n return false;\n }\n if (policy === \"throw\") {\n throw new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n );\n }\n // \"warn\" — report via handleError, then proceed\n ctx.handleError(\n new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return true;\n}\n\n/**\n * Fires captureLog with matched before/after pairs using pre-mutation state.\n */\nasync function fireCaptureLogWithBeforeState(\n result: unknown,\n beforeRows: Record<string, unknown>[],\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"UPDATE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared between SELECT and UPDATE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but UPDATE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original UPDATE behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n // Build before map keyed by primary key\n const beforeMap = new Map<string, Record<string, unknown>>();\n for (const row of beforeRows) {\n const pk = extractRecordId(row, ctx.primaryKey);\n if (pk !== \"\") {\n beforeMap.set(pk, row);\n }\n }\n\n if (beforeMap.size === 0) {\n // All before-rows lacked the primary key column — likely misconfiguration\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state rows exist but none have primary key \"${ctx.primaryKey}\" — check primaryKey option`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n if (state.hasReturning === true && returnedRows.length > 0) {\n // After-state from .returning() result\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n const beforeRow = beforeMap.get(recordId);\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(beforeRow !== undefined && { before: beforeRow }),\n after: row,\n });\n }\n } else {\n // After-state: merge .set() data onto each before-row\n for (const [pk, beforeRow] of beforeMap) {\n const afterRow =\n state.trackedSet !== undefined\n ? { ...beforeRow, ...state.trackedSet }\n : beforeRow;\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: pk,\n before: beforeRow,\n after: afterRow,\n });\n }\n }\n } else if (ctx.operation === \"DELETE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared before DELETE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but DELETE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n for (const beforeRow of beforeRows) {\n const recordId = extractRecordId(beforeRow, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"before-state row missing primary key\",\n );\n if (!shouldProceed) {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n before: beforeRow,\n });\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: beforeRow,\n });\n }\n }\n}\n\nasync function fireCaptureLog(\n result: unknown,\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const { trackedValues, trackedSet } = state;\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"INSERT\") {\n const values = trackedValues ?? [];\n for (let i = 0; i < values.length; i++) {\n const row = values[i];\n if (row === undefined) {\n continue;\n }\n const returnedRow =\n returnedRows.length > i\n ? (returnedRows[i] as Record<string, unknown>)\n : undefined;\n const recordId =\n returnedRow !== undefined\n ? extractRecordId(returnedRow, ctx.primaryKey)\n : extractRecordId(row, ctx.primaryKey);\n\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() or include the primary key in .values()\",\n );\n if (!shouldProceed) {\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n after: row,\n });\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n after: row,\n });\n }\n } else if (ctx.operation === \"UPDATE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(trackedSet !== undefined && { after: trackedSet }),\n });\n }\n } else if (trackedSet !== undefined) {\n const recordId = extractRecordId(trackedSet, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: recordId || \"unknown\",\n after: trackedSet,\n });\n }\n } else if (ctx.operation === \"DELETE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: row,\n });\n }\n } else {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n });\n }\n }\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\nexport const OPERATION_MAP = {\n insert: \"INSERT\",\n update: \"UPDATE\",\n delete: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type DrizzleMutationMethod = keyof typeof OPERATION_MAP;\n"],"mappings":"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","names":["import_audit_core","import_drizzle_orm","import_audit_core","import_audit_core","import_drizzle_orm","import_audit_core","import_audit_core","import_drizzle_orm","FIELD_COLUMNS","mapCondition","DEFAULT_LIMIT","MAX_LIMIT","import_drizzle_orm","import_audit_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/schema.ts","../src/column-map.ts","../src/query.ts","../src/sqlite-adapter.ts","../src/sqlite-schema.ts","../src/sqlite-column-map.ts","../src/sqlite-query.ts","../src/proxy.ts","../src/operation-map.ts"],"sourcesContent":["export { drizzleAuditAdapter } from \"./adapter.js\";\nexport type { DrizzlePgDatabase } from \"./adapter.js\";\nexport { drizzleSqliteAuditAdapter } from \"./sqlite-adapter.js\";\nexport type { DrizzleSqliteDatabase } from \"./sqlite-adapter.js\";\nexport { withAuditProxy } from \"./proxy.js\";\nexport type { AuditProxyOptions, MissingRecordIdBehavior } from \"./proxy.js\";\nexport { auditLogs } from \"./schema.js\";\nexport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\nexport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nexport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\nexport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\nexport { encodeCursor, decodeCursor } from \"@usebetterdev/audit-core\";\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\nimport { auditLogToRow, rowToAuditLog } from \"./column-map.js\";\nimport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\n\n/**\n * Minimal shape for a Drizzle pg database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzlePgDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle pg database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleAuditAdapter(\n db: DrizzlePgDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = auditLogToRow(log);\n await db.insert(auditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildOrderBy(sortOrder);\n\n // The duck-typed interface returns `unknown` from select().\n // We build the full Drizzle query chain and cast at the boundary.\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(rowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(eq(auditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return rowToAuditLog(row);\n },\n\n /**\n * Delete audit log entries older than `before`.\n *\n * **Warning:** Large deletes may hold a row-level lock on the `audit_logs`\n * table for an extended period. Run during low-traffic windows when purging\n * millions of rows.\n */\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(auditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(auditLogs.tableName, options.tableName));\n }\n\n const result = await (\n db.delete(auditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n const rowCount = (result as { rowCount?: number | null }).rowCount;\n return { deletedCount: rowCount ?? 0 };\n },\n\n async getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(auditLogs.timestamp, options.since)\n : undefined;\n const untilCondition =\n options?.until !== undefined\n ? lt(auditLogs.timestamp, options.until)\n : undefined;\n const timeCondition = and(sinceCondition, untilCondition);\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${auditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition);\n\n // Query 2: eventsPerDay\n const eventsPerDayQuery = (\n db.select({\n date: sql`date_trunc('day', ${auditLogs.timestamp})::date`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .orderBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: auditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(timeCondition, isNotNull(auditLogs.actorId)))\n .groupBy(auditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: auditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(auditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: auditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(auditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: auditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(timeCondition, isNotNull(auditLogs.severity)))\n .groupBy(auditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n * Not exported; exists to avoid `any`.\n */\nfunction buildSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n where(condition: unknown): Promise<{ rowCount: number }>;\n };\n}\n","import {\n pgTable,\n uuid,\n timestamp,\n text,\n jsonb,\n boolean,\n index,\n} from \"drizzle-orm/pg-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const auditLogs = pgTable(\n \"audit_logs\",\n {\n id: uuid().primaryKey().defaultRandom(),\n timestamp: timestamp({ withTimezone: true }).notNull().defaultNow(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: jsonb(\"before_data\"),\n afterData: jsonb(\"after_data\"),\n diff: jsonb(),\n label: text(),\n description: text(),\n severity: text(),\n compliance: jsonb(),\n notify: boolean(),\n reason: text(),\n metadata: jsonb(),\n redactedFields: jsonb(\"redacted_fields\"),\n },\n (table) => [\n index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n index(\"audit_logs_actor_id_idx\").on(table.actorId),\n index(\"audit_logs_record_id_idx\").on(table.recordId),\n index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n index(\"audit_logs_operation_idx\").on(table.operation),\n index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n ],\n);\n\nexport type AuditLogRow = InferSelectModel<typeof auditLogs>;\nexport type NewAuditLogRow = InferInsertModel<typeof auditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a snake_case DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function auditLogToRow(log: AuditLog): NewAuditLogRow {\n const row: NewAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a snake_case DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function rowToAuditLog(row: AuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n ilike,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle column reference. */\nconst FIELD_COLUMNS = {\n tableName: auditLogs.tableName,\n recordId: auditLogs.recordId,\n actorId: auditLogs.actorId,\n severity: auditLogs.severity,\n operation: auditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (PostgreSQL dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(auditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(auditLogs.timestamp, condition.value);\n }\n case \"search\": {\n return or(\n ilike(auditLogs.label, condition.pattern),\n ilike(auditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n return sql`${auditLogs.compliance} @> ${JSON.stringify(condition.tags)}::jsonb`;\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(auditLogs.timestamp, condition.timestamp),\n and(\n eq(auditLogs.timestamp, condition.timestamp),\n idCompare(auditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition (combined with AND). Returns `undefined` when no filters are active.\n */\nexport function buildWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction.\n * Default is descending (newest first).\n */\nexport function buildOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(auditLogs.timestamp), asc(auditLogs.id)];\n }\n return [desc(auditLogs.timestamp), desc(auditLogs.id)];\n}\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, toCount, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nimport { sqliteAuditLogToRow, sqliteRowToAuditLog } from \"./sqlite-column-map.js\";\nimport {\n buildSqliteWhereConditions,\n buildSqliteOrderBy,\n} from \"./sqlite-query.js\";\n\n/**\n * Minimal shape for a Drizzle SQLite database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzleSqliteDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle SQLite database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleSqliteAuditAdapter(\n db: DrizzleSqliteDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = sqliteAuditLogToRow(log);\n await db.insert(sqliteAuditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildSqliteWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildSqliteOrderBy(sortOrder);\n\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(sqliteRowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(eq(sqliteAuditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return sqliteRowToAuditLog(row);\n },\n\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(sqliteAuditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(sqliteAuditLogs.tableName, options.tableName));\n }\n\n await (\n db.delete(sqliteAuditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n // SQLite: use changes() to get the number of rows affected\n const changesResult = (await (\n db.select({ count: sql`changes()` }) as ReturnType<typeof buildAggregateSelectChain>\n ).from(sqliteAuditLogs)) as Array<{ count: unknown }>;\n\n const deletedCount = changesResult[0] !== undefined ? toCount(changesResult[0].count) : 0;\n return { deletedCount };\n },\n\n async getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(sqliteAuditLogs.timestamp, options.since)\n : undefined;\n const untilCondition =\n options?.until !== undefined\n ? lt(sqliteAuditLogs.timestamp, options.until)\n : undefined;\n const timeCondition = and(sinceCondition, untilCondition);\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${sqliteAuditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition);\n\n // Query 2: eventsPerDay — use strftime for SQLite\n const eventsPerDayQuery = (\n db.select({\n date: sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .orderBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: sqliteAuditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(timeCondition, isNotNull(sqliteAuditLogs.actorId)))\n .groupBy(sqliteAuditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: sqliteAuditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sqliteAuditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: sqliteAuditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sqliteAuditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: sqliteAuditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(timeCondition, isNotNull(sqliteAuditLogs.severity)))\n .groupBy(sqliteAuditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n */\nfunction buildSelectChain() {\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n return undefined as unknown as {\n from(table: unknown): AggregateFiltered & {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n return undefined as unknown as {\n where(condition: unknown): Promise<unknown>;\n };\n}\n","import {\n sqliteTable,\n text,\n integer,\n index,\n} from \"drizzle-orm/sqlite-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const sqliteAuditLogs = sqliteTable(\n \"audit_logs\",\n {\n id: text().primaryKey(),\n timestamp: integer({ mode: \"timestamp\" }).notNull(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: text(\"before_data\", { mode: \"json\" }),\n afterData: text(\"after_data\", { mode: \"json\" }),\n diff: text({ mode: \"json\" }),\n label: text(),\n description: text(),\n severity: text(),\n compliance: text({ mode: \"json\" }),\n notify: integer({ mode: \"boolean\" }),\n reason: text(),\n metadata: text({ mode: \"json\" }),\n redactedFields: text(\"redacted_fields\", { mode: \"json\" }),\n },\n (table) => ({\n tableNameTimestampIdx: index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n actorIdIdx: index(\"audit_logs_actor_id_idx\").on(table.actorId),\n recordIdIdx: index(\"audit_logs_record_id_idx\").on(table.recordId),\n tableNameRecordIdIdx: index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n operationIdx: index(\"audit_logs_operation_idx\").on(table.operation),\n timestampIdx: index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n timestampIdIdx: index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n }),\n);\n\nexport type SqliteAuditLogRow = InferSelectModel<typeof sqliteAuditLogs>;\nexport type NewSqliteAuditLogRow = InferInsertModel<typeof sqliteAuditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a SQLite DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function sqliteAuditLogToRow(log: AuditLog): NewSqliteAuditLogRow {\n const row: NewSqliteAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a SQLite DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function sqliteRowToAuditLog(row: SqliteAuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n like,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle SQLite column reference. */\nconst FIELD_COLUMNS = {\n tableName: sqliteAuditLogs.tableName,\n recordId: sqliteAuditLogs.recordId,\n actorId: sqliteAuditLogs.actorId,\n severity: sqliteAuditLogs.severity,\n operation: sqliteAuditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (SQLite dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"search\": {\n // SQLite LIKE is case-insensitive for ASCII by default\n return or(\n like(sqliteAuditLogs.label, condition.pattern),\n like(sqliteAuditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n // Per-tag EXISTS with json_each (AND semantics)\n const tagConditions: SQL[] = [];\n for (const tag of condition.tags) {\n tagConditions.push(\n sql`EXISTS (SELECT 1 FROM json_each(${sqliteAuditLogs.compliance}) WHERE value = ${tag})`,\n );\n }\n return and(...tagConditions);\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(sqliteAuditLogs.timestamp, condition.timestamp),\n and(\n eq(sqliteAuditLogs.timestamp, condition.timestamp),\n idCompare(sqliteAuditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition for SQLite. Returns `undefined` when no filters are active.\n */\nexport function buildSqliteWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction (SQLite schema).\n */\nexport function buildSqliteOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(sqliteAuditLogs.timestamp), asc(sqliteAuditLogs.id)];\n }\n return [desc(sqliteAuditLogs.timestamp), desc(sqliteAuditLogs.id)];\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getTableName } from \"drizzle-orm\";\nimport type { PgTable } from \"drizzle-orm/pg-core\";\nimport { OPERATION_MAP, type DrizzleMutationMethod } from \"./operation-map.js\";\n\nexport type MissingRecordIdBehavior = \"warn\" | \"skip\" | \"throw\";\n\nexport interface AuditProxyOptions {\n /**\n * Fallback column name for extracting `recordId` when the primary key\n * cannot be auto-detected from the Drizzle table schema. Defaults to `\"id\"`.\n *\n * In most cases this is not needed — the proxy reads `.primaryKey()` metadata\n * from Drizzle column objects at runtime.\n */\n primaryKey?: string;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n */\n onError?: (error: unknown) => void;\n /**\n * What to do when the `recordId` cannot be determined.\n *\n * - `\"warn\"` (default) — log via `onError`, proceed with `recordId: \"unknown\"`\n * - `\"skip\"` — silently drop the audit entry\n * - `\"throw\"` — throw an error (caught by the proxy's outer error handler)\n */\n onMissingRecordId?: MissingRecordIdBehavior;\n /**\n * Table names for which the pre-mutation SELECT should be skipped.\n * Use this for high-throughput tables where the extra query is too expensive.\n */\n skipBeforeState?: string[];\n /**\n * Safety limit: if the pre-mutation SELECT returns more rows than this,\n * skip the before-state capture and warn. Defaults to 1000.\n */\n maxBeforeStateRows?: number;\n}\n\ninterface BuilderContext {\n tableName: string;\n operation: AuditOperation;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n primaryKey: string;\n handleError: (error: unknown, table: string, op: string) => void;\n onMissingRecordId: MissingRecordIdBehavior;\n auditedSet: WeakSet<object>;\n dbTarget: Record<string, unknown>;\n table: PgTable;\n skipBeforeState: Set<string>;\n maxBeforeStateRows: number;\n}\n\n/**\n * Wraps a Drizzle database (or transaction) with a transparent proxy that\n * intercepts `db.insert()`, `db.update()`, `db.delete()` and calls\n * `captureLog()` after each successful mutation.\n *\n * The proxy also intercepts `db.transaction()` so that the `tx` handle\n * passed to the callback is itself proxied — this is what makes it work\n * with `better-tenant`, where all user code runs inside a transaction.\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`, so there is\n * a single source of truth for which tables are audited.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport function withAuditProxy<TDb extends {}>(\n db: TDb,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options?: AuditProxyOptions,\n): TDb {\n const primaryKey = options?.primaryKey ?? \"id\";\n const missingRecordIdPolicy = options?.onMissingRecordId ?? \"warn\";\n const skipBeforeState = new Set(options?.skipBeforeState ?? []);\n const maxBeforeStateRows = options?.maxBeforeStateRows ?? 1000;\n\n const handleError = (error: unknown, table: string, op: string): void => {\n try {\n if (options?.onError !== undefined) {\n options.onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(\n `audit-drizzle: capture failed for ${op} on ${table} — ${msg}`,\n );\n }\n } catch {\n // onError callback itself threw — swallow to prevent audit from breaking mutations.\n }\n };\n\n return new Proxy(db, {\n get(target, prop, receiver) {\n // Intercept insert / update / delete\n if (\n typeof prop === \"string\" &&\n (prop === \"insert\" || prop === \"update\" || prop === \"delete\")\n ) {\n const method = prop as DrizzleMutationMethod;\n const originalMethod = Reflect.get(target, prop, receiver) as (\n table: PgTable,\n ) => Record<string, unknown>;\n\n return (table: PgTable) => {\n const tableName = getTableName(table);\n const detectedPk = getPrimaryKeyColumnName(table);\n const effectivePk = detectedPk ?? primaryKey;\n const originalBuilder = originalMethod.call(target, table);\n\n const ctx: BuilderContext = {\n tableName,\n operation: OPERATION_MAP[method],\n captureLog,\n primaryKey: effectivePk,\n handleError,\n onMissingRecordId: missingRecordIdPolicy,\n auditedSet: new WeakSet<object>(),\n dbTarget: target as Record<string, unknown>,\n table,\n skipBeforeState,\n maxBeforeStateRows,\n };\n\n return wrapBuilder(originalBuilder, ctx);\n };\n }\n\n // Intercept transaction() so the tx handle is also proxied\n if (prop === \"transaction\") {\n const originalTransaction = Reflect.get(\n target,\n prop,\n receiver,\n ) as Function;\n\n return (...args: unknown[]) => {\n const callback = args[0] as (tx: unknown) => Promise<unknown>;\n const rest = args.slice(1);\n const wrappedCallback = (tx: unknown) => {\n const proxiedTx = withAuditProxy(\n tx as TDb,\n captureLog,\n options,\n );\n return callback(proxiedTx);\n };\n return originalTransaction.call(target, wrappedCallback, ...rest);\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\ninterface TrackedState {\n trackedValues?: Record<string, unknown>[];\n trackedSet?: Record<string, unknown>;\n trackedWhere?: unknown;\n hasReturning?: boolean;\n}\n\nfunction wrapBuilder(\n builder: Record<string, unknown>,\n ctx: BuilderContext,\n state: TrackedState = {},\n): Record<string, unknown> {\n return new Proxy(builder, {\n get(target, prop, receiver) {\n // Track .values() data for INSERT\n if (prop === \"values\") {\n return (...args: unknown[]) => {\n const data = args[0] as\n | Record<string, unknown>\n | Record<string, unknown>[];\n const newTrackedValues = Array.isArray(data) ? data : [data];\n const result = (\n target.values as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedValues: newTrackedValues,\n });\n };\n }\n\n // Track .set() data for UPDATE\n if (prop === \"set\") {\n return (...args: unknown[]) => {\n const newTrackedSet = args[0] as Record<string, unknown>;\n const result = (\n target.set as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedSet: newTrackedSet,\n });\n };\n }\n\n // Track .where() condition for UPDATE/DELETE pre-SELECT\n if (prop === \"where\") {\n return (...args: unknown[]) => {\n const condition = args[0];\n const result = (\n target.where as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedWhere: condition,\n });\n };\n }\n\n // Track .returning() so we know the result contains full rows\n if (prop === \"returning\") {\n return (...args: unknown[]) => {\n const result = (\n target.returning as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n hasReturning: true,\n });\n };\n }\n\n // Intercept .then() — the terminal await point\n if (prop === \"then\") {\n return (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => {\n const thenFn = Reflect.get(target, \"then\", receiver) as (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown;\n\n const needsBeforeState =\n (ctx.operation === \"UPDATE\" || ctx.operation === \"DELETE\") &&\n state.trackedWhere !== undefined &&\n !ctx.skipBeforeState.has(ctx.tableName) &&\n // For DELETE with .returning(), returned rows ARE the before-state\n !(ctx.operation === \"DELETE\" && state.hasReturning === true);\n\n if (needsBeforeState) {\n return executeWithBeforeState(\n target,\n thenFn,\n ctx,\n state,\n onFulfilled,\n onRejected,\n );\n }\n\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n };\n }\n\n // All other methods: forward and re-wrap\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return (...args: unknown[]) => {\n const result = (value as (...a: unknown[]) => unknown).apply(\n target,\n args,\n );\n if (result !== null && typeof result === \"object\") {\n return wrapBuilder(\n result as Record<string, unknown>,\n ctx,\n state,\n );\n }\n return result;\n };\n }\n\n return value;\n },\n });\n}\n\n/**\n * Executes a pre-mutation SELECT, then the original mutation, then fires\n * captureLog with matched before/after pairs.\n */\nfunction executeWithBeforeState(\n target: Record<string, unknown>,\n thenFn: (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown,\n ctx: BuilderContext,\n state: TrackedState,\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n): unknown {\n // Issue the pre-mutation SELECT, then chain the original mutation\n const beforePromise = fetchBeforeState(ctx, state);\n\n return beforePromise.then(\n (beforeRows) => {\n // Now execute the original mutation\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n if (beforeRows !== undefined) {\n await fireCaptureLogWithBeforeState(\n result,\n beforeRows,\n ctx,\n state,\n );\n } else {\n // Fallback: before-state unavailable, use original behavior\n await fireCaptureLog(result, ctx, state);\n }\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n (error) => {\n // Pre-SELECT failed — log error, fall back to original behavior.\n ctx.handleError(error, ctx.tableName, ctx.operation);\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (captureError) {\n ctx.handleError(captureError, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n );\n}\n\n/**\n * Issues a SELECT to fetch the rows that will be affected by the mutation.\n * Returns `undefined` if the fetch should be skipped (too many rows).\n */\nasync function fetchBeforeState(\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<Record<string, unknown>[] | undefined> {\n const selectFn = ctx.dbTarget.select as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (selectFn === undefined) {\n return undefined;\n }\n\n const selectBuilder = selectFn.call(ctx.dbTarget);\n const fromFn = selectBuilder.from as\n | ((table: PgTable) => Record<string, unknown>)\n | undefined;\n if (fromFn === undefined) {\n return undefined;\n }\n\n const fromBuilder = fromFn.call(selectBuilder, ctx.table);\n const whereFn = fromBuilder.where as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (whereFn === undefined) {\n return undefined;\n }\n\n const whereBuilder = whereFn.call(fromBuilder, state.trackedWhere);\n\n // Apply LIMIT to avoid fetching unbounded rows into memory.\n // Fetch limit + 1 so we can detect when the limit is exceeded.\n const limitFn = whereBuilder.limit as\n | ((n: number) => Record<string, unknown>)\n | undefined;\n const fetchLimit = ctx.maxBeforeStateRows + 1;\n const queryBuilder =\n limitFn !== undefined\n ? limitFn.call(whereBuilder, fetchLimit)\n : whereBuilder;\n\n const rows = (await queryBuilder) as unknown as Record<string, unknown>[];\n\n if (rows.length > ctx.maxBeforeStateRows) {\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state SELECT returned more than ${ctx.maxBeforeStateRows} rows, skipping before-state capture`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return undefined;\n }\n\n return rows;\n}\n\nfunction getPrimaryKeyColumnName(table: PgTable): string | undefined {\n for (const [key, value] of Object.entries(table)) {\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"primary\" in value &&\n value.primary === true\n ) {\n return key;\n }\n }\n return undefined;\n}\n\nfunction extractRecordId(\n row: Record<string, unknown>,\n primaryKey: string,\n): string {\n const value = row[primaryKey];\n if (value !== undefined && value !== null) {\n return String(value);\n }\n return \"\";\n}\n\n/**\n * Applies the missing-record-id policy.\n * Returns `true` if the audit entry should still be written (with \"unknown\"),\n * or `false` if it should be skipped.\n */\nfunction applyMissingRecordIdPolicy(\n ctx: BuilderContext,\n detail: string,\n): boolean {\n const policy = ctx.onMissingRecordId;\n if (policy === \"skip\") {\n return false;\n }\n if (policy === \"throw\") {\n throw new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n );\n }\n // \"warn\" — report via handleError, then proceed\n ctx.handleError(\n new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return true;\n}\n\n/**\n * Fires captureLog with matched before/after pairs using pre-mutation state.\n */\nasync function fireCaptureLogWithBeforeState(\n result: unknown,\n beforeRows: Record<string, unknown>[],\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"UPDATE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared between SELECT and UPDATE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but UPDATE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original UPDATE behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n // Build before map keyed by primary key\n const beforeMap = new Map<string, Record<string, unknown>>();\n for (const row of beforeRows) {\n const pk = extractRecordId(row, ctx.primaryKey);\n if (pk !== \"\") {\n beforeMap.set(pk, row);\n }\n }\n\n if (beforeMap.size === 0) {\n // All before-rows lacked the primary key column — likely misconfiguration\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state rows exist but none have primary key \"${ctx.primaryKey}\" — check primaryKey option`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n if (state.hasReturning === true && returnedRows.length > 0) {\n // After-state from .returning() result\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n const beforeRow = beforeMap.get(recordId);\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(beforeRow !== undefined && { before: beforeRow }),\n after: row,\n });\n }\n } else {\n // After-state: merge .set() data onto each before-row\n for (const [pk, beforeRow] of beforeMap) {\n const afterRow =\n state.trackedSet !== undefined\n ? { ...beforeRow, ...state.trackedSet }\n : beforeRow;\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: pk,\n before: beforeRow,\n after: afterRow,\n });\n }\n }\n } else if (ctx.operation === \"DELETE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared before DELETE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but DELETE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n for (const beforeRow of beforeRows) {\n const recordId = extractRecordId(beforeRow, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"before-state row missing primary key\",\n );\n if (!shouldProceed) {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n before: beforeRow,\n });\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: beforeRow,\n });\n }\n }\n}\n\nasync function fireCaptureLog(\n result: unknown,\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const { trackedValues, trackedSet } = state;\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"INSERT\") {\n const values = trackedValues ?? [];\n for (let i = 0; i < values.length; i++) {\n const row = values[i];\n if (row === undefined) {\n continue;\n }\n const returnedRow =\n returnedRows.length > i\n ? (returnedRows[i] as Record<string, unknown>)\n : undefined;\n const recordId =\n returnedRow !== undefined\n ? extractRecordId(returnedRow, ctx.primaryKey)\n : extractRecordId(row, ctx.primaryKey);\n\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() or include the primary key in .values()\",\n );\n if (!shouldProceed) {\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n after: row,\n });\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n after: row,\n });\n }\n } else if (ctx.operation === \"UPDATE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(trackedSet !== undefined && { after: trackedSet }),\n });\n }\n } else if (trackedSet !== undefined) {\n const recordId = extractRecordId(trackedSet, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: recordId || \"unknown\",\n after: trackedSet,\n });\n }\n } else if (ctx.operation === \"DELETE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: row,\n });\n }\n } else {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n });\n }\n }\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\nexport const OPERATION_MAP = {\n insert: \"INSERT\",\n update: \"UPDATE\",\n delete: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type DrizzleMutationMethod = keyof typeof OPERATION_MAP;\n"],"mappings":"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","names":["import_audit_core","import_drizzle_orm","import_audit_core","import_audit_core","import_drizzle_orm","import_audit_core","import_audit_core","import_drizzle_orm","FIELD_COLUMNS","mapCondition","DEFAULT_LIMIT","MAX_LIMIT","import_drizzle_orm","import_audit_core"]}
package/dist/index.js CHANGED
@@ -298,30 +298,32 @@ function drizzleAuditAdapter(db) {
298
298
  },
299
299
  async getStats(options) {
300
300
  const sinceCondition = options?.since !== void 0 ? gte2(auditLogs.timestamp, options.since) : void 0;
301
+ const untilCondition = options?.until !== void 0 ? lt2(auditLogs.timestamp, options.until) : void 0;
302
+ const timeCondition = and2(sinceCondition, untilCondition);
301
303
  const summaryQuery = db.select({
302
304
  totalLogs: sql2`count(*)`,
303
305
  tablesAudited: sql2`count(DISTINCT ${auditLogs.tableName})`
304
- }).from(auditLogs).where(sinceCondition);
306
+ }).from(auditLogs).where(timeCondition);
305
307
  const eventsPerDayQuery = db.select({
306
308
  date: sql2`date_trunc('day', ${auditLogs.timestamp})::date`,
307
309
  count: sql2`count(*)`
308
- }).from(auditLogs).where(sinceCondition).groupBy(sql2`date_trunc('day', ${auditLogs.timestamp})`).orderBy(sql2`date_trunc('day', ${auditLogs.timestamp})`).limit(365);
310
+ }).from(auditLogs).where(timeCondition).groupBy(sql2`date_trunc('day', ${auditLogs.timestamp})`).orderBy(sql2`date_trunc('day', ${auditLogs.timestamp})`).limit(365);
309
311
  const topActorsQuery = db.select({
310
312
  actorId: auditLogs.actorId,
311
313
  count: sql2`count(*)`
312
- }).from(auditLogs).where(and2(sinceCondition, isNotNull(auditLogs.actorId))).groupBy(auditLogs.actorId).orderBy(desc2(sql2`count(*)`)).limit(10);
314
+ }).from(auditLogs).where(and2(timeCondition, isNotNull(auditLogs.actorId))).groupBy(auditLogs.actorId).orderBy(desc2(sql2`count(*)`)).limit(10);
313
315
  const topTablesQuery = db.select({
314
316
  tableName: auditLogs.tableName,
315
317
  count: sql2`count(*)`
316
- }).from(auditLogs).where(sinceCondition).groupBy(auditLogs.tableName).orderBy(desc2(sql2`count(*)`)).limit(10);
318
+ }).from(auditLogs).where(timeCondition).groupBy(auditLogs.tableName).orderBy(desc2(sql2`count(*)`)).limit(10);
317
319
  const operationQuery = db.select({
318
320
  operation: auditLogs.operation,
319
321
  count: sql2`count(*)`
320
- }).from(auditLogs).where(sinceCondition).groupBy(auditLogs.operation);
322
+ }).from(auditLogs).where(timeCondition).groupBy(auditLogs.operation);
321
323
  const severityQuery = db.select({
322
324
  severity: auditLogs.severity,
323
325
  count: sql2`count(*)`
324
- }).from(auditLogs).where(and2(sinceCondition, isNotNull(auditLogs.severity))).groupBy(auditLogs.severity);
326
+ }).from(auditLogs).where(and2(timeCondition, isNotNull(auditLogs.severity))).groupBy(auditLogs.severity);
325
327
  const results = await Promise.all([
326
328
  summaryQuery,
327
329
  eventsPerDayQuery,
@@ -647,30 +649,32 @@ function drizzleSqliteAuditAdapter(db) {
647
649
  },
648
650
  async getStats(options) {
649
651
  const sinceCondition = options?.since !== void 0 ? gte4(sqliteAuditLogs.timestamp, options.since) : void 0;
652
+ const untilCondition = options?.until !== void 0 ? lt4(sqliteAuditLogs.timestamp, options.until) : void 0;
653
+ const timeCondition = and4(sinceCondition, untilCondition);
650
654
  const summaryQuery = db.select({
651
655
  totalLogs: sql4`count(*)`,
652
656
  tablesAudited: sql4`count(DISTINCT ${sqliteAuditLogs.tableName})`
653
- }).from(sqliteAuditLogs).where(sinceCondition);
657
+ }).from(sqliteAuditLogs).where(timeCondition);
654
658
  const eventsPerDayQuery = db.select({
655
659
  date: sql4`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,
656
660
  count: sql4`count(*)`
657
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(sql4`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).orderBy(sql4`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).limit(365);
661
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(sql4`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).orderBy(sql4`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`).limit(365);
658
662
  const topActorsQuery = db.select({
659
663
  actorId: sqliteAuditLogs.actorId,
660
664
  count: sql4`count(*)`
661
- }).from(sqliteAuditLogs).where(and4(sinceCondition, isNotNull2(sqliteAuditLogs.actorId))).groupBy(sqliteAuditLogs.actorId).orderBy(desc4(sql4`count(*)`)).limit(10);
665
+ }).from(sqliteAuditLogs).where(and4(timeCondition, isNotNull2(sqliteAuditLogs.actorId))).groupBy(sqliteAuditLogs.actorId).orderBy(desc4(sql4`count(*)`)).limit(10);
662
666
  const topTablesQuery = db.select({
663
667
  tableName: sqliteAuditLogs.tableName,
664
668
  count: sql4`count(*)`
665
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(sqliteAuditLogs.tableName).orderBy(desc4(sql4`count(*)`)).limit(10);
669
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(sqliteAuditLogs.tableName).orderBy(desc4(sql4`count(*)`)).limit(10);
666
670
  const operationQuery = db.select({
667
671
  operation: sqliteAuditLogs.operation,
668
672
  count: sql4`count(*)`
669
- }).from(sqliteAuditLogs).where(sinceCondition).groupBy(sqliteAuditLogs.operation);
673
+ }).from(sqliteAuditLogs).where(timeCondition).groupBy(sqliteAuditLogs.operation);
670
674
  const severityQuery = db.select({
671
675
  severity: sqliteAuditLogs.severity,
672
676
  count: sql4`count(*)`
673
- }).from(sqliteAuditLogs).where(and4(sinceCondition, isNotNull2(sqliteAuditLogs.severity))).groupBy(sqliteAuditLogs.severity);
677
+ }).from(sqliteAuditLogs).where(and4(timeCondition, isNotNull2(sqliteAuditLogs.severity))).groupBy(sqliteAuditLogs.severity);
674
678
  const results = await Promise.all([
675
679
  summaryQuery,
676
680
  eventsPerDayQuery,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts","../src/schema.ts","../src/column-map.ts","../src/query.ts","../src/sqlite-adapter.ts","../src/sqlite-schema.ts","../src/sqlite-column-map.ts","../src/sqlite-query.ts","../src/proxy.ts","../src/operation-map.ts","../src/index.ts"],"sourcesContent":["import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\nimport { auditLogToRow, rowToAuditLog } from \"./column-map.js\";\nimport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\n\n/**\n * Minimal shape for a Drizzle pg database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzlePgDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle pg database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleAuditAdapter(\n db: DrizzlePgDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = auditLogToRow(log);\n await db.insert(auditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildOrderBy(sortOrder);\n\n // The duck-typed interface returns `unknown` from select().\n // We build the full Drizzle query chain and cast at the boundary.\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(rowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(eq(auditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return rowToAuditLog(row);\n },\n\n /**\n * Delete audit log entries older than `before`.\n *\n * **Warning:** Large deletes may hold a row-level lock on the `audit_logs`\n * table for an extended period. Run during low-traffic windows when purging\n * millions of rows.\n */\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(auditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(auditLogs.tableName, options.tableName));\n }\n\n const result = await (\n db.delete(auditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n const rowCount = (result as { rowCount?: number | null }).rowCount;\n return { deletedCount: rowCount ?? 0 };\n },\n\n async getStats(options?: { since?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(auditLogs.timestamp, options.since)\n : undefined;\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${auditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition);\n\n // Query 2: eventsPerDay\n const eventsPerDayQuery = (\n db.select({\n date: sql`date_trunc('day', ${auditLogs.timestamp})::date`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .orderBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: auditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(sinceCondition, isNotNull(auditLogs.actorId)))\n .groupBy(auditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: auditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(auditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: auditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(sinceCondition)\n .groupBy(auditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: auditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(sinceCondition, isNotNull(auditLogs.severity)))\n .groupBy(auditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n * Not exported; exists to avoid `any`.\n */\nfunction buildSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n where(condition: unknown): Promise<{ rowCount: number }>;\n };\n}\n","import {\n pgTable,\n uuid,\n timestamp,\n text,\n jsonb,\n boolean,\n index,\n} from \"drizzle-orm/pg-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const auditLogs = pgTable(\n \"audit_logs\",\n {\n id: uuid().primaryKey().defaultRandom(),\n timestamp: timestamp({ withTimezone: true }).notNull().defaultNow(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: jsonb(\"before_data\"),\n afterData: jsonb(\"after_data\"),\n diff: jsonb(),\n label: text(),\n description: text(),\n severity: text(),\n compliance: jsonb(),\n notify: boolean(),\n reason: text(),\n metadata: jsonb(),\n redactedFields: jsonb(\"redacted_fields\"),\n },\n (table) => [\n index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n index(\"audit_logs_actor_id_idx\").on(table.actorId),\n index(\"audit_logs_record_id_idx\").on(table.recordId),\n index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n index(\"audit_logs_operation_idx\").on(table.operation),\n index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n ],\n);\n\nexport type AuditLogRow = InferSelectModel<typeof auditLogs>;\nexport type NewAuditLogRow = InferInsertModel<typeof auditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a snake_case DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function auditLogToRow(log: AuditLog): NewAuditLogRow {\n const row: NewAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a snake_case DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function rowToAuditLog(row: AuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n ilike,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle column reference. */\nconst FIELD_COLUMNS = {\n tableName: auditLogs.tableName,\n recordId: auditLogs.recordId,\n actorId: auditLogs.actorId,\n severity: auditLogs.severity,\n operation: auditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (PostgreSQL dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(auditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(auditLogs.timestamp, condition.value);\n }\n case \"search\": {\n return or(\n ilike(auditLogs.label, condition.pattern),\n ilike(auditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n return sql`${auditLogs.compliance} @> ${JSON.stringify(condition.tags)}::jsonb`;\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(auditLogs.timestamp, condition.timestamp),\n and(\n eq(auditLogs.timestamp, condition.timestamp),\n idCompare(auditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition (combined with AND). Returns `undefined` when no filters are active.\n */\nexport function buildWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction.\n * Default is descending (newest first).\n */\nexport function buildOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(auditLogs.timestamp), asc(auditLogs.id)];\n }\n return [desc(auditLogs.timestamp), desc(auditLogs.id)];\n}\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, toCount, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nimport { sqliteAuditLogToRow, sqliteRowToAuditLog } from \"./sqlite-column-map.js\";\nimport {\n buildSqliteWhereConditions,\n buildSqliteOrderBy,\n} from \"./sqlite-query.js\";\n\n/**\n * Minimal shape for a Drizzle SQLite database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzleSqliteDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle SQLite database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleSqliteAuditAdapter(\n db: DrizzleSqliteDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = sqliteAuditLogToRow(log);\n await db.insert(sqliteAuditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildSqliteWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildSqliteOrderBy(sortOrder);\n\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(sqliteRowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(eq(sqliteAuditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return sqliteRowToAuditLog(row);\n },\n\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(sqliteAuditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(sqliteAuditLogs.tableName, options.tableName));\n }\n\n await (\n db.delete(sqliteAuditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n // SQLite: use changes() to get the number of rows affected\n const changesResult = (await (\n db.select({ count: sql`changes()` }) as ReturnType<typeof buildAggregateSelectChain>\n ).from(sqliteAuditLogs)) as Array<{ count: unknown }>;\n\n const deletedCount = changesResult[0] !== undefined ? toCount(changesResult[0].count) : 0;\n return { deletedCount };\n },\n\n async getStats(options?: { since?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(sqliteAuditLogs.timestamp, options.since)\n : undefined;\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${sqliteAuditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition);\n\n // Query 2: eventsPerDay — use strftime for SQLite\n const eventsPerDayQuery = (\n db.select({\n date: sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .orderBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: sqliteAuditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(sinceCondition, isNotNull(sqliteAuditLogs.actorId)))\n .groupBy(sqliteAuditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: sqliteAuditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sqliteAuditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: sqliteAuditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(sinceCondition)\n .groupBy(sqliteAuditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: sqliteAuditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(sinceCondition, isNotNull(sqliteAuditLogs.severity)))\n .groupBy(sqliteAuditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n */\nfunction buildSelectChain() {\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n return undefined as unknown as {\n from(table: unknown): AggregateFiltered & {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n return undefined as unknown as {\n where(condition: unknown): Promise<unknown>;\n };\n}\n","import {\n sqliteTable,\n text,\n integer,\n index,\n} from \"drizzle-orm/sqlite-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const sqliteAuditLogs = sqliteTable(\n \"audit_logs\",\n {\n id: text().primaryKey(),\n timestamp: integer({ mode: \"timestamp\" }).notNull(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: text(\"before_data\", { mode: \"json\" }),\n afterData: text(\"after_data\", { mode: \"json\" }),\n diff: text({ mode: \"json\" }),\n label: text(),\n description: text(),\n severity: text(),\n compliance: text({ mode: \"json\" }),\n notify: integer({ mode: \"boolean\" }),\n reason: text(),\n metadata: text({ mode: \"json\" }),\n redactedFields: text(\"redacted_fields\", { mode: \"json\" }),\n },\n (table) => ({\n tableNameTimestampIdx: index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n actorIdIdx: index(\"audit_logs_actor_id_idx\").on(table.actorId),\n recordIdIdx: index(\"audit_logs_record_id_idx\").on(table.recordId),\n tableNameRecordIdIdx: index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n operationIdx: index(\"audit_logs_operation_idx\").on(table.operation),\n timestampIdx: index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n timestampIdIdx: index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n }),\n);\n\nexport type SqliteAuditLogRow = InferSelectModel<typeof sqliteAuditLogs>;\nexport type NewSqliteAuditLogRow = InferInsertModel<typeof sqliteAuditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a SQLite DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function sqliteAuditLogToRow(log: AuditLog): NewSqliteAuditLogRow {\n const row: NewSqliteAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a SQLite DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function sqliteRowToAuditLog(row: SqliteAuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n like,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle SQLite column reference. */\nconst FIELD_COLUMNS = {\n tableName: sqliteAuditLogs.tableName,\n recordId: sqliteAuditLogs.recordId,\n actorId: sqliteAuditLogs.actorId,\n severity: sqliteAuditLogs.severity,\n operation: sqliteAuditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (SQLite dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"search\": {\n // SQLite LIKE is case-insensitive for ASCII by default\n return or(\n like(sqliteAuditLogs.label, condition.pattern),\n like(sqliteAuditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n // Per-tag EXISTS with json_each (AND semantics)\n const tagConditions: SQL[] = [];\n for (const tag of condition.tags) {\n tagConditions.push(\n sql`EXISTS (SELECT 1 FROM json_each(${sqliteAuditLogs.compliance}) WHERE value = ${tag})`,\n );\n }\n return and(...tagConditions);\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(sqliteAuditLogs.timestamp, condition.timestamp),\n and(\n eq(sqliteAuditLogs.timestamp, condition.timestamp),\n idCompare(sqliteAuditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition for SQLite. Returns `undefined` when no filters are active.\n */\nexport function buildSqliteWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction (SQLite schema).\n */\nexport function buildSqliteOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(sqliteAuditLogs.timestamp), asc(sqliteAuditLogs.id)];\n }\n return [desc(sqliteAuditLogs.timestamp), desc(sqliteAuditLogs.id)];\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getTableName } from \"drizzle-orm\";\nimport type { PgTable } from \"drizzle-orm/pg-core\";\nimport { OPERATION_MAP, type DrizzleMutationMethod } from \"./operation-map.js\";\n\nexport type MissingRecordIdBehavior = \"warn\" | \"skip\" | \"throw\";\n\nexport interface AuditProxyOptions {\n /**\n * Fallback column name for extracting `recordId` when the primary key\n * cannot be auto-detected from the Drizzle table schema. Defaults to `\"id\"`.\n *\n * In most cases this is not needed — the proxy reads `.primaryKey()` metadata\n * from Drizzle column objects at runtime.\n */\n primaryKey?: string;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n */\n onError?: (error: unknown) => void;\n /**\n * What to do when the `recordId` cannot be determined.\n *\n * - `\"warn\"` (default) — log via `onError`, proceed with `recordId: \"unknown\"`\n * - `\"skip\"` — silently drop the audit entry\n * - `\"throw\"` — throw an error (caught by the proxy's outer error handler)\n */\n onMissingRecordId?: MissingRecordIdBehavior;\n /**\n * Table names for which the pre-mutation SELECT should be skipped.\n * Use this for high-throughput tables where the extra query is too expensive.\n */\n skipBeforeState?: string[];\n /**\n * Safety limit: if the pre-mutation SELECT returns more rows than this,\n * skip the before-state capture and warn. Defaults to 1000.\n */\n maxBeforeStateRows?: number;\n}\n\ninterface BuilderContext {\n tableName: string;\n operation: AuditOperation;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n primaryKey: string;\n handleError: (error: unknown, table: string, op: string) => void;\n onMissingRecordId: MissingRecordIdBehavior;\n auditedSet: WeakSet<object>;\n dbTarget: Record<string, unknown>;\n table: PgTable;\n skipBeforeState: Set<string>;\n maxBeforeStateRows: number;\n}\n\n/**\n * Wraps a Drizzle database (or transaction) with a transparent proxy that\n * intercepts `db.insert()`, `db.update()`, `db.delete()` and calls\n * `captureLog()` after each successful mutation.\n *\n * The proxy also intercepts `db.transaction()` so that the `tx` handle\n * passed to the callback is itself proxied — this is what makes it work\n * with `better-tenant`, where all user code runs inside a transaction.\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`, so there is\n * a single source of truth for which tables are audited.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport function withAuditProxy<TDb extends {}>(\n db: TDb,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options?: AuditProxyOptions,\n): TDb {\n const primaryKey = options?.primaryKey ?? \"id\";\n const missingRecordIdPolicy = options?.onMissingRecordId ?? \"warn\";\n const skipBeforeState = new Set(options?.skipBeforeState ?? []);\n const maxBeforeStateRows = options?.maxBeforeStateRows ?? 1000;\n\n const handleError = (error: unknown, table: string, op: string): void => {\n try {\n if (options?.onError !== undefined) {\n options.onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(\n `audit-drizzle: capture failed for ${op} on ${table} — ${msg}`,\n );\n }\n } catch {\n // onError callback itself threw — swallow to prevent audit from breaking mutations.\n }\n };\n\n return new Proxy(db, {\n get(target, prop, receiver) {\n // Intercept insert / update / delete\n if (\n typeof prop === \"string\" &&\n (prop === \"insert\" || prop === \"update\" || prop === \"delete\")\n ) {\n const method = prop as DrizzleMutationMethod;\n const originalMethod = Reflect.get(target, prop, receiver) as (\n table: PgTable,\n ) => Record<string, unknown>;\n\n return (table: PgTable) => {\n const tableName = getTableName(table);\n const detectedPk = getPrimaryKeyColumnName(table);\n const effectivePk = detectedPk ?? primaryKey;\n const originalBuilder = originalMethod.call(target, table);\n\n const ctx: BuilderContext = {\n tableName,\n operation: OPERATION_MAP[method],\n captureLog,\n primaryKey: effectivePk,\n handleError,\n onMissingRecordId: missingRecordIdPolicy,\n auditedSet: new WeakSet<object>(),\n dbTarget: target as Record<string, unknown>,\n table,\n skipBeforeState,\n maxBeforeStateRows,\n };\n\n return wrapBuilder(originalBuilder, ctx);\n };\n }\n\n // Intercept transaction() so the tx handle is also proxied\n if (prop === \"transaction\") {\n const originalTransaction = Reflect.get(\n target,\n prop,\n receiver,\n ) as Function;\n\n return (...args: unknown[]) => {\n const callback = args[0] as (tx: unknown) => Promise<unknown>;\n const rest = args.slice(1);\n const wrappedCallback = (tx: unknown) => {\n const proxiedTx = withAuditProxy(\n tx as TDb,\n captureLog,\n options,\n );\n return callback(proxiedTx);\n };\n return originalTransaction.call(target, wrappedCallback, ...rest);\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\ninterface TrackedState {\n trackedValues?: Record<string, unknown>[];\n trackedSet?: Record<string, unknown>;\n trackedWhere?: unknown;\n hasReturning?: boolean;\n}\n\nfunction wrapBuilder(\n builder: Record<string, unknown>,\n ctx: BuilderContext,\n state: TrackedState = {},\n): Record<string, unknown> {\n return new Proxy(builder, {\n get(target, prop, receiver) {\n // Track .values() data for INSERT\n if (prop === \"values\") {\n return (...args: unknown[]) => {\n const data = args[0] as\n | Record<string, unknown>\n | Record<string, unknown>[];\n const newTrackedValues = Array.isArray(data) ? data : [data];\n const result = (\n target.values as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedValues: newTrackedValues,\n });\n };\n }\n\n // Track .set() data for UPDATE\n if (prop === \"set\") {\n return (...args: unknown[]) => {\n const newTrackedSet = args[0] as Record<string, unknown>;\n const result = (\n target.set as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedSet: newTrackedSet,\n });\n };\n }\n\n // Track .where() condition for UPDATE/DELETE pre-SELECT\n if (prop === \"where\") {\n return (...args: unknown[]) => {\n const condition = args[0];\n const result = (\n target.where as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedWhere: condition,\n });\n };\n }\n\n // Track .returning() so we know the result contains full rows\n if (prop === \"returning\") {\n return (...args: unknown[]) => {\n const result = (\n target.returning as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n hasReturning: true,\n });\n };\n }\n\n // Intercept .then() — the terminal await point\n if (prop === \"then\") {\n return (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => {\n const thenFn = Reflect.get(target, \"then\", receiver) as (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown;\n\n const needsBeforeState =\n (ctx.operation === \"UPDATE\" || ctx.operation === \"DELETE\") &&\n state.trackedWhere !== undefined &&\n !ctx.skipBeforeState.has(ctx.tableName) &&\n // For DELETE with .returning(), returned rows ARE the before-state\n !(ctx.operation === \"DELETE\" && state.hasReturning === true);\n\n if (needsBeforeState) {\n return executeWithBeforeState(\n target,\n thenFn,\n ctx,\n state,\n onFulfilled,\n onRejected,\n );\n }\n\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n };\n }\n\n // All other methods: forward and re-wrap\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return (...args: unknown[]) => {\n const result = (value as (...a: unknown[]) => unknown).apply(\n target,\n args,\n );\n if (result !== null && typeof result === \"object\") {\n return wrapBuilder(\n result as Record<string, unknown>,\n ctx,\n state,\n );\n }\n return result;\n };\n }\n\n return value;\n },\n });\n}\n\n/**\n * Executes a pre-mutation SELECT, then the original mutation, then fires\n * captureLog with matched before/after pairs.\n */\nfunction executeWithBeforeState(\n target: Record<string, unknown>,\n thenFn: (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown,\n ctx: BuilderContext,\n state: TrackedState,\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n): unknown {\n // Issue the pre-mutation SELECT, then chain the original mutation\n const beforePromise = fetchBeforeState(ctx, state);\n\n return beforePromise.then(\n (beforeRows) => {\n // Now execute the original mutation\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n if (beforeRows !== undefined) {\n await fireCaptureLogWithBeforeState(\n result,\n beforeRows,\n ctx,\n state,\n );\n } else {\n // Fallback: before-state unavailable, use original behavior\n await fireCaptureLog(result, ctx, state);\n }\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n (error) => {\n // Pre-SELECT failed — log error, fall back to original behavior.\n ctx.handleError(error, ctx.tableName, ctx.operation);\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (captureError) {\n ctx.handleError(captureError, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n );\n}\n\n/**\n * Issues a SELECT to fetch the rows that will be affected by the mutation.\n * Returns `undefined` if the fetch should be skipped (too many rows).\n */\nasync function fetchBeforeState(\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<Record<string, unknown>[] | undefined> {\n const selectFn = ctx.dbTarget.select as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (selectFn === undefined) {\n return undefined;\n }\n\n const selectBuilder = selectFn.call(ctx.dbTarget);\n const fromFn = selectBuilder.from as\n | ((table: PgTable) => Record<string, unknown>)\n | undefined;\n if (fromFn === undefined) {\n return undefined;\n }\n\n const fromBuilder = fromFn.call(selectBuilder, ctx.table);\n const whereFn = fromBuilder.where as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (whereFn === undefined) {\n return undefined;\n }\n\n const whereBuilder = whereFn.call(fromBuilder, state.trackedWhere);\n\n // Apply LIMIT to avoid fetching unbounded rows into memory.\n // Fetch limit + 1 so we can detect when the limit is exceeded.\n const limitFn = whereBuilder.limit as\n | ((n: number) => Record<string, unknown>)\n | undefined;\n const fetchLimit = ctx.maxBeforeStateRows + 1;\n const queryBuilder =\n limitFn !== undefined\n ? limitFn.call(whereBuilder, fetchLimit)\n : whereBuilder;\n\n const rows = (await queryBuilder) as unknown as Record<string, unknown>[];\n\n if (rows.length > ctx.maxBeforeStateRows) {\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state SELECT returned more than ${ctx.maxBeforeStateRows} rows, skipping before-state capture`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return undefined;\n }\n\n return rows;\n}\n\nfunction getPrimaryKeyColumnName(table: PgTable): string | undefined {\n for (const [key, value] of Object.entries(table)) {\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"primary\" in value &&\n value.primary === true\n ) {\n return key;\n }\n }\n return undefined;\n}\n\nfunction extractRecordId(\n row: Record<string, unknown>,\n primaryKey: string,\n): string {\n const value = row[primaryKey];\n if (value !== undefined && value !== null) {\n return String(value);\n }\n return \"\";\n}\n\n/**\n * Applies the missing-record-id policy.\n * Returns `true` if the audit entry should still be written (with \"unknown\"),\n * or `false` if it should be skipped.\n */\nfunction applyMissingRecordIdPolicy(\n ctx: BuilderContext,\n detail: string,\n): boolean {\n const policy = ctx.onMissingRecordId;\n if (policy === \"skip\") {\n return false;\n }\n if (policy === \"throw\") {\n throw new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n );\n }\n // \"warn\" — report via handleError, then proceed\n ctx.handleError(\n new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return true;\n}\n\n/**\n * Fires captureLog with matched before/after pairs using pre-mutation state.\n */\nasync function fireCaptureLogWithBeforeState(\n result: unknown,\n beforeRows: Record<string, unknown>[],\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"UPDATE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared between SELECT and UPDATE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but UPDATE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original UPDATE behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n // Build before map keyed by primary key\n const beforeMap = new Map<string, Record<string, unknown>>();\n for (const row of beforeRows) {\n const pk = extractRecordId(row, ctx.primaryKey);\n if (pk !== \"\") {\n beforeMap.set(pk, row);\n }\n }\n\n if (beforeMap.size === 0) {\n // All before-rows lacked the primary key column — likely misconfiguration\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state rows exist but none have primary key \"${ctx.primaryKey}\" — check primaryKey option`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n if (state.hasReturning === true && returnedRows.length > 0) {\n // After-state from .returning() result\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n const beforeRow = beforeMap.get(recordId);\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(beforeRow !== undefined && { before: beforeRow }),\n after: row,\n });\n }\n } else {\n // After-state: merge .set() data onto each before-row\n for (const [pk, beforeRow] of beforeMap) {\n const afterRow =\n state.trackedSet !== undefined\n ? { ...beforeRow, ...state.trackedSet }\n : beforeRow;\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: pk,\n before: beforeRow,\n after: afterRow,\n });\n }\n }\n } else if (ctx.operation === \"DELETE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared before DELETE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but DELETE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n for (const beforeRow of beforeRows) {\n const recordId = extractRecordId(beforeRow, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"before-state row missing primary key\",\n );\n if (!shouldProceed) {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n before: beforeRow,\n });\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: beforeRow,\n });\n }\n }\n}\n\nasync function fireCaptureLog(\n result: unknown,\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const { trackedValues, trackedSet } = state;\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"INSERT\") {\n const values = trackedValues ?? [];\n for (let i = 0; i < values.length; i++) {\n const row = values[i];\n if (row === undefined) {\n continue;\n }\n const returnedRow =\n returnedRows.length > i\n ? (returnedRows[i] as Record<string, unknown>)\n : undefined;\n const recordId =\n returnedRow !== undefined\n ? extractRecordId(returnedRow, ctx.primaryKey)\n : extractRecordId(row, ctx.primaryKey);\n\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() or include the primary key in .values()\",\n );\n if (!shouldProceed) {\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n after: row,\n });\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n after: row,\n });\n }\n } else if (ctx.operation === \"UPDATE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(trackedSet !== undefined && { after: trackedSet }),\n });\n }\n } else if (trackedSet !== undefined) {\n const recordId = extractRecordId(trackedSet, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: recordId || \"unknown\",\n after: trackedSet,\n });\n }\n } else if (ctx.operation === \"DELETE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: row,\n });\n }\n } else {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n });\n }\n }\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\nexport const OPERATION_MAP = {\n insert: \"INSERT\",\n update: \"UPDATE\",\n delete: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type DrizzleMutationMethod = keyof typeof OPERATION_MAP;\n","export { drizzleAuditAdapter } from \"./adapter.js\";\nexport type { DrizzlePgDatabase } from \"./adapter.js\";\nexport { drizzleSqliteAuditAdapter } from \"./sqlite-adapter.js\";\nexport type { DrizzleSqliteDatabase } from \"./sqlite-adapter.js\";\nexport { withAuditProxy } from \"./proxy.js\";\nexport type { AuditProxyOptions, MissingRecordIdBehavior } from \"./proxy.js\";\nexport { auditLogs } from \"./schema.js\";\nexport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\nexport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nexport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\nexport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\nexport { encodeCursor, decodeCursor } from \"@usebetterdev/audit-core\";\n"],"mappings":"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","names":["and","eq","gte","lt","sql","desc","eq","lt","and","gte","sql","desc","encodeCursor","assembleStats","and","eq","gte","lt","isNotNull","sql","desc","text","index","isAuditOperation","isAuditSeverity","decodeCursor","interpretFilters","and","or","eq","gt","lt","gte","lte","inArray","asc","desc","sql","FIELD_COLUMNS","mapCondition","eq","inArray","gte","lte","or","sql","and","gt","lt","decodeCursor","interpretFilters","asc","desc","DEFAULT_LIMIT","MAX_LIMIT","encodeCursor","eq","lt","and","sql","gte","isNotNull","desc","assembleStats","encodeCursor","decodeCursor"]}
1
+ {"version":3,"sources":["../src/adapter.ts","../src/schema.ts","../src/column-map.ts","../src/query.ts","../src/sqlite-adapter.ts","../src/sqlite-schema.ts","../src/sqlite-column-map.ts","../src/sqlite-query.ts","../src/proxy.ts","../src/operation-map.ts","../src/index.ts"],"sourcesContent":["import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\nimport { auditLogToRow, rowToAuditLog } from \"./column-map.js\";\nimport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\n\n/**\n * Minimal shape for a Drizzle pg database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzlePgDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle pg database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleAuditAdapter(\n db: DrizzlePgDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = auditLogToRow(log);\n await db.insert(auditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildOrderBy(sortOrder);\n\n // The duck-typed interface returns `unknown` from select().\n // We build the full Drizzle query chain and cast at the boundary.\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(rowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(auditLogs)\n .where(eq(auditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof auditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return rowToAuditLog(row);\n },\n\n /**\n * Delete audit log entries older than `before`.\n *\n * **Warning:** Large deletes may hold a row-level lock on the `audit_logs`\n * table for an extended period. Run during low-traffic windows when purging\n * millions of rows.\n */\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(auditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(auditLogs.tableName, options.tableName));\n }\n\n const result = await (\n db.delete(auditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n const rowCount = (result as { rowCount?: number | null }).rowCount;\n return { deletedCount: rowCount ?? 0 };\n },\n\n async getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(auditLogs.timestamp, options.since)\n : undefined;\n const untilCondition =\n options?.until !== undefined\n ? lt(auditLogs.timestamp, options.until)\n : undefined;\n const timeCondition = and(sinceCondition, untilCondition);\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${auditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition);\n\n // Query 2: eventsPerDay\n const eventsPerDayQuery = (\n db.select({\n date: sql`date_trunc('day', ${auditLogs.timestamp})::date`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .orderBy(sql`date_trunc('day', ${auditLogs.timestamp})`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: auditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(timeCondition, isNotNull(auditLogs.actorId)))\n .groupBy(auditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: auditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(auditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: auditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(timeCondition)\n .groupBy(auditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: auditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(auditLogs)\n .where(and(timeCondition, isNotNull(auditLogs.severity)))\n .groupBy(auditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n * Not exported; exists to avoid `any`.\n */\nfunction buildSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n // This function is never called — it exists only for its return type.\n return undefined as unknown as {\n where(condition: unknown): Promise<{ rowCount: number }>;\n };\n}\n","import {\n pgTable,\n uuid,\n timestamp,\n text,\n jsonb,\n boolean,\n index,\n} from \"drizzle-orm/pg-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const auditLogs = pgTable(\n \"audit_logs\",\n {\n id: uuid().primaryKey().defaultRandom(),\n timestamp: timestamp({ withTimezone: true }).notNull().defaultNow(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: jsonb(\"before_data\"),\n afterData: jsonb(\"after_data\"),\n diff: jsonb(),\n label: text(),\n description: text(),\n severity: text(),\n compliance: jsonb(),\n notify: boolean(),\n reason: text(),\n metadata: jsonb(),\n redactedFields: jsonb(\"redacted_fields\"),\n },\n (table) => [\n index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n index(\"audit_logs_actor_id_idx\").on(table.actorId),\n index(\"audit_logs_record_id_idx\").on(table.recordId),\n index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n index(\"audit_logs_operation_idx\").on(table.operation),\n index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n ],\n);\n\nexport type AuditLogRow = InferSelectModel<typeof auditLogs>;\nexport type NewAuditLogRow = InferInsertModel<typeof auditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a snake_case DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function auditLogToRow(log: AuditLog): NewAuditLogRow {\n const row: NewAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a snake_case DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function rowToAuditLog(row: AuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n ilike,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { auditLogs } from \"./schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle column reference. */\nconst FIELD_COLUMNS = {\n tableName: auditLogs.tableName,\n recordId: auditLogs.recordId,\n actorId: auditLogs.actorId,\n severity: auditLogs.severity,\n operation: auditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (PostgreSQL dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(auditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(auditLogs.timestamp, condition.value);\n }\n case \"search\": {\n return or(\n ilike(auditLogs.label, condition.pattern),\n ilike(auditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n return sql`${auditLogs.compliance} @> ${JSON.stringify(condition.tags)}::jsonb`;\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(auditLogs.timestamp, condition.timestamp),\n and(\n eq(auditLogs.timestamp, condition.timestamp),\n idCompare(auditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition (combined with AND). Returns `undefined` when no filters are active.\n */\nexport function buildWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction.\n * Default is descending (newest first).\n */\nexport function buildOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(auditLogs.timestamp), asc(auditLogs.id)];\n }\n return [desc(auditLogs.timestamp), desc(auditLogs.id)];\n}\n","import type {\n AuditDatabaseAdapter,\n AuditLog,\n AuditQuerySpec,\n AuditQueryResult,\n AuditStats,\n} from \"@usebetterdev/audit-core\";\nimport { encodeCursor, toCount, assembleStats } from \"@usebetterdev/audit-core\";\nimport { and, eq, gte, lt, isNotNull, sql, desc } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nimport { sqliteAuditLogToRow, sqliteRowToAuditLog } from \"./sqlite-column-map.js\";\nimport {\n buildSqliteWhereConditions,\n buildSqliteOrderBy,\n} from \"./sqlite-query.js\";\n\n/**\n * Minimal shape for a Drizzle SQLite database that supports insert, select, and delete operations.\n * Duck-typed so callers don't need the full Drizzle generic types.\n */\nexport interface DrizzleSqliteDatabase {\n insert(table: unknown): {\n values(data: unknown): { execute(): Promise<unknown> };\n };\n select(fields?: unknown): unknown;\n delete(table: unknown): unknown;\n}\n\nconst DEFAULT_LIMIT = 50;\nconst MAX_LIMIT = 250;\n\n/**\n * Creates an `AuditDatabaseAdapter` backed by a Drizzle SQLite database.\n *\n * Implements `writeLog` for inserting audit entries and `queryLogs` for\n * filtered, cursor-paginated queries.\n */\nexport function drizzleSqliteAuditAdapter(\n db: DrizzleSqliteDatabase,\n): AuditDatabaseAdapter {\n return {\n async writeLog(log: AuditLog): Promise<void> {\n const row = sqliteAuditLogToRow(log);\n await db.insert(sqliteAuditLogs).values(row).execute();\n },\n\n async queryLogs(spec: AuditQuerySpec): Promise<AuditQueryResult> {\n const sortOrder = spec.sortOrder ?? \"desc\";\n const limit = Math.min(spec.limit ?? DEFAULT_LIMIT, MAX_LIMIT);\n\n const combined = buildSqliteWhereConditions(spec.filters, spec.cursor, sortOrder);\n\n const fetchLimit = limit + 1;\n const orderColumns = buildSqliteOrderBy(sortOrder);\n\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(combined)\n .orderBy(...orderColumns)\n .limit(fetchLimit);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n\n const hasNextPage = rows.length > limit;\n const resultRows = hasNextPage ? rows.slice(0, -1) : rows;\n const entries = resultRows.map(sqliteRowToAuditLog);\n const lastRow = resultRows[resultRows.length - 1];\n\n if (hasNextPage && lastRow !== undefined) {\n return { entries, nextCursor: encodeCursor(lastRow.timestamp, lastRow.id) };\n }\n\n return { entries };\n },\n\n async getLogById(id: string): Promise<AuditLog | null> {\n const query = (db.select() as ReturnType<typeof buildSelectChain>)\n .from(sqliteAuditLogs)\n .where(eq(sqliteAuditLogs.id, id))\n .limit(1);\n\n const rows = (await query) as (typeof sqliteAuditLogs.$inferSelect)[];\n const row = rows[0];\n if (row === undefined) {\n return null;\n }\n return sqliteRowToAuditLog(row);\n },\n\n async purgeLogs(options: { before: Date; tableName?: string }): Promise<{ deletedCount: number }> {\n if (options.tableName !== undefined && options.tableName.trim().length === 0) {\n throw new Error(\"purgeLogs: tableName must be a non-empty string when provided\");\n }\n\n const conditions = [lt(sqliteAuditLogs.timestamp, options.before)];\n if (options.tableName !== undefined) {\n conditions.push(eq(sqliteAuditLogs.tableName, options.tableName));\n }\n\n await (\n db.delete(sqliteAuditLogs) as ReturnType<typeof buildDeleteChain>\n ).where(and(...conditions));\n\n // SQLite: use changes() to get the number of rows affected\n const changesResult = (await (\n db.select({ count: sql`changes()` }) as ReturnType<typeof buildAggregateSelectChain>\n ).from(sqliteAuditLogs)) as Array<{ count: unknown }>;\n\n const deletedCount = changesResult[0] !== undefined ? toCount(changesResult[0].count) : 0;\n return { deletedCount };\n },\n\n async getStats(options?: { since?: Date; until?: Date }): Promise<AuditStats> {\n const sinceCondition =\n options?.since !== undefined\n ? gte(sqliteAuditLogs.timestamp, options.since)\n : undefined;\n const untilCondition =\n options?.until !== undefined\n ? lt(sqliteAuditLogs.timestamp, options.until)\n : undefined;\n const timeCondition = and(sinceCondition, untilCondition);\n\n // Query 1: totalLogs + tablesAudited\n const summaryQuery = (\n db.select({\n totalLogs: sql`count(*)`,\n tablesAudited: sql`count(DISTINCT ${sqliteAuditLogs.tableName})`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition);\n\n // Query 2: eventsPerDay — use strftime for SQLite\n const eventsPerDayQuery = (\n db.select({\n date: sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .orderBy(sql`strftime('%Y-%m-%d', ${sqliteAuditLogs.timestamp}, 'unixepoch')`)\n .limit(365);\n\n // Query 3: topActors (filter NULL actors)\n const topActorsQuery = (\n db.select({\n actorId: sqliteAuditLogs.actorId,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(timeCondition, isNotNull(sqliteAuditLogs.actorId)))\n .groupBy(sqliteAuditLogs.actorId)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 4: topTables\n const topTablesQuery = (\n db.select({\n tableName: sqliteAuditLogs.tableName,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sqliteAuditLogs.tableName)\n .orderBy(desc(sql`count(*)`))\n .limit(10);\n\n // Query 5: operationBreakdown\n const operationQuery = (\n db.select({\n operation: sqliteAuditLogs.operation,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(timeCondition)\n .groupBy(sqliteAuditLogs.operation);\n\n // Query 6: severityBreakdown (filter NULL severity)\n const severityQuery = (\n db.select({\n severity: sqliteAuditLogs.severity,\n count: sql`count(*)`,\n }) as ReturnType<typeof buildAggregateSelectChain>\n )\n .from(sqliteAuditLogs)\n .where(and(timeCondition, isNotNull(sqliteAuditLogs.severity)))\n .groupBy(sqliteAuditLogs.severity);\n\n const results = await Promise.all([\n summaryQuery,\n eventsPerDayQuery,\n topActorsQuery,\n topTablesQuery,\n operationQuery,\n severityQuery,\n ]);\n\n const [\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n ] = results as unknown as [\n Array<{ totalLogs: unknown; tablesAudited: unknown }>,\n Array<{ date: unknown; count: unknown }>,\n Array<{ actorId: unknown; count: unknown }>,\n Array<{ tableName: unknown; count: unknown }>,\n Array<{ operation: unknown; count: unknown }>,\n Array<{ severity: unknown; count: unknown }>,\n ];\n\n return assembleStats(\n summaryRows,\n eventsPerDayRows,\n topActorsRows,\n topTablesRows,\n operationRows,\n severityRows,\n );\n },\n };\n}\n\n/**\n * Type helper for the Drizzle select chain — used only for casting.\n */\nfunction buildSelectChain() {\n return undefined as unknown as {\n from(table: unknown): {\n where(condition: unknown): {\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n orderBy(...columns: unknown[]): {\n limit(n: number): Promise<unknown[]>;\n };\n limit(n: number): Promise<unknown[]>;\n };\n };\n}\n\n/** Awaitable node: can be awaited directly or chained further. */\ninterface AggregateTerminal extends Promise<unknown[]> {\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After groupBy: can orderBy, limit, or await. */\ninterface AggregateGrouped extends Promise<unknown[]> {\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/** After where: can groupBy, orderBy, limit, or await. */\ninterface AggregateFiltered extends Promise<unknown[]> {\n groupBy(...columns: unknown[]): AggregateGrouped;\n orderBy(...columns: unknown[]): AggregateTerminal;\n limit(n: number): Promise<unknown[]>;\n}\n\n/**\n * Type helper for aggregate select chains with groupBy support — used only for casting.\n */\nfunction buildAggregateSelectChain() {\n return undefined as unknown as {\n from(table: unknown): AggregateFiltered & {\n where(condition: unknown): AggregateFiltered;\n groupBy(...columns: unknown[]): AggregateGrouped;\n };\n };\n}\n\n/**\n * Type helper for the Drizzle delete chain — used only for casting.\n */\nfunction buildDeleteChain() {\n return undefined as unknown as {\n where(condition: unknown): Promise<unknown>;\n };\n}\n","import {\n sqliteTable,\n text,\n integer,\n index,\n} from \"drizzle-orm/sqlite-core\";\nimport type { InferSelectModel, InferInsertModel } from \"drizzle-orm\";\n\nexport const sqliteAuditLogs = sqliteTable(\n \"audit_logs\",\n {\n id: text().primaryKey(),\n timestamp: integer({ mode: \"timestamp\" }).notNull(),\n tableName: text(\"table_name\").notNull(),\n operation: text().notNull(),\n recordId: text(\"record_id\").notNull(),\n actorId: text(\"actor_id\"),\n beforeData: text(\"before_data\", { mode: \"json\" }),\n afterData: text(\"after_data\", { mode: \"json\" }),\n diff: text({ mode: \"json\" }),\n label: text(),\n description: text(),\n severity: text(),\n compliance: text({ mode: \"json\" }),\n notify: integer({ mode: \"boolean\" }),\n reason: text(),\n metadata: text({ mode: \"json\" }),\n redactedFields: text(\"redacted_fields\", { mode: \"json\" }),\n },\n (table) => ({\n tableNameTimestampIdx: index(\"audit_logs_table_name_timestamp_idx\").on(\n table.tableName,\n table.timestamp,\n ),\n actorIdIdx: index(\"audit_logs_actor_id_idx\").on(table.actorId),\n recordIdIdx: index(\"audit_logs_record_id_idx\").on(table.recordId),\n tableNameRecordIdIdx: index(\"audit_logs_table_name_record_id_idx\").on(\n table.tableName,\n table.recordId,\n ),\n operationIdx: index(\"audit_logs_operation_idx\").on(table.operation),\n timestampIdx: index(\"audit_logs_timestamp_idx\").on(table.timestamp),\n timestampIdIdx: index(\"audit_logs_timestamp_id_idx\").on(table.timestamp, table.id),\n }),\n);\n\nexport type SqliteAuditLogRow = InferSelectModel<typeof sqliteAuditLogs>;\nexport type NewSqliteAuditLogRow = InferInsertModel<typeof sqliteAuditLogs>;\n","import type { AuditLog } from \"@usebetterdev/audit-core\";\nimport { isAuditOperation, isAuditSeverity } from \"@usebetterdev/audit-core\";\nimport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\n\n/**\n * Converts a camelCase `AuditLog` to a SQLite DB row for insertion.\n * Only includes optional fields when defined (satisfies `exactOptionalPropertyTypes`).\n */\nexport function sqliteAuditLogToRow(log: AuditLog): NewSqliteAuditLogRow {\n const row: NewSqliteAuditLogRow = {\n id: log.id,\n timestamp: log.timestamp,\n tableName: log.tableName,\n operation: log.operation,\n recordId: log.recordId,\n };\n\n if (log.actorId !== undefined) {\n row.actorId = log.actorId;\n }\n if (log.beforeData !== undefined) {\n row.beforeData = log.beforeData;\n }\n if (log.afterData !== undefined) {\n row.afterData = log.afterData;\n }\n if (log.diff !== undefined) {\n row.diff = log.diff;\n }\n if (log.label !== undefined) {\n row.label = log.label;\n }\n if (log.description !== undefined) {\n row.description = log.description;\n }\n if (log.severity !== undefined) {\n row.severity = log.severity;\n }\n if (log.compliance !== undefined) {\n row.compliance = log.compliance;\n }\n if (log.notify !== undefined) {\n row.notify = log.notify;\n }\n if (log.reason !== undefined) {\n row.reason = log.reason;\n }\n if (log.metadata !== undefined) {\n row.metadata = log.metadata;\n }\n if (log.redactedFields !== undefined) {\n row.redactedFields = log.redactedFields;\n }\n\n return row;\n}\n\n/**\n * Converts a SQLite DB row to a camelCase `AuditLog`.\n * Only includes optional fields when non-null.\n */\nexport function sqliteRowToAuditLog(row: SqliteAuditLogRow): AuditLog {\n if (!isAuditOperation(row.operation)) {\n throw new Error(\n `Invalid audit operation: \"${row.operation}\". Expected one of: INSERT, UPDATE, DELETE`,\n );\n }\n\n const log: AuditLog = {\n id: row.id,\n timestamp: row.timestamp,\n tableName: row.tableName,\n operation: row.operation,\n recordId: row.recordId,\n };\n\n if (row.actorId !== null) {\n log.actorId = row.actorId;\n }\n if (row.beforeData !== null) {\n log.beforeData = row.beforeData as Record<string, unknown>;\n }\n if (row.afterData !== null) {\n log.afterData = row.afterData as Record<string, unknown>;\n }\n if (row.diff !== null) {\n log.diff = row.diff as { changedFields: string[] };\n }\n if (row.label !== null) {\n log.label = row.label;\n }\n if (row.description !== null) {\n log.description = row.description;\n }\n if (row.severity !== null && row.severity !== undefined) {\n if (!isAuditSeverity(row.severity)) {\n throw new Error(\n `Invalid audit severity: \"${row.severity}\". Expected one of: low, medium, high, critical`,\n );\n }\n log.severity = row.severity;\n }\n if (row.compliance !== null) {\n log.compliance = row.compliance as string[];\n }\n if (row.notify !== null) {\n log.notify = row.notify;\n }\n if (row.reason !== null) {\n log.reason = row.reason;\n }\n if (row.metadata !== null) {\n log.metadata = row.metadata as Record<string, unknown>;\n }\n if (row.redactedFields !== null) {\n log.redactedFields = row.redactedFields as string[];\n }\n\n return log;\n}\n","import type { AuditQueryFilters } from \"@usebetterdev/audit-core\";\nimport {\n decodeCursor,\n interpretFilters,\n} from \"@usebetterdev/audit-core\";\nimport type { FilterCondition } from \"@usebetterdev/audit-core\";\nimport {\n and,\n or,\n eq,\n gt,\n lt,\n gte,\n lte,\n inArray,\n like,\n asc,\n desc,\n sql,\n} from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { sqliteAuditLogs } from \"./sqlite-schema.js\";\n\n/** Maps an AuditFilterField name to the corresponding Drizzle SQLite column reference. */\nconst FIELD_COLUMNS = {\n tableName: sqliteAuditLogs.tableName,\n recordId: sqliteAuditLogs.recordId,\n actorId: sqliteAuditLogs.actorId,\n severity: sqliteAuditLogs.severity,\n operation: sqliteAuditLogs.operation,\n} as const;\n\n/** Maps a single FilterCondition to a Drizzle SQL expression (SQLite dialect). */\nfunction mapCondition(condition: FilterCondition): SQL | undefined {\n switch (condition.kind) {\n case \"eq\": {\n return eq(FIELD_COLUMNS[condition.field], condition.value);\n }\n case \"in\": {\n return inArray(FIELD_COLUMNS[condition.field], condition.values);\n }\n case \"timestampGte\": {\n return gte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"timestampLte\": {\n return lte(sqliteAuditLogs.timestamp, condition.value);\n }\n case \"search\": {\n // SQLite LIKE is case-insensitive for ASCII by default\n return or(\n like(sqliteAuditLogs.label, condition.pattern),\n like(sqliteAuditLogs.description, condition.pattern),\n );\n }\n case \"compliance\": {\n // Per-tag EXISTS with json_each (AND semantics)\n const tagConditions: SQL[] = [];\n for (const tag of condition.tags) {\n tagConditions.push(\n sql`EXISTS (SELECT 1 FROM json_each(${sqliteAuditLogs.compliance}) WHERE value = ${tag})`,\n );\n }\n return and(...tagConditions);\n }\n case \"cursor\": {\n const tsCompare = condition.sortOrder === \"asc\" ? gt : lt;\n const idCompare = condition.sortOrder === \"asc\" ? gt : lt;\n return or(\n tsCompare(sqliteAuditLogs.timestamp, condition.timestamp),\n and(\n eq(sqliteAuditLogs.timestamp, condition.timestamp),\n idCompare(sqliteAuditLogs.id, condition.id),\n ),\n );\n }\n }\n}\n\n/**\n * Translates `AuditQueryFilters` (+ optional cursor) into a Drizzle `SQL`\n * condition for SQLite. Returns `undefined` when no filters are active.\n */\nexport function buildSqliteWhereConditions(\n filters: AuditQueryFilters,\n cursor?: string,\n sortOrder?: \"asc\" | \"desc\",\n): SQL | undefined {\n const decoded = cursor !== undefined ? decodeCursor(cursor) : undefined;\n const irConditions = interpretFilters(filters, {\n cursor: decoded,\n sortOrder,\n });\n\n const sqlConditions: SQL[] = [];\n for (const c of irConditions) {\n const mapped = mapCondition(c);\n if (mapped !== undefined) {\n sqlConditions.push(mapped);\n }\n }\n\n if (sqlConditions.length === 0) {\n return undefined;\n }\n\n return and(...sqlConditions);\n}\n\n/**\n * Returns the `orderBy` columns for the given sort direction (SQLite schema).\n */\nexport function buildSqliteOrderBy(sortOrder: \"asc\" | \"desc\") {\n if (sortOrder === \"asc\") {\n return [asc(sqliteAuditLogs.timestamp), asc(sqliteAuditLogs.id)];\n }\n return [desc(sqliteAuditLogs.timestamp), desc(sqliteAuditLogs.id)];\n}\n","import type { CaptureLogInput, AuditOperation } from \"@usebetterdev/audit-core\";\nimport { getTableName } from \"drizzle-orm\";\nimport type { PgTable } from \"drizzle-orm/pg-core\";\nimport { OPERATION_MAP, type DrizzleMutationMethod } from \"./operation-map.js\";\n\nexport type MissingRecordIdBehavior = \"warn\" | \"skip\" | \"throw\";\n\nexport interface AuditProxyOptions {\n /**\n * Fallback column name for extracting `recordId` when the primary key\n * cannot be auto-detected from the Drizzle table schema. Defaults to `\"id\"`.\n *\n * In most cases this is not needed — the proxy reads `.primaryKey()` metadata\n * from Drizzle column objects at runtime.\n */\n primaryKey?: string;\n /**\n * Called when audit capture fails. Falls back to `console.error` if not set.\n */\n onError?: (error: unknown) => void;\n /**\n * What to do when the `recordId` cannot be determined.\n *\n * - `\"warn\"` (default) — log via `onError`, proceed with `recordId: \"unknown\"`\n * - `\"skip\"` — silently drop the audit entry\n * - `\"throw\"` — throw an error (caught by the proxy's outer error handler)\n */\n onMissingRecordId?: MissingRecordIdBehavior;\n /**\n * Table names for which the pre-mutation SELECT should be skipped.\n * Use this for high-throughput tables where the extra query is too expensive.\n */\n skipBeforeState?: string[];\n /**\n * Safety limit: if the pre-mutation SELECT returns more rows than this,\n * skip the before-state capture and warn. Defaults to 1000.\n */\n maxBeforeStateRows?: number;\n}\n\ninterface BuilderContext {\n tableName: string;\n operation: AuditOperation;\n captureLog: (input: CaptureLogInput) => Promise<void>;\n primaryKey: string;\n handleError: (error: unknown, table: string, op: string) => void;\n onMissingRecordId: MissingRecordIdBehavior;\n auditedSet: WeakSet<object>;\n dbTarget: Record<string, unknown>;\n table: PgTable;\n skipBeforeState: Set<string>;\n maxBeforeStateRows: number;\n}\n\n/**\n * Wraps a Drizzle database (or transaction) with a transparent proxy that\n * intercepts `db.insert()`, `db.update()`, `db.delete()` and calls\n * `captureLog()` after each successful mutation.\n *\n * The proxy also intercepts `db.transaction()` so that the `tx` handle\n * passed to the callback is itself proxied — this is what makes it work\n * with `better-tenant`, where all user code runs inside a transaction.\n *\n * Filtering by audited tables is NOT done here — `captureLog` (from\n * `betterAudit`) already skips tables not in `auditTables`, so there is\n * a single source of truth for which tables are audited.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport function withAuditProxy<TDb extends {}>(\n db: TDb,\n captureLog: (input: CaptureLogInput) => Promise<void>,\n options?: AuditProxyOptions,\n): TDb {\n const primaryKey = options?.primaryKey ?? \"id\";\n const missingRecordIdPolicy = options?.onMissingRecordId ?? \"warn\";\n const skipBeforeState = new Set(options?.skipBeforeState ?? []);\n const maxBeforeStateRows = options?.maxBeforeStateRows ?? 1000;\n\n const handleError = (error: unknown, table: string, op: string): void => {\n try {\n if (options?.onError !== undefined) {\n options.onError(error);\n } else {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(\n `audit-drizzle: capture failed for ${op} on ${table} — ${msg}`,\n );\n }\n } catch {\n // onError callback itself threw — swallow to prevent audit from breaking mutations.\n }\n };\n\n return new Proxy(db, {\n get(target, prop, receiver) {\n // Intercept insert / update / delete\n if (\n typeof prop === \"string\" &&\n (prop === \"insert\" || prop === \"update\" || prop === \"delete\")\n ) {\n const method = prop as DrizzleMutationMethod;\n const originalMethod = Reflect.get(target, prop, receiver) as (\n table: PgTable,\n ) => Record<string, unknown>;\n\n return (table: PgTable) => {\n const tableName = getTableName(table);\n const detectedPk = getPrimaryKeyColumnName(table);\n const effectivePk = detectedPk ?? primaryKey;\n const originalBuilder = originalMethod.call(target, table);\n\n const ctx: BuilderContext = {\n tableName,\n operation: OPERATION_MAP[method],\n captureLog,\n primaryKey: effectivePk,\n handleError,\n onMissingRecordId: missingRecordIdPolicy,\n auditedSet: new WeakSet<object>(),\n dbTarget: target as Record<string, unknown>,\n table,\n skipBeforeState,\n maxBeforeStateRows,\n };\n\n return wrapBuilder(originalBuilder, ctx);\n };\n }\n\n // Intercept transaction() so the tx handle is also proxied\n if (prop === \"transaction\") {\n const originalTransaction = Reflect.get(\n target,\n prop,\n receiver,\n ) as Function;\n\n return (...args: unknown[]) => {\n const callback = args[0] as (tx: unknown) => Promise<unknown>;\n const rest = args.slice(1);\n const wrappedCallback = (tx: unknown) => {\n const proxiedTx = withAuditProxy(\n tx as TDb,\n captureLog,\n options,\n );\n return callback(proxiedTx);\n };\n return originalTransaction.call(target, wrappedCallback, ...rest);\n };\n }\n\n return Reflect.get(target, prop, receiver);\n },\n });\n}\n\ninterface TrackedState {\n trackedValues?: Record<string, unknown>[];\n trackedSet?: Record<string, unknown>;\n trackedWhere?: unknown;\n hasReturning?: boolean;\n}\n\nfunction wrapBuilder(\n builder: Record<string, unknown>,\n ctx: BuilderContext,\n state: TrackedState = {},\n): Record<string, unknown> {\n return new Proxy(builder, {\n get(target, prop, receiver) {\n // Track .values() data for INSERT\n if (prop === \"values\") {\n return (...args: unknown[]) => {\n const data = args[0] as\n | Record<string, unknown>\n | Record<string, unknown>[];\n const newTrackedValues = Array.isArray(data) ? data : [data];\n const result = (\n target.values as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedValues: newTrackedValues,\n });\n };\n }\n\n // Track .set() data for UPDATE\n if (prop === \"set\") {\n return (...args: unknown[]) => {\n const newTrackedSet = args[0] as Record<string, unknown>;\n const result = (\n target.set as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedSet: newTrackedSet,\n });\n };\n }\n\n // Track .where() condition for UPDATE/DELETE pre-SELECT\n if (prop === \"where\") {\n return (...args: unknown[]) => {\n const condition = args[0];\n const result = (\n target.where as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n trackedWhere: condition,\n });\n };\n }\n\n // Track .returning() so we know the result contains full rows\n if (prop === \"returning\") {\n return (...args: unknown[]) => {\n const result = (\n target.returning as (...a: unknown[]) => Record<string, unknown>\n )(...args);\n return wrapBuilder(result, ctx, {\n ...state,\n hasReturning: true,\n });\n };\n }\n\n // Intercept .then() — the terminal await point\n if (prop === \"then\") {\n return (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => {\n const thenFn = Reflect.get(target, \"then\", receiver) as (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown;\n\n const needsBeforeState =\n (ctx.operation === \"UPDATE\" || ctx.operation === \"DELETE\") &&\n state.trackedWhere !== undefined &&\n !ctx.skipBeforeState.has(ctx.tableName) &&\n // For DELETE with .returning(), returned rows ARE the before-state\n !(ctx.operation === \"DELETE\" && state.hasReturning === true);\n\n if (needsBeforeState) {\n return executeWithBeforeState(\n target,\n thenFn,\n ctx,\n state,\n onFulfilled,\n onRejected,\n );\n }\n\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n };\n }\n\n // All other methods: forward and re-wrap\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return (...args: unknown[]) => {\n const result = (value as (...a: unknown[]) => unknown).apply(\n target,\n args,\n );\n if (result !== null && typeof result === \"object\") {\n return wrapBuilder(\n result as Record<string, unknown>,\n ctx,\n state,\n );\n }\n return result;\n };\n }\n\n return value;\n },\n });\n}\n\n/**\n * Executes a pre-mutation SELECT, then the original mutation, then fires\n * captureLog with matched before/after pairs.\n */\nfunction executeWithBeforeState(\n target: Record<string, unknown>,\n thenFn: (\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n ) => unknown,\n ctx: BuilderContext,\n state: TrackedState,\n onFulfilled?: (value: unknown) => unknown,\n onRejected?: (reason: unknown) => unknown,\n): unknown {\n // Issue the pre-mutation SELECT, then chain the original mutation\n const beforePromise = fetchBeforeState(ctx, state);\n\n return beforePromise.then(\n (beforeRows) => {\n // Now execute the original mutation\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n if (beforeRows !== undefined) {\n await fireCaptureLogWithBeforeState(\n result,\n beforeRows,\n ctx,\n state,\n );\n } else {\n // Fallback: before-state unavailable, use original behavior\n await fireCaptureLog(result, ctx, state);\n }\n } catch (error) {\n ctx.handleError(error, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n (error) => {\n // Pre-SELECT failed — log error, fall back to original behavior.\n ctx.handleError(error, ctx.tableName, ctx.operation);\n return thenFn.call(\n target,\n async (result: unknown) => {\n if (ctx.auditedSet.has(target)) {\n return onFulfilled?.(result);\n }\n ctx.auditedSet.add(target);\n\n try {\n await fireCaptureLog(result, ctx, state);\n } catch (captureError) {\n ctx.handleError(captureError, ctx.tableName, ctx.operation);\n }\n return onFulfilled?.(result);\n },\n onRejected,\n );\n },\n );\n}\n\n/**\n * Issues a SELECT to fetch the rows that will be affected by the mutation.\n * Returns `undefined` if the fetch should be skipped (too many rows).\n */\nasync function fetchBeforeState(\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<Record<string, unknown>[] | undefined> {\n const selectFn = ctx.dbTarget.select as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (selectFn === undefined) {\n return undefined;\n }\n\n const selectBuilder = selectFn.call(ctx.dbTarget);\n const fromFn = selectBuilder.from as\n | ((table: PgTable) => Record<string, unknown>)\n | undefined;\n if (fromFn === undefined) {\n return undefined;\n }\n\n const fromBuilder = fromFn.call(selectBuilder, ctx.table);\n const whereFn = fromBuilder.where as\n | ((...args: unknown[]) => Record<string, unknown>)\n | undefined;\n if (whereFn === undefined) {\n return undefined;\n }\n\n const whereBuilder = whereFn.call(fromBuilder, state.trackedWhere);\n\n // Apply LIMIT to avoid fetching unbounded rows into memory.\n // Fetch limit + 1 so we can detect when the limit is exceeded.\n const limitFn = whereBuilder.limit as\n | ((n: number) => Record<string, unknown>)\n | undefined;\n const fetchLimit = ctx.maxBeforeStateRows + 1;\n const queryBuilder =\n limitFn !== undefined\n ? limitFn.call(whereBuilder, fetchLimit)\n : whereBuilder;\n\n const rows = (await queryBuilder) as unknown as Record<string, unknown>[];\n\n if (rows.length > ctx.maxBeforeStateRows) {\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state SELECT returned more than ${ctx.maxBeforeStateRows} rows, skipping before-state capture`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return undefined;\n }\n\n return rows;\n}\n\nfunction getPrimaryKeyColumnName(table: PgTable): string | undefined {\n for (const [key, value] of Object.entries(table)) {\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"primary\" in value &&\n value.primary === true\n ) {\n return key;\n }\n }\n return undefined;\n}\n\nfunction extractRecordId(\n row: Record<string, unknown>,\n primaryKey: string,\n): string {\n const value = row[primaryKey];\n if (value !== undefined && value !== null) {\n return String(value);\n }\n return \"\";\n}\n\n/**\n * Applies the missing-record-id policy.\n * Returns `true` if the audit entry should still be written (with \"unknown\"),\n * or `false` if it should be skipped.\n */\nfunction applyMissingRecordIdPolicy(\n ctx: BuilderContext,\n detail: string,\n): boolean {\n const policy = ctx.onMissingRecordId;\n if (policy === \"skip\") {\n return false;\n }\n if (policy === \"throw\") {\n throw new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n );\n }\n // \"warn\" — report via handleError, then proceed\n ctx.handleError(\n new Error(\n `audit-drizzle: missing recordId for ${ctx.operation} on ${ctx.tableName} — ${detail}`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n return true;\n}\n\n/**\n * Fires captureLog with matched before/after pairs using pre-mutation state.\n */\nasync function fireCaptureLogWithBeforeState(\n result: unknown,\n beforeRows: Record<string, unknown>[],\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"UPDATE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared between SELECT and UPDATE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but UPDATE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original UPDATE behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n // Build before map keyed by primary key\n const beforeMap = new Map<string, Record<string, unknown>>();\n for (const row of beforeRows) {\n const pk = extractRecordId(row, ctx.primaryKey);\n if (pk !== \"\") {\n beforeMap.set(pk, row);\n }\n }\n\n if (beforeMap.size === 0) {\n // All before-rows lacked the primary key column — likely misconfiguration\n ctx.handleError(\n new Error(\n `audit-drizzle: before-state rows exist but none have primary key \"${ctx.primaryKey}\" — check primaryKey option`,\n ),\n ctx.tableName,\n ctx.operation,\n );\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n if (state.hasReturning === true && returnedRows.length > 0) {\n // After-state from .returning() result\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n const beforeRow = beforeMap.get(recordId);\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(beforeRow !== undefined && { before: beforeRow }),\n after: row,\n });\n }\n } else {\n // After-state: merge .set() data onto each before-row\n for (const [pk, beforeRow] of beforeMap) {\n const afterRow =\n state.trackedSet !== undefined\n ? { ...beforeRow, ...state.trackedSet }\n : beforeRow;\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: pk,\n before: beforeRow,\n after: afterRow,\n });\n }\n }\n } else if (ctx.operation === \"DELETE\") {\n if (beforeRows.length === 0) {\n // Race condition: rows disappeared before DELETE\n ctx.handleError(\n new Error(\n \"audit-drizzle: before-state SELECT returned 0 rows but DELETE succeeded — possible race condition\",\n ),\n ctx.tableName,\n ctx.operation,\n );\n // Fall back to original behavior\n await fireCaptureLog(result, ctx, state);\n return;\n }\n\n for (const beforeRow of beforeRows) {\n const recordId = extractRecordId(beforeRow, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"before-state row missing primary key\",\n );\n if (!shouldProceed) {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n before: beforeRow,\n });\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: beforeRow,\n });\n }\n }\n}\n\nasync function fireCaptureLog(\n result: unknown,\n ctx: BuilderContext,\n state: TrackedState,\n): Promise<void> {\n const { trackedValues, trackedSet } = state;\n const returnedRows = Array.isArray(result) ? result : [];\n\n if (ctx.operation === \"INSERT\") {\n const values = trackedValues ?? [];\n for (let i = 0; i < values.length; i++) {\n const row = values[i];\n if (row === undefined) {\n continue;\n }\n const returnedRow =\n returnedRows.length > i\n ? (returnedRows[i] as Record<string, unknown>)\n : undefined;\n const recordId =\n returnedRow !== undefined\n ? extractRecordId(returnedRow, ctx.primaryKey)\n : extractRecordId(row, ctx.primaryKey);\n\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() or include the primary key in .values()\",\n );\n if (!shouldProceed) {\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n after: row,\n });\n continue;\n }\n\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n after: row,\n });\n }\n } else if (ctx.operation === \"UPDATE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n ...(trackedSet !== undefined && { after: trackedSet }),\n });\n }\n } else if (trackedSet !== undefined) {\n const recordId = extractRecordId(trackedSet, ctx.primaryKey);\n if (recordId === \"\") {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: recordId || \"unknown\",\n after: trackedSet,\n });\n }\n } else if (ctx.operation === \"DELETE\") {\n if (returnedRows.length > 0) {\n for (const returnedRow of returnedRows) {\n const row = returnedRow as Record<string, unknown>;\n const recordId = extractRecordId(row, ctx.primaryKey);\n if (recordId === \"\") {\n continue;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId,\n before: row,\n });\n }\n } else {\n const shouldProceed = applyMissingRecordIdPolicy(\n ctx,\n \"use .returning() to get the record id\",\n );\n if (!shouldProceed) {\n return;\n }\n await ctx.captureLog({\n tableName: ctx.tableName,\n operation: ctx.operation,\n recordId: \"unknown\",\n });\n }\n }\n}\n","import type { AuditOperation } from \"@usebetterdev/audit-core\";\n\nexport const OPERATION_MAP = {\n insert: \"INSERT\",\n update: \"UPDATE\",\n delete: \"DELETE\",\n} as const satisfies Record<string, AuditOperation>;\n\nexport type DrizzleMutationMethod = keyof typeof OPERATION_MAP;\n","export { drizzleAuditAdapter } from \"./adapter.js\";\nexport type { DrizzlePgDatabase } from \"./adapter.js\";\nexport { drizzleSqliteAuditAdapter } from \"./sqlite-adapter.js\";\nexport type { DrizzleSqliteDatabase } from \"./sqlite-adapter.js\";\nexport { withAuditProxy } from \"./proxy.js\";\nexport type { AuditProxyOptions, MissingRecordIdBehavior } from \"./proxy.js\";\nexport { auditLogs } from \"./schema.js\";\nexport type { AuditLogRow, NewAuditLogRow } from \"./schema.js\";\nexport { sqliteAuditLogs } from \"./sqlite-schema.js\";\nexport type { SqliteAuditLogRow, NewSqliteAuditLogRow } from \"./sqlite-schema.js\";\nexport {\n buildWhereConditions,\n buildOrderBy,\n} from \"./query.js\";\nexport { encodeCursor, decodeCursor } from \"@usebetterdev/audit-core\";\n"],"mappings":"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","names":["and","eq","gte","lt","sql","desc","eq","lt","and","gte","sql","desc","encodeCursor","assembleStats","and","eq","gte","lt","isNotNull","sql","desc","text","index","isAuditOperation","isAuditSeverity","decodeCursor","interpretFilters","and","or","eq","gt","lt","gte","lte","inArray","asc","desc","sql","FIELD_COLUMNS","mapCondition","eq","inArray","gte","lte","or","sql","and","gt","lt","decodeCursor","interpretFilters","asc","desc","DEFAULT_LIMIT","MAX_LIMIT","encodeCursor","eq","lt","and","sql","gte","isNotNull","desc","assembleStats","encodeCursor","decodeCursor"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebetterdev/audit-drizzle",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "repository": "github:usebetter-dev/usebetter",
5
5
  "bugs": "https://github.com/usebetter-dev/usebetter/issues",
6
6
  "homepage": "https://github.com/usebetter-dev/usebetter#readme",
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "drizzle-orm": "^0.45.0",
28
- "@usebetterdev/audit-core": "0.7.0"
28
+ "@usebetterdev/audit-core": "0.8.1"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "pg": ">=8.0.0",
@@ -53,7 +53,7 @@
53
53
  "tsup": "^8.3.5",
54
54
  "typescript": "~5.7.2",
55
55
  "vitest": "^2.1.6",
56
- "@usebetterdev/test-utils": "0.5.2"
56
+ "@usebetterdev/test-utils": "0.5.4"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=22"