@willyim/drizzle-audit 0.1.3

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 (56) hide show
  1. package/README.md +278 -0
  2. package/dist/src/cli/generate-migration.d.ts +14 -0
  3. package/dist/src/cli/generate-migration.d.ts.map +1 -0
  4. package/dist/src/cli/generate-migration.js +118 -0
  5. package/dist/src/cli/runner.d.ts +2 -0
  6. package/dist/src/cli/runner.d.ts.map +1 -0
  7. package/dist/src/cli/runner.js +23 -0
  8. package/dist/src/d1/audit-log-schema.d.ts +181 -0
  9. package/dist/src/d1/audit-log-schema.d.ts.map +1 -0
  10. package/dist/src/d1/audit-log-schema.js +30 -0
  11. package/dist/src/d1/index.d.ts +7 -0
  12. package/dist/src/d1/index.d.ts.map +1 -0
  13. package/dist/src/d1/index.js +3 -0
  14. package/dist/src/d1/runtime.d.ts +44 -0
  15. package/dist/src/d1/runtime.d.ts.map +1 -0
  16. package/dist/src/d1/runtime.js +68 -0
  17. package/dist/src/d1/sql.d.ts +37 -0
  18. package/dist/src/d1/sql.d.ts.map +1 -0
  19. package/dist/src/d1/sql.js +261 -0
  20. package/dist/src/d1/types.d.ts +22 -0
  21. package/dist/src/d1/types.d.ts.map +1 -0
  22. package/dist/src/d1/types.js +1 -0
  23. package/dist/src/d1-runtime/index.d.ts +3 -0
  24. package/dist/src/d1-runtime/index.d.ts.map +1 -0
  25. package/dist/src/d1-runtime/index.js +1 -0
  26. package/dist/src/d1-runtime/with-audit.d.ts +77 -0
  27. package/dist/src/d1-runtime/with-audit.d.ts.map +1 -0
  28. package/dist/src/d1-runtime/with-audit.js +130 -0
  29. package/dist/src/index.d.ts +4 -0
  30. package/dist/src/index.d.ts.map +1 -0
  31. package/dist/src/index.js +3 -0
  32. package/dist/src/postgres/audit-log-schema.d.ts +140 -0
  33. package/dist/src/postgres/audit-log-schema.d.ts.map +1 -0
  34. package/dist/src/postgres/audit-log-schema.js +25 -0
  35. package/dist/src/postgres/index.d.ts +6 -0
  36. package/dist/src/postgres/index.d.ts.map +1 -0
  37. package/dist/src/postgres/index.js +3 -0
  38. package/dist/src/postgres/runtime.d.ts +10 -0
  39. package/dist/src/postgres/runtime.d.ts.map +1 -0
  40. package/dist/src/postgres/runtime.js +21 -0
  41. package/dist/src/postgres/sql.d.ts +14 -0
  42. package/dist/src/postgres/sql.d.ts.map +1 -0
  43. package/dist/src/postgres/sql.js +190 -0
  44. package/dist/src/postgres/types.d.ts +22 -0
  45. package/dist/src/postgres/types.d.ts.map +1 -0
  46. package/dist/src/postgres/types.js +1 -0
  47. package/dist/test/d1-runtime.integration.test.d.ts +2 -0
  48. package/dist/test/d1-runtime.integration.test.d.ts.map +1 -0
  49. package/dist/test/d1-runtime.integration.test.js +222 -0
  50. package/dist/test/d1.integration.test.d.ts +2 -0
  51. package/dist/test/d1.integration.test.d.ts.map +1 -0
  52. package/dist/test/d1.integration.test.js +223 -0
  53. package/dist/test/postgres.integration.test.d.ts +2 -0
  54. package/dist/test/postgres.integration.test.d.ts.map +1 -0
  55. package/dist/test/postgres.integration.test.js +286 -0
  56. package/package.json +66 -0
@@ -0,0 +1,222 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import Database from "better-sqlite3";
4
+ import { asc, eq, isNull } from "drizzle-orm";
5
+ import { drizzle } from "drizzle-orm/better-sqlite3";
6
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
7
+ import { d1AuditLogTable } from "../src/d1/index.js";
8
+ import { withAudit } from "../src/d1-runtime/index.js";
9
+ const users = sqliteTable("users", {
10
+ id: text("id").primaryKey(),
11
+ name: text("name").notNull(),
12
+ email: text("email"),
13
+ });
14
+ const invoices = sqliteTable("invoices", {
15
+ invoice_id: text("invoice_id").primaryKey(),
16
+ amount: integer("amount").notNull(),
17
+ status: text("status").notNull().default("pending"),
18
+ });
19
+ const auditLogs = d1AuditLogTable();
20
+ function setupDb() {
21
+ const sqlite = new Database(":memory:");
22
+ const db = drizzle({ client: sqlite, schema: { auditLogs, users, invoices } });
23
+ sqlite.exec(`
24
+ CREATE TABLE audit_logs (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ table_name TEXT NOT NULL,
27
+ operation TEXT NOT NULL,
28
+ row_id TEXT,
29
+ user_id TEXT,
30
+ old_data TEXT,
31
+ new_data TEXT,
32
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
33
+ );
34
+ CREATE TABLE users (
35
+ id TEXT PRIMARY KEY,
36
+ name TEXT NOT NULL,
37
+ email TEXT
38
+ );
39
+ CREATE TABLE invoices (
40
+ invoice_id TEXT PRIMARY KEY,
41
+ amount INTEGER NOT NULL,
42
+ status TEXT NOT NULL DEFAULT 'pending'
43
+ );
44
+ `);
45
+ return { db, sqlite };
46
+ }
47
+ test("withAudit insert logs audit row with new_data", () => {
48
+ const { db, sqlite } = setupDb();
49
+ try {
50
+ const audited = withAudit(db, auditLogs, { userId: "user_1" });
51
+ const row = audited.insert(users, { id: "u1", name: "Ada", email: "ada@example.com" });
52
+ assert.equal(row.id, "u1");
53
+ assert.equal(row.name, "Ada");
54
+ const logs = db.select().from(auditLogs).all();
55
+ assert.equal(logs.length, 1);
56
+ assert.equal(logs[0]?.table_name, "users");
57
+ assert.equal(logs[0]?.operation, "INSERT");
58
+ assert.equal(logs[0]?.row_id, "u1");
59
+ assert.equal(logs[0]?.user_id, "user_1");
60
+ assert.equal(logs[0]?.old_data, null);
61
+ assert.deepEqual(JSON.parse(logs[0]?.new_data), {
62
+ id: "u1",
63
+ name: "Ada",
64
+ email: "ada@example.com",
65
+ });
66
+ }
67
+ finally {
68
+ sqlite.close();
69
+ }
70
+ });
71
+ test("withAudit update captures old and new data", () => {
72
+ const { db, sqlite } = setupDb();
73
+ try {
74
+ // Seed a row
75
+ db.insert(users).values({ id: "u1", name: "Ada", email: "ada@example.com" }).run();
76
+ const audited = withAudit(db, auditLogs, { userId: "user_2" });
77
+ const rows = audited.update(users, eq(users.id, "u1"), { name: "Ada Lovelace" });
78
+ assert.equal(rows.length, 1);
79
+ assert.equal(rows[0]?.name, "Ada Lovelace");
80
+ const logs = db.select().from(auditLogs).all();
81
+ assert.equal(logs.length, 1);
82
+ assert.equal(logs[0]?.operation, "UPDATE");
83
+ assert.equal(logs[0]?.row_id, "u1");
84
+ assert.equal(logs[0]?.user_id, "user_2");
85
+ const oldData = JSON.parse(logs[0]?.old_data);
86
+ assert.equal(oldData.name, "Ada");
87
+ assert.equal(oldData.email, "ada@example.com");
88
+ const newData = JSON.parse(logs[0]?.new_data);
89
+ assert.equal(newData.name, "Ada Lovelace");
90
+ assert.equal(newData.email, "ada@example.com");
91
+ }
92
+ finally {
93
+ sqlite.close();
94
+ }
95
+ });
96
+ test("withAudit delete captures old data", () => {
97
+ const { db, sqlite } = setupDb();
98
+ try {
99
+ db.insert(users).values({ id: "u1", name: "Ada" }).run();
100
+ const audited = withAudit(db, auditLogs, { userId: "user_3" });
101
+ const deleted = audited.delete(users, eq(users.id, "u1"));
102
+ assert.equal(deleted.length, 1);
103
+ assert.equal(deleted[0]?.id, "u1");
104
+ // Verify row is gone
105
+ const remaining = db.select().from(users).all();
106
+ assert.equal(remaining.length, 0);
107
+ // Verify audit log
108
+ const logs = db.select().from(auditLogs).all();
109
+ assert.equal(logs.length, 1);
110
+ assert.equal(logs[0]?.operation, "DELETE");
111
+ assert.equal(logs[0]?.row_id, "u1");
112
+ assert.equal(logs[0]?.user_id, "user_3");
113
+ assert.deepEqual(JSON.parse(logs[0]?.old_data), {
114
+ id: "u1",
115
+ name: "Ada",
116
+ email: null,
117
+ });
118
+ assert.equal(logs[0]?.new_data, null);
119
+ }
120
+ finally {
121
+ sqlite.close();
122
+ }
123
+ });
124
+ test("withAudit works with custom primary key column", () => {
125
+ const { db, sqlite } = setupDb();
126
+ try {
127
+ const audited = withAudit(db, auditLogs, { userId: "user_1" });
128
+ audited.insert(invoices, { invoice_id: "inv_1", amount: 100 });
129
+ audited.update(invoices, eq(invoices.invoice_id, "inv_1"), { amount: 200 });
130
+ audited.delete(invoices, eq(invoices.invoice_id, "inv_1"));
131
+ const logs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
132
+ assert.equal(logs.length, 3);
133
+ assert.equal(logs[0]?.table_name, "invoices");
134
+ assert.equal(logs[0]?.row_id, "inv_1");
135
+ assert.equal(logs[1]?.operation, "UPDATE");
136
+ assert.equal(logs[1]?.row_id, "inv_1");
137
+ assert.equal(JSON.parse(logs[1]?.old_data).amount, 100);
138
+ assert.equal(JSON.parse(logs[1]?.new_data).amount, 200);
139
+ assert.equal(logs[2]?.operation, "DELETE");
140
+ assert.equal(logs[2]?.row_id, "inv_1");
141
+ }
142
+ finally {
143
+ sqlite.close();
144
+ }
145
+ });
146
+ test("withAudit handles multi-row update", () => {
147
+ const { db, sqlite } = setupDb();
148
+ try {
149
+ db.insert(users).values([
150
+ { id: "u1", name: "Ada" },
151
+ { id: "u2", name: "Bob" },
152
+ { id: "u3", name: "Carol" },
153
+ ]).run();
154
+ const audited = withAudit(db, auditLogs, { userId: "admin" });
155
+ // Update all users (no where = all rows, but let's use a broader condition)
156
+ const rows = audited.update(users, isNull(users.email), { email: "bulk@example.com" });
157
+ assert.equal(rows.length, 3);
158
+ const logs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
159
+ assert.equal(logs.length, 3);
160
+ for (const log of logs) {
161
+ assert.equal(log.operation, "UPDATE");
162
+ assert.equal(log.user_id, "admin");
163
+ const newData = JSON.parse(log.new_data);
164
+ assert.equal(newData.email, "bulk@example.com");
165
+ }
166
+ }
167
+ finally {
168
+ sqlite.close();
169
+ }
170
+ });
171
+ test("withAudit with workspace_id", () => {
172
+ const sqlite = new Database(":memory:");
173
+ const auditLogsWithWs = d1AuditLogTable({ workspaceIdColumn: "workspace_id" });
174
+ const db = drizzle({ client: sqlite, schema: { auditLogs: auditLogsWithWs, users } });
175
+ try {
176
+ sqlite.exec(`
177
+ CREATE TABLE audit_logs (
178
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
179
+ table_name TEXT NOT NULL,
180
+ operation TEXT NOT NULL,
181
+ row_id TEXT,
182
+ user_id TEXT,
183
+ workspace_id TEXT,
184
+ old_data TEXT,
185
+ new_data TEXT,
186
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
187
+ );
188
+ CREATE TABLE users (
189
+ id TEXT PRIMARY KEY,
190
+ name TEXT NOT NULL,
191
+ email TEXT
192
+ );
193
+ `);
194
+ const audited = withAudit(db, auditLogsWithWs, {
195
+ userId: "user_1",
196
+ workspaceId: "ws_1",
197
+ });
198
+ audited.insert(users, { id: "u1", name: "Ada" });
199
+ const logs = db.select().from(auditLogsWithWs).all();
200
+ assert.equal(logs.length, 1);
201
+ assert.equal(logs[0]?.user_id, "user_1");
202
+ assert.equal(logs[0].workspace_id, "ws_1");
203
+ }
204
+ finally {
205
+ sqlite.close();
206
+ }
207
+ });
208
+ test("withAudit.db gives access to raw db for non-audited ops", () => {
209
+ const { db, sqlite } = setupDb();
210
+ try {
211
+ const audited = withAudit(db, auditLogs, { userId: "user_1" });
212
+ // Direct insert — no audit
213
+ audited.db.insert(users).values({ id: "u1", name: "Ada" }).run();
214
+ const logs = db.select().from(auditLogs).all();
215
+ assert.equal(logs.length, 0);
216
+ const rows = db.select().from(users).all();
217
+ assert.equal(rows.length, 1);
218
+ }
219
+ finally {
220
+ sqlite.close();
221
+ }
222
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=d1.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1.integration.test.d.ts","sourceRoot":"","sources":["../../test/d1.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,223 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import Database from "better-sqlite3";
4
+ import { asc, eq, sql } from "drizzle-orm";
5
+ import { drizzle } from "drizzle-orm/better-sqlite3";
6
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
7
+ import { createAttachD1AuditTriggersSql, createAttachD1AuditTriggersSqlWithColumns, createD1AuditInstallSql, d1AuditLogTable, d1AuditContextTable, withD1AuditedTransaction, } from "../src/d1/index.js";
8
+ const users = sqliteTable("users", {
9
+ id: text("id").primaryKey(),
10
+ name: text("name").notNull(),
11
+ });
12
+ const invoices = sqliteTable("invoices", {
13
+ invoice_id: text("invoice_id").primaryKey(),
14
+ amount: integer("amount").notNull(),
15
+ });
16
+ const auditLogs = d1AuditLogTable();
17
+ const auditContext = d1AuditContextTable();
18
+ function setupDb() {
19
+ const sqlite = new Database(":memory:");
20
+ const db = drizzle({ client: sqlite, schema: { auditLogs, auditContext, users, invoices } });
21
+ sqlite.exec(createD1AuditInstallSql());
22
+ sqlite.exec(`
23
+ CREATE TABLE users (
24
+ id TEXT PRIMARY KEY,
25
+ name TEXT NOT NULL
26
+ );
27
+ CREATE TABLE invoices (
28
+ invoice_id TEXT PRIMARY KEY,
29
+ amount INTEGER NOT NULL
30
+ );
31
+ `);
32
+ sqlite.exec(createAttachD1AuditTriggersSql([
33
+ { table: "users" },
34
+ { table: "invoices", rowIdColumn: "invoice_id" },
35
+ ]));
36
+ return { db, sqlite };
37
+ }
38
+ test("d1 auditing works end to end (without row data)", () => {
39
+ const { db, sqlite } = setupDb();
40
+ try {
41
+ // Insert without audit context: user_id = NULL
42
+ db.insert(users).values({ id: "user_0", name: "No Context" }).run();
43
+ const noContextLogs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
44
+ assert.equal(noContextLogs.length, 1);
45
+ assert.equal(noContextLogs[0]?.user_id, null);
46
+ assert.equal(noContextLogs[0]?.table_name, "users");
47
+ assert.equal(noContextLogs[0]?.operation, "INSERT");
48
+ assert.equal(noContextLogs[0]?.row_id, "user_0");
49
+ // With audit context
50
+ withD1AuditedTransaction(db, "user_123", (tx) => {
51
+ tx.insert(users).values({ id: "user_1", name: "Ada" }).run();
52
+ tx.update(users).set({ name: "Ada Lovelace" }).where(eq(users.id, "user_1")).run();
53
+ tx.insert(invoices).values({ invoice_id: "inv_1", amount: 42 }).run();
54
+ tx.delete(users).where(eq(users.id, "user_1")).run();
55
+ });
56
+ const logs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
57
+ assert.equal(logs.length, 5);
58
+ // INSERT user
59
+ assert.equal(logs[1]?.table_name, "users");
60
+ assert.equal(logs[1]?.operation, "INSERT");
61
+ assert.equal(logs[1]?.row_id, "user_1");
62
+ assert.equal(logs[1]?.user_id, "user_123");
63
+ // UPDATE user
64
+ assert.equal(logs[2]?.table_name, "users");
65
+ assert.equal(logs[2]?.operation, "UPDATE");
66
+ assert.equal(logs[2]?.row_id, "user_1");
67
+ assert.equal(logs[2]?.user_id, "user_123");
68
+ // INSERT invoice (custom rowIdColumn)
69
+ assert.equal(logs[3]?.table_name, "invoices");
70
+ assert.equal(logs[3]?.operation, "INSERT");
71
+ assert.equal(logs[3]?.row_id, "inv_1");
72
+ // DELETE user
73
+ assert.equal(logs[4]?.table_name, "users");
74
+ assert.equal(logs[4]?.operation, "DELETE");
75
+ assert.equal(logs[4]?.row_id, "user_1");
76
+ assert.equal(logs[4]?.user_id, "user_123");
77
+ // Context table should be clean after transaction
78
+ const contextRows = db.select().from(auditContext).all();
79
+ assert.equal(contextRows.length, 0);
80
+ }
81
+ finally {
82
+ sqlite.close();
83
+ }
84
+ });
85
+ test("d1 column-aware triggers capture full row data", () => {
86
+ const sqlite = new Database(":memory:");
87
+ const db = drizzle({ client: sqlite, schema: { auditLogs, auditContext, users } });
88
+ try {
89
+ sqlite.exec(createD1AuditInstallSql());
90
+ sqlite.exec(`
91
+ CREATE TABLE users (
92
+ id TEXT PRIMARY KEY,
93
+ name TEXT NOT NULL
94
+ );
95
+ `);
96
+ sqlite.exec(createAttachD1AuditTriggersSqlWithColumns([
97
+ { table: "users", columns: ["id", "name"] },
98
+ ]));
99
+ withD1AuditedTransaction(db, "user_1", (tx) => {
100
+ tx.insert(users).values({ id: "u1", name: "Ada" }).run();
101
+ tx.update(users).set({ name: "Ada Lovelace" }).where(eq(users.id, "u1")).run();
102
+ tx.delete(users).where(eq(users.id, "u1")).run();
103
+ });
104
+ const logs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
105
+ assert.equal(logs.length, 3);
106
+ // INSERT: new_data captured
107
+ assert.equal(logs[0]?.operation, "INSERT");
108
+ assert.deepEqual(JSON.parse(logs[0]?.new_data), { id: "u1", name: "Ada" });
109
+ assert.equal(logs[0]?.old_data, null);
110
+ // UPDATE: old_data and new_data captured
111
+ assert.equal(logs[1]?.operation, "UPDATE");
112
+ assert.deepEqual(JSON.parse(logs[1]?.old_data), { id: "u1", name: "Ada" });
113
+ assert.deepEqual(JSON.parse(logs[1]?.new_data), { id: "u1", name: "Ada Lovelace" });
114
+ // DELETE: old_data captured
115
+ assert.equal(logs[2]?.operation, "DELETE");
116
+ assert.deepEqual(JSON.parse(logs[2]?.old_data), { id: "u1", name: "Ada Lovelace" });
117
+ assert.equal(logs[2]?.new_data, null);
118
+ }
119
+ finally {
120
+ sqlite.close();
121
+ }
122
+ });
123
+ test("d1 workspace_id column and context are stored when enabled", () => {
124
+ const sqlite = new Database(":memory:");
125
+ const auditLogsWithWorkspace = d1AuditLogTable({ workspaceIdColumn: "workspace_id" });
126
+ const db = drizzle({ client: sqlite, schema: { auditLogs: auditLogsWithWorkspace, auditContext, users } });
127
+ try {
128
+ sqlite.exec(createD1AuditInstallSql({ workspaceIdColumn: "workspace_id" }));
129
+ sqlite.exec(`
130
+ CREATE TABLE users (
131
+ id TEXT PRIMARY KEY,
132
+ name TEXT NOT NULL
133
+ );
134
+ `);
135
+ sqlite.exec(createAttachD1AuditTriggersSql([{ table: "users" }], { workspaceIdColumn: "workspace_id" }));
136
+ withD1AuditedTransaction(db, "user_1", (tx) => {
137
+ tx.insert(users).values({ id: "u1", name: "Alice" }).run();
138
+ }, { workspaceId: "ws_1" });
139
+ const logs = db.select().from(auditLogsWithWorkspace).all();
140
+ assert.equal(logs.length, 1);
141
+ assert.equal(logs[0]?.user_id, "user_1");
142
+ assert.equal(logs[0].workspace_id, "ws_1");
143
+ // Without workspace
144
+ withD1AuditedTransaction(db, "user_2", (tx) => {
145
+ tx.insert(users).values({ id: "u2", name: "Bob" }).run();
146
+ });
147
+ const logs2 = db
148
+ .select()
149
+ .from(auditLogsWithWorkspace)
150
+ .orderBy(asc(auditLogsWithWorkspace.id))
151
+ .all();
152
+ assert.equal(logs2.length, 2);
153
+ assert.equal(logs2[1]?.user_id, "user_2");
154
+ assert.equal(logs2[1].workspace_id, null);
155
+ }
156
+ finally {
157
+ sqlite.close();
158
+ }
159
+ });
160
+ test("d1 writes without audit context produce rows with user_id = NULL", () => {
161
+ const { db, sqlite } = setupDb();
162
+ try {
163
+ db.insert(users).values({ id: "u1", name: "Alice" }).run();
164
+ db.update(users).set({ name: "Alice Updated" }).where(eq(users.id, "u1")).run();
165
+ db.delete(users).where(eq(users.id, "u1")).run();
166
+ const logs = db.select().from(auditLogs).orderBy(asc(auditLogs.id)).all();
167
+ assert.equal(logs.length, 3);
168
+ assert.equal(logs[0]?.operation, "INSERT");
169
+ assert.equal(logs[0]?.user_id, null);
170
+ assert.equal(logs[0]?.row_id, "u1");
171
+ assert.equal(logs[1]?.operation, "UPDATE");
172
+ assert.equal(logs[1]?.user_id, null);
173
+ assert.equal(logs[2]?.operation, "DELETE");
174
+ assert.equal(logs[2]?.user_id, null);
175
+ }
176
+ finally {
177
+ sqlite.close();
178
+ }
179
+ });
180
+ test("d1 trigger SQL handles table names with special characters", () => {
181
+ const sqlite = new Database(":memory:");
182
+ const db = drizzle({ client: sqlite, schema: { auditLogs, auditContext } });
183
+ try {
184
+ sqlite.exec(createD1AuditInstallSql());
185
+ // Table name with a single quote — the quoteLiteral fix prevents SQL breakage
186
+ const tableName = "user's_data";
187
+ sqlite.exec(`CREATE TABLE "${tableName}" (id TEXT PRIMARY KEY, val TEXT);`);
188
+ sqlite.exec(createAttachD1AuditTriggersSql([{ table: tableName }]));
189
+ // Seed context and insert
190
+ withD1AuditedTransaction(db, "tester", (tx) => {
191
+ tx.run(sql `INSERT INTO "${sql.raw(tableName)}" (id, val) VALUES ('r1', 'hello')`);
192
+ });
193
+ const logs = db.select().from(auditLogs).all();
194
+ assert.equal(logs.length, 1);
195
+ assert.equal(logs[0]?.table_name, tableName);
196
+ assert.equal(logs[0]?.row_id, "r1");
197
+ assert.equal(logs[0]?.user_id, "tester");
198
+ }
199
+ finally {
200
+ sqlite.close();
201
+ }
202
+ });
203
+ test("d1 column-aware triggers handle column names with special characters", () => {
204
+ const sqlite = new Database(":memory:");
205
+ const db = drizzle({ client: sqlite, schema: { auditLogs, auditContext } });
206
+ try {
207
+ sqlite.exec(createD1AuditInstallSql());
208
+ sqlite.exec(`CREATE TABLE items (id TEXT PRIMARY KEY, "user's name" TEXT);`);
209
+ sqlite.exec(createAttachD1AuditTriggersSqlWithColumns([
210
+ { table: "items", columns: ["id", "user's name"] },
211
+ ]));
212
+ withD1AuditedTransaction(db, "tester", (tx) => {
213
+ tx.run(sql `INSERT INTO items (id, "user's name") VALUES ('i1', 'Ada')`);
214
+ });
215
+ const logs = db.select().from(auditLogs).all();
216
+ assert.equal(logs.length, 1);
217
+ const newData = JSON.parse(logs[0]?.new_data);
218
+ assert.equal(newData["user's name"], "Ada");
219
+ }
220
+ finally {
221
+ sqlite.close();
222
+ }
223
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=postgres.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.integration.test.d.ts","sourceRoot":"","sources":["../../test/postgres.integration.test.ts"],"names":[],"mappings":""}