@willyim/drizzle-audit 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +102 -21
  2. package/dist/src/d1/audit-log-schema.d.ts +3 -2
  3. package/dist/src/d1/audit-log-schema.d.ts.map +1 -1
  4. package/dist/src/d1/audit-log-schema.js +14 -4
  5. package/dist/src/d1/index.d.ts +2 -1
  6. package/dist/src/d1/index.d.ts.map +1 -1
  7. package/dist/src/d1/runtime.d.ts +11 -11
  8. package/dist/src/d1/runtime.d.ts.map +1 -1
  9. package/dist/src/d1/runtime.js +26 -9
  10. package/dist/src/d1/sql.d.ts +2 -2
  11. package/dist/src/d1/sql.d.ts.map +1 -1
  12. package/dist/src/d1/sql.js +61 -29
  13. package/dist/src/d1/types.d.ts +10 -2
  14. package/dist/src/d1/types.d.ts.map +1 -1
  15. package/dist/src/d1-runtime/with-audit.d.ts +15 -11
  16. package/dist/src/d1-runtime/with-audit.d.ts.map +1 -1
  17. package/dist/src/d1-runtime/with-audit.js +53 -60
  18. package/dist/src/index.d.ts +2 -1
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/index.js +1 -1
  21. package/dist/src/postgres/audit-log-schema.d.ts +7 -14
  22. package/dist/src/postgres/audit-log-schema.d.ts.map +1 -1
  23. package/dist/src/postgres/audit-log-schema.js +14 -4
  24. package/dist/src/postgres/index.d.ts +3 -2
  25. package/dist/src/postgres/index.d.ts.map +1 -1
  26. package/dist/src/postgres/index.js +1 -1
  27. package/dist/src/postgres/runtime.d.ts +6 -8
  28. package/dist/src/postgres/runtime.d.ts.map +1 -1
  29. package/dist/src/postgres/runtime.js +15 -3
  30. package/dist/src/postgres/sql.d.ts +10 -7
  31. package/dist/src/postgres/sql.d.ts.map +1 -1
  32. package/dist/src/postgres/sql.js +72 -50
  33. package/dist/src/postgres/types.d.ts +10 -2
  34. package/dist/src/postgres/types.d.ts.map +1 -1
  35. package/dist/test/d1-async.integration.test.d.ts +13 -0
  36. package/dist/test/d1-async.integration.test.d.ts.map +1 -0
  37. package/dist/test/d1-async.integration.test.js +159 -0
  38. package/dist/test/d1.integration.test.js +71 -4
  39. package/dist/test/sqlite.integration.test.d.ts +2 -0
  40. package/dist/test/sqlite.integration.test.d.ts.map +1 -0
  41. package/dist/test/{d1-runtime.integration.test.js → sqlite.integration.test.js} +82 -25
  42. package/package.json +2 -1
  43. package/dist/test/d1-runtime.integration.test.d.ts +0 -2
  44. package/dist/test/d1-runtime.integration.test.d.ts.map +0 -1
  45. package/dist/test/postgres.integration.test.d.ts +0 -2
  46. package/dist/test/postgres.integration.test.d.ts.map +0 -1
  47. package/dist/test/postgres.integration.test.js +0 -286
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/d1/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEtC;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/d1/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEtC;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;IACd,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mDAAmD;IACnD,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iHAAiH;IACjH,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA"}
@@ -2,7 +2,8 @@ import { type InferInsertModel, type InferSelectModel, type SQL } from "drizzle-
2
2
  import type { SQLiteTable } from "drizzle-orm/sqlite-core";
3
3
  export type AuditContext = {
4
4
  userId: string;
5
- workspaceId?: string;
5
+ /** Map of extra audit context column name → value (matching contextColumns). */
6
+ context?: Record<string, string>;
6
7
  };
7
8
  export type AuditLogInsertShape = {
8
9
  table_name: string;
@@ -22,37 +23,40 @@ export type DrizzleSQLiteDb = {
22
23
  update: (table: any) => any;
23
24
  delete: (table: any) => any;
24
25
  select: (fields?: any) => any;
25
- transaction: (cb: (tx: any) => any) => any;
26
26
  };
27
27
  export type AuditedDb<TDb extends DrizzleSQLiteDb> = {
28
28
  /**
29
29
  * Insert a row and log an INSERT audit event.
30
30
  * Returns the inserted row.
31
31
  */
32
- insert: <T extends SQLiteTable>(table: T, data: InferInsertModel<T>) => InferSelectModel<T>;
32
+ insert: <T extends SQLiteTable>(table: T, data: InferInsertModel<T>) => Promise<InferSelectModel<T>>;
33
33
  /**
34
34
  * Update rows matching `where` and log an UPDATE audit event for each affected row.
35
35
  * Captures old_data (before) and new_data (after).
36
36
  * Returns the updated rows.
37
37
  */
38
- update: <T extends SQLiteTable>(table: T, where: SQL, data: Partial<InferInsertModel<T>>) => InferSelectModel<T>[];
38
+ update: <T extends SQLiteTable>(table: T, where: SQL, data: Partial<InferInsertModel<T>>) => Promise<InferSelectModel<T>[]>;
39
39
  /**
40
40
  * Delete rows matching `where` and log a DELETE audit event for each affected row.
41
41
  * Captures old_data.
42
42
  * Returns the deleted rows.
43
43
  */
44
- delete: <T extends SQLiteTable>(table: T, where: SQL) => InferSelectModel<T>[];
44
+ delete: <T extends SQLiteTable>(table: T, where: SQL) => Promise<InferSelectModel<T>[]>;
45
45
  /** Access the underlying db for non-audited operations. */
46
46
  db: TDb;
47
47
  };
48
48
  /**
49
49
  * Creates an audited wrapper around a Drizzle SQLite database.
50
- * Each insert/update/delete is wrapped in a transaction that atomically
51
- * writes to both the target table and the audit_logs table.
50
+ * Each insert/update/delete runs the data write and audit log insert sequentially
51
+ * as individual statements (no transaction). This works with both D1 (async) and
52
+ * better-sqlite3 (sync), but writes are best-effort: a failure between the data
53
+ * write and the audit insert can leave one without the other.
54
+ *
55
+ * For atomic auditing on D1, use the trigger-based approach from `@willyim/drizzle-audit/d1`.
52
56
  *
53
57
  * @param db - A Drizzle SQLite database instance (D1, better-sqlite3, libsql)
54
58
  * @param auditTable - The Drizzle table definition for audit_logs
55
- * @param context - The audit context (userId, optional workspaceId)
59
+ * @param context - The audit context (userId, optional context columns)
56
60
  *
57
61
  * @example
58
62
  * import { withAudit } from "drizzle-audit/d1-runtime"
@@ -62,13 +66,13 @@ export type AuditedDb<TDb extends DrizzleSQLiteDb> = {
62
66
  * const audited = withAudit(db, auditLogs, { userId: session.userId })
63
67
  *
64
68
  * // Audited insert
65
- * audited.insert(users, { id: "u1", name: "Ada" })
69
+ * await audited.insert(users, { id: "u1", name: "Ada" })
66
70
  *
67
71
  * // Audited update — captures old + new data
68
- * audited.update(users, eq(users.id, "u1"), { name: "Ada Lovelace" })
72
+ * await audited.update(users, eq(users.id, "u1"), { name: "Ada Lovelace" })
69
73
  *
70
74
  * // Audited delete — captures deleted data
71
- * audited.delete(users, eq(users.id, "u1"))
75
+ * await audited.delete(users, eq(users.id, "u1"))
72
76
  *
73
77
  * // Non-audited access
74
78
  * audited.db.select().from(users).all()
@@ -1 +1 @@
1
- {"version":3,"file":"with-audit.d.ts","sourceRoot":"","sources":["../../../src/d1-runtime/with-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAIxE,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,GAAG,CAAA;IAC7B,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,GAAG,KAAK,GAAG,CAAA;CAC3C,CAAA;AAgBD,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,eAAe,IAAI;IACnD;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,KACtB,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAExB;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAC/B,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAA;IAE1B;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,GAAG,KACP,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAA;IAE1B,2DAA2D;IAC3D,EAAE,EAAE,GAAG,CAAA;CACR,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,SAAS,CAAC,GAAG,SAAS,eAAe,EACnD,EAAE,EAAE,GAAG,EACP,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,YAAY,GACpB,SAAS,CAAC,GAAG,CAAC,CAoHhB"}
1
+ {"version":3,"file":"with-audit.d.ts","sourceRoot":"","sources":["../../../src/d1-runtime/with-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,GAAG,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAIxE,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;IAC3B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,GAAG,CAAA;CAC9B,CAAA;AAgBD,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,eAAe,IAAI;IACnD;;;OAGG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,KACtB,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IAEjC;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAEnC;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,SAAS,WAAW,EAC5B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,GAAG,KACP,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAEnC,2DAA2D;IAC3D,EAAE,EAAE,GAAG,CAAA;CACR,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,SAAS,CAAC,GAAG,SAAS,eAAe,EACnD,EAAE,EAAE,GAAG,EACP,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,YAAY,GACpB,SAAS,CAAC,GAAG,CAAC,CAqGhB"}
@@ -12,12 +12,16 @@ function getRowId(row, pk) {
12
12
  }
13
13
  /**
14
14
  * Creates an audited wrapper around a Drizzle SQLite database.
15
- * Each insert/update/delete is wrapped in a transaction that atomically
16
- * writes to both the target table and the audit_logs table.
15
+ * Each insert/update/delete runs the data write and audit log insert sequentially
16
+ * as individual statements (no transaction). This works with both D1 (async) and
17
+ * better-sqlite3 (sync), but writes are best-effort: a failure between the data
18
+ * write and the audit insert can leave one without the other.
19
+ *
20
+ * For atomic auditing on D1, use the trigger-based approach from `@willyim/drizzle-audit/d1`.
17
21
  *
18
22
  * @param db - A Drizzle SQLite database instance (D1, better-sqlite3, libsql)
19
23
  * @param auditTable - The Drizzle table definition for audit_logs
20
- * @param context - The audit context (userId, optional workspaceId)
24
+ * @param context - The audit context (userId, optional context columns)
21
25
  *
22
26
  * @example
23
27
  * import { withAudit } from "drizzle-audit/d1-runtime"
@@ -27,19 +31,19 @@ function getRowId(row, pk) {
27
31
  * const audited = withAudit(db, auditLogs, { userId: session.userId })
28
32
  *
29
33
  * // Audited insert
30
- * audited.insert(users, { id: "u1", name: "Ada" })
34
+ * await audited.insert(users, { id: "u1", name: "Ada" })
31
35
  *
32
36
  * // Audited update — captures old + new data
33
- * audited.update(users, eq(users.id, "u1"), { name: "Ada Lovelace" })
37
+ * await audited.update(users, eq(users.id, "u1"), { name: "Ada Lovelace" })
34
38
  *
35
39
  * // Audited delete — captures deleted data
36
- * audited.delete(users, eq(users.id, "u1"))
40
+ * await audited.delete(users, eq(users.id, "u1"))
37
41
  *
38
42
  * // Non-audited access
39
43
  * audited.db.select().from(users).all()
40
44
  */
41
45
  export function withAudit(db, auditTable, context) {
42
- const workspaceIdColumn = (() => {
46
+ const extraColumns = (() => {
43
47
  const cols = getTableColumns(auditTable);
44
48
  const known = new Set([
45
49
  "id",
@@ -51,10 +55,17 @@ export function withAudit(db, auditTable, context) {
51
55
  "new_data",
52
56
  "created_at",
53
57
  ]);
54
- const extra = Object.keys(cols).find((k) => !known.has(k));
55
- return extra ?? null;
58
+ return Object.keys(cols).filter((k) => !known.has(k));
56
59
  })();
60
+ const contextValues = { ...(context.context ?? {}) };
57
61
  function buildAuditRow(tableName, operation, rowId, oldData, newData) {
62
+ const extra = {};
63
+ for (const column of extraColumns) {
64
+ const value = contextValues[column];
65
+ if (value !== undefined && value !== "") {
66
+ extra[column] = value;
67
+ }
68
+ }
58
69
  return {
59
70
  table_name: tableName,
60
71
  operation,
@@ -62,69 +73,51 @@ export function withAudit(db, auditTable, context) {
62
73
  user_id: context.userId,
63
74
  old_data: oldData ? JSON.stringify(oldData) : null,
64
75
  new_data: newData ? JSON.stringify(newData) : null,
65
- ...(workspaceIdColumn && context.workspaceId
66
- ? { [workspaceIdColumn]: context.workspaceId }
67
- : {}),
76
+ ...extra,
68
77
  };
69
78
  }
70
79
  return {
71
80
  db,
72
- insert(table, data) {
81
+ async insert(table, data) {
73
82
  const tableName = getTableName(table);
74
83
  const pk = getPrimaryKeyColumn(table);
75
- return db.transaction((tx) => {
76
- const [row] = tx.insert(table).values(data).returning().all();
77
- const rowId = getRowId(row, pk);
78
- tx.insert(auditTable)
79
- .values(buildAuditRow(tableName, "INSERT", rowId, null, row))
80
- .run();
81
- return row;
82
- });
84
+ const [row] = await db.insert(table).values(data).returning().all();
85
+ const rowId = getRowId(row, pk);
86
+ await db
87
+ .insert(auditTable)
88
+ .values(buildAuditRow(tableName, "INSERT", rowId, null, row))
89
+ .run();
90
+ return row;
83
91
  },
84
- update(table, where, data) {
92
+ async update(table, where, data) {
85
93
  const tableName = getTableName(table);
86
94
  const pk = getPrimaryKeyColumn(table);
87
- return db.transaction((tx) => {
88
- const oldRows = tx
89
- .select()
90
- .from(table)
91
- .where(where)
92
- .all();
93
- const newRows = tx
94
- .update(table)
95
- .set(data)
96
- .where(where)
97
- .returning()
98
- .all();
99
- for (let i = 0; i < newRows.length; i++) {
100
- const oldRow = oldRows[i] ?? null;
101
- const newRow = newRows[i];
102
- const rowId = getRowId(newRow, pk);
103
- tx.insert(auditTable)
104
- .values(buildAuditRow(tableName, "UPDATE", rowId, oldRow, newRow))
105
- .run();
106
- }
107
- return newRows;
108
- });
95
+ const oldRows = await db.select().from(table).where(where).all();
96
+ const newRows = await db.update(table).set(data).where(where).returning().all();
97
+ for (let i = 0; i < newRows.length; i++) {
98
+ const oldRow = oldRows[i] ?? null;
99
+ const newRow = newRows[i];
100
+ const rowId = getRowId(newRow, pk);
101
+ await db
102
+ .insert(auditTable)
103
+ .values(buildAuditRow(tableName, "UPDATE", rowId, oldRow, newRow))
104
+ .run();
105
+ }
106
+ return newRows;
109
107
  },
110
- delete(table, where) {
108
+ async delete(table, where) {
111
109
  const tableName = getTableName(table);
112
110
  const pk = getPrimaryKeyColumn(table);
113
- return db.transaction((tx) => {
114
- const oldRows = tx
115
- .select()
116
- .from(table)
117
- .where(where)
118
- .all();
119
- tx.delete(table).where(where).run();
120
- for (const oldRow of oldRows) {
121
- const rowId = getRowId(oldRow, pk);
122
- tx.insert(auditTable)
123
- .values(buildAuditRow(tableName, "DELETE", rowId, oldRow, null))
124
- .run();
125
- }
126
- return oldRows;
127
- });
111
+ const oldRows = await db.select().from(table).where(where).all();
112
+ await db.delete(table).where(where).run();
113
+ for (const oldRow of oldRows) {
114
+ const rowId = getRowId(oldRow, pk);
115
+ await db
116
+ .insert(auditTable)
117
+ .values(buildAuditRow(tableName, "DELETE", rowId, oldRow, null))
118
+ .run();
119
+ }
120
+ return oldRows;
128
121
  },
129
122
  };
130
123
  }
@@ -1,5 +1,6 @@
1
1
  export * from "./postgres/index.js";
2
- export * from "./d1/index.js";
2
+ export { clearD1AuditContext, createAttachD1AuditTriggerSql, createAttachD1AuditTriggersSql, createAttachD1AuditTriggerSqlWithColumns, createAttachD1AuditTriggersSqlWithColumns, createD1AuditInstallSql, d1AuditContextTable, d1AuditLogTable, setD1AuditContext, withD1AuditedTransaction, } from "./d1/index.js";
3
+ export type { D1AuditContextOptions, D1AuditInstallOptions, D1AuditLogTableOptions, D1AuditSqlExecutor, D1AuditTriggerTarget, D1AuditTriggerTargetWithColumns, } from "./d1/index.js";
3
4
  export * from "./d1-runtime/index.js";
4
5
  export * from "./compute-diff.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,8BAA8B,EAC9B,wCAAwC,EACxC,yCAAyC,EACzC,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,+BAA+B,GAChC,MAAM,eAAe,CAAA;AACtB,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA"}
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from "./postgres/index.js";
2
- export * from "./d1/index.js";
2
+ export { clearD1AuditContext, createAttachD1AuditTriggerSql, createAttachD1AuditTriggersSql, createAttachD1AuditTriggerSqlWithColumns, createAttachD1AuditTriggersSqlWithColumns, createD1AuditInstallSql, d1AuditContextTable, d1AuditLogTable, setD1AuditContext, withD1AuditedTransaction, } from "./d1/index.js";
3
3
  export * from "./d1-runtime/index.js";
4
4
  export * from "./compute-diff.js";
@@ -1,6 +1,7 @@
1
+ import type { AuditContextColumn } from "./types.js";
1
2
  export type PgAuditLogTableOptions = {
2
- /** When set (e.g. "workspace_id"), the table definition includes this optional column to match the install. */
3
- workspaceIdColumn?: string;
3
+ /** Extra context columns to include in the table definition, matching the install. */
4
+ contextColumns?: AuditContextColumn[];
4
5
  };
5
6
  export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): import("drizzle-orm/pg-core").PgTableWithColumns<{
6
7
  name: "audit_logs";
@@ -20,7 +21,6 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
20
21
  enumValues: undefined;
21
22
  identity: undefined;
22
23
  generated: undefined;
23
- insertType: unknown;
24
24
  }>;
25
25
  new_data: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").PgJsonbBuilder, {
26
26
  name: string;
@@ -36,7 +36,6 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
36
36
  enumValues: undefined;
37
37
  identity: undefined;
38
38
  generated: undefined;
39
- insertType: unknown;
40
39
  }>;
41
40
  created_at: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").PgTimestampBuilder>>, {
42
41
  name: string;
@@ -52,7 +51,6 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
52
51
  enumValues: undefined;
53
52
  identity: undefined;
54
53
  generated: undefined;
55
- insertType: Date | undefined;
56
54
  }>;
57
55
  id: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetIsPrimaryKey<import("drizzle-orm/pg-core").PgBigSerial53Builder>, {
58
56
  name: string;
@@ -68,9 +66,8 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
68
66
  enumValues: undefined;
69
67
  identity: undefined;
70
68
  generated: undefined;
71
- insertType: number | undefined;
72
69
  }>;
73
- table_name: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<undefined>>, {
70
+ table_name: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
74
71
  name: string;
75
72
  tableName: "audit_logs";
76
73
  dataType: "string";
@@ -84,9 +81,8 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
84
81
  enumValues: undefined;
85
82
  identity: undefined;
86
83
  generated: undefined;
87
- insertType: string;
88
84
  }>;
89
- operation: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<undefined>>, {
85
+ operation: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
90
86
  name: string;
91
87
  tableName: "audit_logs";
92
88
  dataType: "string";
@@ -100,9 +96,8 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
100
96
  enumValues: undefined;
101
97
  identity: undefined;
102
98
  generated: undefined;
103
- insertType: string;
104
99
  }>;
105
- row_id: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").PgTextBuilder<undefined>, {
100
+ row_id: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
106
101
  name: string;
107
102
  tableName: "audit_logs";
108
103
  dataType: "string";
@@ -116,9 +111,8 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
116
111
  enumValues: undefined;
117
112
  identity: undefined;
118
113
  generated: undefined;
119
- insertType: string | null | undefined;
120
114
  }>;
121
- user_id: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").PgTextBuilder<undefined>, {
115
+ user_id: import("drizzle-orm/pg-core").PgBuildColumn<"audit_logs", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
122
116
  name: string;
123
117
  tableName: "audit_logs";
124
118
  dataType: "string";
@@ -132,7 +126,6 @@ export declare function pgAuditLogTable(options?: PgAuditLogTableOptions): impor
132
126
  enumValues: undefined;
133
127
  identity: undefined;
134
128
  generated: undefined;
135
- insertType: string | null | undefined;
136
129
  }>;
137
130
  };
138
131
  dialect: "pg";
@@ -1 +1 @@
1
- {"version":3,"file":"audit-log-schema.d.ts","sourceRoot":"","sources":["../../../src/postgres/audit-log-schema.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,sBAAsB,GAAG;IACnC,+GAA+G;IAC/G,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2B/D"}
1
+ {"version":3,"file":"audit-log-schema.d.ts","sourceRoot":"","sources":["../../../src/postgres/audit-log-schema.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,MAAM,sBAAsB,GAAG;IACnC,sFAAsF;IACtF,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAA;CACtC,CAAA;AAiBD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyB/D"}
@@ -1,15 +1,25 @@
1
1
  import { bigserial, index, jsonb, pgTable, text, timestamp, } from "drizzle-orm/pg-core";
2
+ function resolveColumns(options) {
3
+ const columns = [];
4
+ const seen = new Set();
5
+ for (const entry of options?.contextColumns ?? []) {
6
+ const column = entry.column?.trim();
7
+ if (column && !seen.has(column)) {
8
+ seen.add(column);
9
+ columns.push(column);
10
+ }
11
+ }
12
+ return columns;
13
+ }
2
14
  export function pgAuditLogTable(options) {
3
- const workspaceIdColumn = options?.workspaceIdColumn?.trim();
15
+ const contextColumns = resolveColumns(options);
4
16
  const columns = {
5
17
  id: bigserial("id", { mode: "number" }).primaryKey(),
6
18
  table_name: text("table_name").notNull(),
7
19
  operation: text("operation").notNull(),
8
20
  row_id: text("row_id"),
9
21
  user_id: text("user_id"),
10
- ...(workspaceIdColumn
11
- ? { [workspaceIdColumn]: text(workspaceIdColumn) }
12
- : {}),
22
+ ...Object.fromEntries(contextColumns.map((c) => [c, text(c)])),
13
23
  old_data: jsonb("old_data"),
14
24
  new_data: jsonb("new_data"),
15
25
  created_at: timestamp("created_at", { withTimezone: true })
@@ -1,6 +1,7 @@
1
1
  export { pgAuditLogTable } from "./audit-log-schema.js";
2
2
  export type { PgAuditLogTableOptions } from "./audit-log-schema.js";
3
- export { createAttachAuditTriggerSql, createAttachAuditTriggersSql, createAuditAddWorkspaceColumnSql, createAuditInstallSql, } from "./sql.js";
3
+ export { createAttachAuditTriggerSql, createAttachAuditTriggersSql, createAuditAddContextColumnsSql, createAuditInstallSql, } from "./sql.js";
4
4
  export { setAuditContext, withAuditedTransaction } from "./runtime.js";
5
- export type { AuditInstallOptions, AuditSqlExecutor, AuditTransactionCapable, AuditTriggerTarget, } from "./types.js";
5
+ export type { AuditContextOptions } from "./runtime.js";
6
+ export type { AuditContextColumn, AuditInstallOptions, AuditSqlExecutor, AuditTransactionCapable, AuditTriggerTarget, } from "./types.js";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAEtE,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,+BAA+B,EAC/B,qBAAqB,GACtB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvD,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
@@ -1,3 +1,3 @@
1
1
  export { pgAuditLogTable } from "./audit-log-schema.js";
2
- export { createAttachAuditTriggerSql, createAttachAuditTriggersSql, createAuditAddWorkspaceColumnSql, createAuditInstallSql, } from "./sql.js";
2
+ export { createAttachAuditTriggerSql, createAttachAuditTriggersSql, createAuditAddContextColumnsSql, createAuditInstallSql, } from "./sql.js";
3
3
  export { setAuditContext, withAuditedTransaction } from "./runtime.js";
@@ -1,10 +1,8 @@
1
1
  import type { AuditSqlExecutor, AuditTransactionCapable } from "./types.js";
2
- export declare function setAuditContext(db: AuditSqlExecutor, actorId: string, contextKey?: string, options?: {
3
- workspaceId?: string;
4
- workspaceContextKey?: string;
5
- }): Promise<void>;
6
- export declare function withAuditedTransaction<TTransaction extends AuditSqlExecutor, TResult>(db: AuditTransactionCapable<TTransaction>, actorId: string, callback: (tx: TTransaction) => Promise<TResult> | TResult, contextKey?: string, options?: {
7
- workspaceId?: string;
8
- workspaceContextKey?: string;
9
- }): Promise<TResult>;
2
+ export type AuditContextOptions = {
3
+ /** Map of session GUC name → value to set for the transaction. */
4
+ context?: Record<string, string>;
5
+ };
6
+ export declare function setAuditContext(db: AuditSqlExecutor, actorId: string, contextKey?: string, options?: AuditContextOptions): Promise<void>;
7
+ export declare function withAuditedTransaction<TTransaction extends AuditSqlExecutor, TResult>(db: AuditTransactionCapable<TTransaction>, actorId: string, callback: (tx: TTransaction) => Promise<TResult> | TResult, contextKey?: string, options?: AuditContextOptions): Promise<TResult>;
10
8
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/postgres/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,YAAY,CAAA;AAQnB,wBAAsB,eAAe,CACnC,EAAE,EAAE,gBAAgB,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,SAAgB,EAC1B,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,iBAajE;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,SAAS,gBAAgB,EACrC,OAAO,EAEP,EAAE,EAAE,uBAAuB,CAAC,YAAY,CAAC,EACzC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,EAC1D,UAAU,SAAgB,EAC1B,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,oBAQjE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/postgres/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,YAAY,CAAA;AAwBnB,MAAM,MAAM,mBAAmB,GAAG;IAChC,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC,CAAA;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,gBAAgB,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,SAAgB,EAC1B,OAAO,CAAC,EAAE,mBAAmB,iBAa9B;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,SAAS,gBAAgB,EACrC,OAAO,EAEP,EAAE,EAAE,uBAAuB,CAAC,YAAY,CAAC,EACzC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,EAC1D,UAAU,SAAgB,EAC1B,OAAO,CAAC,EAAE,mBAAmB,oBAQ9B"}
@@ -4,12 +4,24 @@ function assertActorId(actorId) {
4
4
  throw new Error("actorId must not be empty");
5
5
  }
6
6
  }
7
+ /**
8
+ * Builds the `context` GUC map from a set of options. Empty/undefined values
9
+ * are dropped so the trigger's NULLIF yields NULL.
10
+ */
11
+ function resolveContext(options) {
12
+ const context = {};
13
+ for (const [key, value] of Object.entries(options?.context ?? {})) {
14
+ if (value !== undefined && value !== "") {
15
+ context[key] = value;
16
+ }
17
+ }
18
+ return context;
19
+ }
7
20
  export async function setAuditContext(db, actorId, contextKey = "app.user_id", options) {
8
21
  assertActorId(actorId);
9
22
  await db.execute(sql `select set_config(${contextKey}, ${actorId}, true) as audit_context`);
10
- if (options?.workspaceId !== undefined && options.workspaceId !== "") {
11
- const wsKey = options.workspaceContextKey ?? "app.workspace_id";
12
- await db.execute(sql `select set_config(${wsKey}, ${options.workspaceId}, true) as workspace_context`);
23
+ for (const [key, value] of Object.entries(resolveContext(options))) {
24
+ await db.execute(sql `select set_config(${key}, ${value}, true) as audit_context`);
13
25
  }
14
26
  }
15
27
  export async function withAuditedTransaction(db, actorId, callback, contextKey = "app.user_id", options) {
@@ -3,12 +3,15 @@ export declare function createAuditInstallSql(options?: AuditInstallOptions): st
3
3
  export declare function createAttachAuditTriggerSql(target: AuditTriggerTarget, options?: AuditInstallOptions): string;
4
4
  export declare function createAttachAuditTriggersSql(targets: AuditTriggerTarget[], options?: AuditInstallOptions): string;
5
5
  /**
6
- * Generates SQL to add the workspace column and replace the trigger function on an
7
- * existing audit_logs table. Use in a new migration when adding workspace_id after
8
- * the initial install. Options must match your install (auditSchema, auditTable,
9
- * triggerFunctionName, contextKey, workspaceIdColumn).
6
+ * Generates SQL to add context columns and regenerate the trigger function on an
7
+ * existing audit_logs table. Use in a new migration when adding context columns
8
+ * after the initial install.
9
+ *
10
+ * Pass the FULL set of context columns the audit table should have (the trigger is
11
+ * a single CREATE OR REPLACE, so it must reference every column). Columns are added
12
+ * with `ADD COLUMN IF NOT EXISTS`, so already-present columns are left untouched.
13
+ * Options must match your install (auditSchema, auditTable, triggerFunctionName,
14
+ * contextKey).
10
15
  */
11
- export declare function createAuditAddWorkspaceColumnSql(options: AuditInstallOptions & {
12
- workspaceIdColumn: string;
13
- }): string;
16
+ export declare function createAuditAddContextColumnsSql(options?: AuditInstallOptions): string;
14
17
  //# sourceMappingURL=sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../../src/postgres/sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAqHzE,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,mBAAwB,UA6DtE;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,mBAAwB,UAiClC;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,GAAE,mBAAwB,UASlC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,mBAAmB,GAAG;IAAE,iBAAiB,EAAE,MAAM,CAAA;CAAE,UAkC7D"}
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../../src/postgres/sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAkJnB,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,mBAAwB,UA8DtE;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,mBAAwB,UAiClC;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,GAAE,mBAAwB,UASlC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,mBAAwB,UA2ChF"}