@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 +16 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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(
|
|
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(
|
|
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)(
|
|
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(
|
|
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(
|
|
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)(
|
|
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(
|
|
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(
|
|
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)(
|
|
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(
|
|
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(
|
|
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)(
|
|
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,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
56
|
+
"@usebetterdev/test-utils": "0.5.4"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">=22"
|