better-auth-studio 1.1.2-beta.2 → 1.1.2-beta.20

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 (108) hide show
  1. package/README.md +48 -78
  2. package/data/default-geo.json +1 -1
  3. package/dist/adapters/astro.d.ts +1 -1
  4. package/dist/adapters/astro.js +13 -13
  5. package/dist/adapters/elysia.d.ts +2 -2
  6. package/dist/adapters/elysia.js +13 -13
  7. package/dist/adapters/express.d.ts +2 -2
  8. package/dist/adapters/express.js +4 -4
  9. package/dist/adapters/hono.d.ts +2 -2
  10. package/dist/adapters/hono.js +12 -12
  11. package/dist/adapters/nextjs.d.ts +1 -1
  12. package/dist/adapters/nextjs.js +10 -10
  13. package/dist/adapters/nuxt.d.ts +1 -1
  14. package/dist/adapters/nuxt.js +22 -22
  15. package/dist/adapters/remix.d.ts +1 -1
  16. package/dist/adapters/remix.js +13 -13
  17. package/dist/adapters/solid-start.d.ts +1 -1
  18. package/dist/adapters/solid-start.js +13 -13
  19. package/dist/adapters/svelte-kit.d.ts +2 -2
  20. package/dist/adapters/svelte-kit.js +13 -13
  21. package/dist/adapters/tanstack-start.d.ts +1 -1
  22. package/dist/adapters/tanstack-start.js +13 -13
  23. package/dist/add-svelte-kit-env-modules.js +11 -11
  24. package/dist/auth-adapter.d.ts +1 -1
  25. package/dist/auth-adapter.js +96 -96
  26. package/dist/cli/commands/init.js +57 -57
  27. package/dist/cli.js +75 -75
  28. package/dist/config.d.ts +5 -5
  29. package/dist/config.js +37 -37
  30. package/dist/core/handler.d.ts +1 -1
  31. package/dist/core/handler.js +116 -115
  32. package/dist/core/handler.js.map +1 -1
  33. package/dist/data.d.ts +2 -2
  34. package/dist/data.js +60 -60
  35. package/dist/geo-service.js +78 -78
  36. package/dist/get-tsconfig-info.js +4 -4
  37. package/dist/index.d.ts +8 -8
  38. package/dist/index.js +6 -6
  39. package/dist/providers/events/helpers.d.ts +2 -2
  40. package/dist/providers/events/helpers.d.ts.map +1 -1
  41. package/dist/providers/events/helpers.js +178 -154
  42. package/dist/providers/events/helpers.js.map +1 -1
  43. package/dist/public/assets/{main-BDJUrMKx.js → main-BeCk6LUx.js} +133 -133
  44. package/dist/public/assets/main-w2bJSKlF.css +1 -0
  45. package/dist/public/index.html +2 -2
  46. package/dist/routes/api-router.d.ts +3 -1
  47. package/dist/routes/api-router.d.ts.map +1 -1
  48. package/dist/routes/api-router.js +3 -3
  49. package/dist/routes/api-router.js.map +1 -1
  50. package/dist/routes.d.ts +6 -4
  51. package/dist/routes.d.ts.map +1 -1
  52. package/dist/routes.js +1333 -1277
  53. package/dist/routes.js.map +1 -1
  54. package/dist/studio.d.ts +3 -3
  55. package/dist/studio.d.ts.map +1 -1
  56. package/dist/studio.js +64 -65
  57. package/dist/studio.js.map +1 -1
  58. package/dist/types/events.d.ts +8 -7
  59. package/dist/types/events.d.ts.map +1 -1
  60. package/dist/types/events.js +165 -165
  61. package/dist/types/events.js.map +1 -1
  62. package/dist/types/handler.d.ts +14 -4
  63. package/dist/types/handler.d.ts.map +1 -1
  64. package/dist/types/handler.js.map +1 -1
  65. package/dist/utils/auth-callbacks-injector.d.ts +2 -2
  66. package/dist/utils/auth-callbacks-injector.js +27 -27
  67. package/dist/utils/auth-callbacks-wrapper.d.ts +3 -3
  68. package/dist/utils/auth-callbacks-wrapper.js +117 -107
  69. package/dist/utils/database-detection.d.ts +1 -1
  70. package/dist/utils/database-detection.js +44 -44
  71. package/dist/utils/database-hook-injector.d.ts +3 -3
  72. package/dist/utils/database-hook-injector.js +135 -131
  73. package/dist/utils/email-otp-hooks-injector.d.ts +28 -12
  74. package/dist/utils/email-otp-hooks-injector.js +104 -97
  75. package/dist/utils/event-ingestion.d.ts +5 -5
  76. package/dist/utils/event-ingestion.d.ts.map +1 -1
  77. package/dist/utils/event-ingestion.js +32 -12
  78. package/dist/utils/event-ingestion.js.map +1 -1
  79. package/dist/utils/hook-injector.d.ts +2 -2
  80. package/dist/utils/hook-injector.js +199 -199
  81. package/dist/utils/html-injector.d.ts +11 -2
  82. package/dist/utils/html-injector.d.ts.map +1 -1
  83. package/dist/utils/html-injector.js +40 -39
  84. package/dist/utils/html-injector.js.map +1 -1
  85. package/dist/utils/org-hooks-injector.d.ts +3 -3
  86. package/dist/utils/org-hooks-injector.js +63 -63
  87. package/dist/utils/org-hooks-wrapper.d.ts +41 -35
  88. package/dist/utils/org-hooks-wrapper.js +778 -658
  89. package/dist/utils/organization-hooks-wrapper.d.ts +23 -17
  90. package/dist/utils/organization-hooks-wrapper.js +325 -277
  91. package/dist/utils/package-json.js +11 -11
  92. package/dist/utils/paths.js +1 -1
  93. package/dist/utils/server-init.d.ts +1 -1
  94. package/dist/utils/server-init.js +25 -25
  95. package/dist/utils/session.d.ts +0 -1
  96. package/dist/utils/session.d.ts.map +1 -1
  97. package/dist/utils/session.js +19 -12
  98. package/dist/utils/session.js.map +1 -1
  99. package/dist/utils.js +24 -24
  100. package/package.json +25 -24
  101. package/public/assets/{main-BDJUrMKx.js → main-BeCk6LUx.js} +133 -133
  102. package/public/assets/main-w2bJSKlF.css +1 -0
  103. package/public/index.html +2 -2
  104. package/scripts/download-geolite2.js +8 -8
  105. package/scripts/generate-default-db.js +324 -324
  106. package/scripts/postinstall.js +25 -25
  107. package/dist/public/assets/main-CBA9bZ-w.css +0 -1
  108. package/public/assets/main-CBA9bZ-w.css +0 -1
@@ -1,11 +1,11 @@
1
1
  export function createPostgresProvider(options) {
2
- const { client, tableName = 'auth_events', schema = 'public', clientType } = options;
2
+ const { client, tableName = "auth_events", schema = "public", clientType } = options;
3
3
  // Validate client is provided
4
4
  if (!client) {
5
- throw new Error('Postgres client is required. Provide a pg Pool, Client, or Prisma client instance.');
5
+ throw new Error("Postgres client is required. Provide a pg Pool, Client, or Prisma client instance.");
6
6
  }
7
7
  let actualClient = client;
8
- if (clientType === 'drizzle') {
8
+ if (clientType === "drizzle") {
9
9
  if (client.$client) {
10
10
  actualClient = client.$client;
11
11
  }
@@ -19,11 +19,11 @@ export function createPostgresProvider(options) {
19
19
  const hasExecute = client?.execute;
20
20
  const hasUnsafe = actualClient?.unsafe || client?.$client?.unsafe || client?.unsafe;
21
21
  if (!hasQuery && !hasExecuteRaw && !hasExecute && !hasUnsafe) {
22
- const errorMessage = clientType === 'prisma'
23
- ? 'Invalid Prisma client. Client must have a `$executeRaw` method.'
24
- : clientType === 'drizzle'
25
- ? 'Invalid Drizzle client. Drizzle instance must wrap a postgres-js (with `unsafe` method) or pg (with `query` method) client.'
26
- : 'Invalid Postgres client. Client must have either a `query` method (pg Pool/Client) or `$executeRaw` method (Prisma client).';
22
+ const errorMessage = clientType === "prisma"
23
+ ? "Invalid Prisma client. Client must have a `$executeRaw` method."
24
+ : clientType === "drizzle"
25
+ ? "Invalid Drizzle client. Drizzle instance must wrap a postgres-js (with `unsafe` method) or pg (with `query` method) client."
26
+ : "Invalid Postgres client. Client must have either a `query` method (pg Pool/Client) or `$executeRaw` method (Prisma client).";
27
27
  throw new Error(errorMessage);
28
28
  }
29
29
  // Ensure table exists
@@ -33,13 +33,13 @@ export function createPostgresProvider(options) {
33
33
  try {
34
34
  // Support Prisma client ($executeRaw), Drizzle instance ($client), or standard pg client (query)
35
35
  let executeQuery;
36
- if (clientType === 'prisma' || client.$executeRaw) {
36
+ if (clientType === "prisma" || client.$executeRaw) {
37
37
  // Prisma client
38
38
  executeQuery = async (query) => {
39
39
  return await client.$executeRawUnsafe(query);
40
40
  };
41
41
  }
42
- else if (clientType === 'drizzle') {
42
+ else if (clientType === "drizzle") {
43
43
  // Drizzle client - use $client which exposes the underlying client
44
44
  if (actualClient?.unsafe) {
45
45
  // postgres-js client via db.$client.unsafe()
@@ -54,7 +54,7 @@ export function createPostgresProvider(options) {
54
54
  };
55
55
  }
56
56
  else {
57
- throw new Error(`Drizzle client.$client doesn't expose 'unsafe' (postgres-js) or 'query' (pg) method. Available methods: ${Object.keys(actualClient || {}).join(', ')}`);
57
+ throw new Error(`Drizzle client.$client doesn't expose 'unsafe' (postgres-js) or 'query' (pg) method. Available methods: ${Object.keys(actualClient || {}).join(", ")}`);
58
58
  }
59
59
  }
60
60
  else if (actualClient?.query) {
@@ -69,7 +69,7 @@ export function createPostgresProvider(options) {
69
69
  };
70
70
  }
71
71
  else {
72
- throw new Error(`Postgres client doesn't support $executeRaw or query method. Available properties: ${Object.keys(client).join(', ')}`);
72
+ throw new Error(`Postgres client doesn't support $executeRaw or query method. Available properties: ${Object.keys(client).join(", ")}`);
73
73
  }
74
74
  // Use CREATE TABLE IF NOT EXISTS (simpler and more reliable)
75
75
  const createTableQuery = `
@@ -106,7 +106,7 @@ export function createPostgresProvider(options) {
106
106
  }
107
107
  }
108
108
  catch (error) {
109
- if (error?.message?.includes('already exists') || error?.code === '42P07') {
109
+ if (error?.message?.includes("already exists") || error?.code === "42P07") {
110
110
  return;
111
111
  }
112
112
  console.error(`Failed to ensure ${schema}.${tableName} table:`, error);
@@ -127,7 +127,7 @@ export function createPostgresProvider(options) {
127
127
  tableEnsured = true;
128
128
  }
129
129
  catch (error) {
130
- console.error('Failed to ensure table:', error);
130
+ console.error("Failed to ensure table:", error);
131
131
  tableEnsuringPromise = null;
132
132
  throw error;
133
133
  }
@@ -141,18 +141,18 @@ export function createPostgresProvider(options) {
141
141
  return {
142
142
  async ingest(event) {
143
143
  await ensureTableSync();
144
- if (clientType === 'prisma' || client.$executeRaw) {
144
+ if (clientType === "prisma" || client.$executeRaw) {
145
145
  const query = `
146
146
  INSERT INTO ${schema}.${tableName}
147
147
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
148
- VALUES ('${event.id}'::uuid, '${event.type}', '${event.timestamp.toISOString()}', '${event.status || 'success'}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : 'NULL'}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : 'NULL'}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : 'NULL'}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : 'NULL'}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : 'NULL'}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : 'NULL'}, ${event.display?.severity ? `'${event.display.severity}'` : 'NULL'})
148
+ VALUES ('${event.id}'::uuid, '${event.type}', '${event.timestamp.toISOString()}', '${event.status || "success"}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : "NULL"}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : "NULL"}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : "NULL"}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : "NULL"}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : "NULL"}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : "NULL"}, ${event.display?.severity ? `'${event.display.severity}'` : "NULL"})
149
149
  `;
150
150
  try {
151
151
  await client.$executeRawUnsafe(query);
152
152
  }
153
153
  catch (error) {
154
154
  console.error(`Failed to insert event (${event.type}) into ${schema}.${tableName}:`, error);
155
- if (error.code === '42P01' || error.meta?.code === '42P01' || error.code === 'P2010') {
155
+ if (error.code === "42P01" || error.meta?.code === "42P01" || error.code === "P2010") {
156
156
  tableEnsured = false;
157
157
  await ensureTableSync();
158
158
  try {
@@ -178,14 +178,14 @@ export function createPostgresProvider(options) {
178
178
  const query = `
179
179
  INSERT INTO ${schema}.${tableName}
180
180
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
181
- VALUES ('${event.id}'::uuid, '${event.type}', '${event.timestamp.toISOString()}', '${event.status || 'success'}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : 'NULL'}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : 'NULL'}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : 'NULL'}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : 'NULL'}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : 'NULL'}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : 'NULL'}, ${event.display?.severity ? `'${event.display.severity}'` : 'NULL'})
181
+ VALUES ('${event.id}'::uuid, '${event.type}', '${event.timestamp.toISOString()}', '${event.status || "success"}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : "NULL"}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : "NULL"}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : "NULL"}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : "NULL"}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : "NULL"}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : "NULL"}, ${event.display?.severity ? `'${event.display.severity}'` : "NULL"})
182
182
  `;
183
183
  try {
184
184
  await queryClient.unsafe(query);
185
185
  }
186
186
  catch (error) {
187
187
  console.error(`Failed to insert event (${event.type}) into ${schema}.${tableName}:`, error);
188
- if (error.code === '42P01') {
188
+ if (error.code === "42P01") {
189
189
  tableEnsured = false;
190
190
  await ensureTableSync();
191
191
  try {
@@ -209,7 +209,7 @@ export function createPostgresProvider(options) {
209
209
  event.id,
210
210
  event.type,
211
211
  event.timestamp,
212
- event.status || 'success',
212
+ event.status || "success",
213
213
  event.userId || null,
214
214
  event.sessionId || null,
215
215
  event.organizationId || null,
@@ -223,7 +223,7 @@ export function createPostgresProvider(options) {
223
223
  }
224
224
  catch (error) {
225
225
  console.error(`Failed to insert event (${event.type}) into ${schema}.${tableName}:`, error);
226
- if (error.code === '42P01') {
226
+ if (error.code === "42P01") {
227
227
  tableEnsured = false;
228
228
  await ensureTableSync();
229
229
  try {
@@ -233,7 +233,7 @@ export function createPostgresProvider(options) {
233
233
  event.id,
234
234
  event.type,
235
235
  event.timestamp,
236
- event.status || 'success',
236
+ event.status || "success",
237
237
  event.userId || null,
238
238
  event.sessionId || null,
239
239
  event.organizationId || null,
@@ -251,9 +251,9 @@ export function createPostgresProvider(options) {
251
251
  throw retryError;
252
252
  }
253
253
  }
254
- if (error.code === 'ECONNREFUSED' ||
255
- error.code === 'ETIMEDOUT' ||
256
- error.message?.includes('Connection terminated')) {
254
+ if (error.code === "ECONNREFUSED" ||
255
+ error.code === "ETIMEDOUT" ||
256
+ error.message?.includes("Connection terminated")) {
257
257
  if (client.end) {
258
258
  console.warn(`⚠️ Connection error with pg Pool. The pool will retry automatically on next query.`);
259
259
  }
@@ -263,7 +263,7 @@ export function createPostgresProvider(options) {
263
263
  }
264
264
  }
265
265
  else {
266
- throw new Error('Postgres client does not support $executeRaw, query, or unsafe method. Make sure you are passing a valid pg Pool, Client, Prisma client, or Drizzle instance.');
266
+ throw new Error("Postgres client does not support $executeRaw, query, or unsafe method. Make sure you are passing a valid pg Pool, Client, Prisma client, or Drizzle instance.");
267
267
  }
268
268
  },
269
269
  async ingestBatch(events) {
@@ -271,14 +271,14 @@ export function createPostgresProvider(options) {
271
271
  return;
272
272
  await ensureTableSync();
273
273
  // Support Prisma client ($executeRaw), Drizzle instance, or standard pg client (query)
274
- if (clientType === 'prisma' || client.$executeRaw) {
274
+ if (clientType === "prisma" || client.$executeRaw) {
275
275
  // Prisma client - use $executeRawUnsafe for batch inserts
276
276
  const CHUNK_SIZE = 500; // Reasonable chunk size for string-based queries
277
277
  for (let i = 0; i < events.length; i += CHUNK_SIZE) {
278
278
  const chunk = events.slice(i, i + CHUNK_SIZE);
279
279
  const values = chunk
280
- .map((event) => `('${event.id}', '${event.type}', '${event.timestamp.toISOString()}', '${event.status || 'success'}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : 'NULL'}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : 'NULL'}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : 'NULL'}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : 'NULL'}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : 'NULL'}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : 'NULL'}, ${event.display?.severity ? `'${event.display.severity}'` : 'NULL'})`)
281
- .join(', ');
280
+ .map((event) => `('${event.id}', '${event.type}', '${event.timestamp.toISOString()}', '${event.status || "success"}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : "NULL"}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : "NULL"}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : "NULL"}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : "NULL"}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : "NULL"}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : "NULL"}, ${event.display?.severity ? `'${event.display.severity}'` : "NULL"})`)
281
+ .join(", ");
282
282
  const query = `
283
283
  INSERT INTO ${schema}.${tableName}
284
284
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
@@ -305,8 +305,8 @@ export function createPostgresProvider(options) {
305
305
  for (let i = 0; i < events.length; i += CHUNK_SIZE) {
306
306
  const chunk = events.slice(i, i + CHUNK_SIZE);
307
307
  const values = chunk
308
- .map((event) => `('${event.id}', '${event.type}', '${event.timestamp.toISOString()}', '${event.status || 'success'}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : 'NULL'}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : 'NULL'}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : 'NULL'}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : 'NULL'}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : 'NULL'}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : 'NULL'}, ${event.display?.severity ? `'${event.display.severity}'` : 'NULL'})`)
309
- .join(', ');
308
+ .map((event) => `('${event.id}', '${event.type}', '${event.timestamp.toISOString()}', '${event.status || "success"}', ${event.userId ? `'${event.userId.replace(/'/g, "''")}'` : "NULL"}, ${event.sessionId ? `'${event.sessionId.replace(/'/g, "''")}'` : "NULL"}, ${event.organizationId ? `'${event.organizationId.replace(/'/g, "''")}'` : "NULL"}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress.replace(/'/g, "''")}'` : "NULL"}, ${event.userAgent ? `'${event.userAgent.replace(/'/g, "''")}'` : "NULL"}, '${event.source}', ${event.display?.message ? `'${event.display.message.replace(/'/g, "''")}'` : "NULL"}, ${event.display?.severity ? `'${event.display.severity}'` : "NULL"})`)
309
+ .join(", ");
310
310
  const query = `
311
311
  INSERT INTO ${schema}.${tableName}
312
312
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
@@ -333,7 +333,7 @@ export function createPostgresProvider(options) {
333
333
  const base = i * PARAMS_PER_EVENT;
334
334
  return `($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}, $${base + 6}, $${base + 7}, $${base + 8}, $${base + 9}, $${base + 10}, $${base + 11}, $${base + 12}, $${base + 13})`;
335
335
  })
336
- .join(', ');
336
+ .join(", ");
337
337
  const query = `
338
338
  INSERT INTO ${schema}.${tableName}
339
339
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
@@ -343,7 +343,7 @@ export function createPostgresProvider(options) {
343
343
  event.id,
344
344
  event.type,
345
345
  event.timestamp,
346
- event.status || 'success',
346
+ event.status || "success",
347
347
  event.userId || null,
348
348
  event.sessionId || null,
349
349
  event.organizationId || null,
@@ -359,9 +359,9 @@ export function createPostgresProvider(options) {
359
359
  }
360
360
  catch (error) {
361
361
  console.error(`Failed to insert batch chunk (${chunk.length} events) into ${schema}.${tableName}:`, error);
362
- if (error.code === 'ECONNREFUSED' ||
363
- error.code === 'ETIMEDOUT' ||
364
- error.message?.includes('Connection terminated')) {
362
+ if (error.code === "ECONNREFUSED" ||
363
+ error.code === "ETIMEDOUT" ||
364
+ error.message?.includes("Connection terminated")) {
365
365
  if (client.end) {
366
366
  console.warn(`⚠️ Connection error with pg Pool. The pool will retry automatically on next query.`);
367
367
  }
@@ -372,11 +372,11 @@ export function createPostgresProvider(options) {
372
372
  }
373
373
  }
374
374
  else {
375
- throw new Error('Postgres client does not support $executeRaw or query method. Make sure you are passing a valid pg Pool, Client, or Prisma client.');
375
+ throw new Error("Postgres client does not support $executeRaw or query method. Make sure you are passing a valid pg Pool, Client, or Prisma client.");
376
376
  }
377
377
  },
378
378
  async query(options) {
379
- const { limit = 20, after, sort = 'desc', type, userId } = options;
379
+ const { limit = 20, after, sort = "desc", type, userId, since } = options;
380
380
  let queryFn;
381
381
  if (client.$executeRaw) {
382
382
  // Prisma client
@@ -385,14 +385,14 @@ export function createPostgresProvider(options) {
385
385
  let processedQuery = query;
386
386
  params.forEach((param, index) => {
387
387
  const placeholder = `$${index + 1}`;
388
- const value = typeof param === 'string'
388
+ const value = typeof param === "string"
389
389
  ? `'${param.replace(/'/g, "''")}'`
390
390
  : param === null
391
- ? 'NULL'
391
+ ? "NULL"
392
392
  : param instanceof Date
393
393
  ? `'${param.toISOString()}'`
394
394
  : String(param);
395
- processedQuery = processedQuery.replace(new RegExp(`\\${placeholder}(?![0-9])`, 'g'), value);
395
+ processedQuery = processedQuery.replace(new RegExp(`\\${placeholder}(?![0-9])`, "g"), value);
396
396
  });
397
397
  return await client.$queryRawUnsafe(processedQuery);
398
398
  }
@@ -409,7 +409,7 @@ export function createPostgresProvider(options) {
409
409
  };
410
410
  }
411
411
  else {
412
- throw new Error('Postgres client does not support $executeRaw or query method');
412
+ throw new Error("Postgres client does not support $executeRaw or query method");
413
413
  }
414
414
  try {
415
415
  const checkTableQuery = `
@@ -423,8 +423,8 @@ export function createPostgresProvider(options) {
423
423
  if (client.$executeRaw) {
424
424
  // Prisma client - replace $1, $2 with actual values
425
425
  const prismaQuery = checkTableQuery
426
- .replace('$1', `'${schema}'`)
427
- .replace('$2', `'${tableName}'`);
426
+ .replace("$1", `'${schema}'`)
427
+ .replace("$2", `'${tableName}'`);
428
428
  checkResult = await client.$queryRawUnsafe(prismaQuery);
429
429
  }
430
430
  else {
@@ -450,7 +450,7 @@ export function createPostgresProvider(options) {
450
450
  let paramIndex = 1;
451
451
  // Cursor-based pagination
452
452
  if (after) {
453
- if (sort === 'desc') {
453
+ if (sort === "desc") {
454
454
  whereClauses.push(`id < $${paramIndex++}`);
455
455
  params.push(after);
456
456
  }
@@ -467,8 +467,12 @@ export function createPostgresProvider(options) {
467
467
  whereClauses.push(`user_id = $${paramIndex++}`);
468
468
  params.push(userId);
469
469
  }
470
- const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '';
471
- const orderDirection = sort === 'desc' ? 'DESC' : 'ASC';
470
+ if (since) {
471
+ whereClauses.push(`timestamp >= $${paramIndex++}`);
472
+ params.push(since instanceof Date ? since.toISOString() : since);
473
+ }
474
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
475
+ const orderDirection = sort === "desc" ? "DESC" : "ASC";
472
476
  const query = `
473
477
  SELECT id, type, timestamp, status, user_id, session_id, organization_id,
474
478
  metadata, ip_address, user_agent, source, display_message, display_severity
@@ -486,17 +490,17 @@ export function createPostgresProvider(options) {
486
490
  id: row.id,
487
491
  type: row.type,
488
492
  timestamp: new Date(row.timestamp),
489
- status: row.status || 'success',
493
+ status: row.status || "success",
490
494
  userId: row.user_id,
491
495
  sessionId: row.session_id,
492
496
  organizationId: row.organization_id,
493
- metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata || {},
497
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata || {},
494
498
  ipAddress: row.ip_address,
495
499
  userAgent: row.user_agent,
496
- source: row.source || 'app',
500
+ source: row.source || "app",
497
501
  display: {
498
502
  message: row.display_message || row.type,
499
- severity: row.display_severity || 'info',
503
+ severity: row.display_severity || "info",
500
504
  },
501
505
  }));
502
506
  return {
@@ -506,7 +510,7 @@ export function createPostgresProvider(options) {
506
510
  };
507
511
  }
508
512
  catch (error) {
509
- if (error?.message?.includes('does not exist') || error?.code === '42P01') {
513
+ if (error?.message?.includes("does not exist") || error?.code === "42P01") {
510
514
  return {
511
515
  events: [],
512
516
  hasMore: false,
@@ -519,29 +523,29 @@ export function createPostgresProvider(options) {
519
523
  };
520
524
  }
521
525
  export function createSqliteProvider(options) {
522
- const { client, tableName = 'auth_events' } = options;
526
+ const { client, tableName = "auth_events" } = options;
523
527
  // Validate client is provided
524
528
  if (!client) {
525
- throw new Error('SQLite client is required. Provide a better-sqlite3 Database instance.');
529
+ throw new Error("SQLite client is required. Provide a better-sqlite3 Database instance.");
526
530
  }
527
531
  // Handle case where client might be a function (lazy initialization) or already initialized
528
- const actualClient = typeof client === 'function' ? client() : client;
532
+ const actualClient = typeof client === "function" ? client() : client;
529
533
  // If client initialization failed (native module not found), try to provide helpful error
530
534
  if (!actualClient) {
531
- throw new Error('SQLite client initialization failed. Make sure better-sqlite3 is properly installed and built. ' +
532
- 'Run: pnpm rebuild better-sqlite3 or npm rebuild better-sqlite3');
535
+ throw new Error("SQLite client initialization failed. Make sure better-sqlite3 is properly installed and built. " +
536
+ "Run: pnpm rebuild better-sqlite3 or npm rebuild better-sqlite3");
533
537
  }
534
538
  // Validate client has required methods (better-sqlite3 Database)
535
- const hasExec = typeof actualClient.exec === 'function';
536
- const hasPrepare = typeof actualClient.prepare === 'function';
539
+ const hasExec = typeof actualClient.exec === "function";
540
+ const hasPrepare = typeof actualClient.prepare === "function";
537
541
  if (!hasExec || !hasPrepare) {
538
542
  // If methods don't exist, it might be an initialization error - provide helpful message
539
543
  if (actualClient instanceof Error) {
540
544
  throw new Error(`SQLite client initialization error: ${actualClient.message}. ` +
541
- 'Make sure better-sqlite3 native module is built. Run: pnpm rebuild better-sqlite3');
545
+ "Make sure better-sqlite3 native module is built. Run: pnpm rebuild better-sqlite3");
542
546
  }
543
- throw new Error('Invalid SQLite client. Client must have `exec` and `prepare` methods (better-sqlite3 Database instance). ' +
544
- 'If using better-sqlite3, make sure the native module is built: pnpm rebuild better-sqlite3');
547
+ throw new Error("Invalid SQLite client. Client must have `exec` and `prepare` methods (better-sqlite3 Database instance). " +
548
+ "If using better-sqlite3, make sure the native module is built: pnpm rebuild better-sqlite3");
545
549
  }
546
550
  // Ensure table exists
547
551
  const ensureTable = async () => {
@@ -584,7 +588,7 @@ export function createSqliteProvider(options) {
584
588
  }
585
589
  }
586
590
  catch (error) {
587
- if (error?.message?.includes('already exists')) {
591
+ if (error?.message?.includes("already exists")) {
588
592
  return;
589
593
  }
590
594
  console.error(`Failed to ensure ${tableName} table:`, error);
@@ -624,12 +628,12 @@ export function createSqliteProvider(options) {
624
628
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
625
629
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
626
630
  `);
627
- stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || 'success', event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
631
+ stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || "success", event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
628
632
  }
629
633
  catch (error) {
630
634
  console.error(`Failed to insert event (${event.type}) into ${tableName}:`, error);
631
635
  // If table doesn't exist, try to create it and retry
632
- if (error.message?.includes('no such table')) {
636
+ if (error.message?.includes("no such table")) {
633
637
  tableEnsured = false;
634
638
  await ensureTableSync();
635
639
  try {
@@ -638,7 +642,7 @@ export function createSqliteProvider(options) {
638
642
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
639
643
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
640
644
  `);
641
- stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || 'success', event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
645
+ stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || "success", event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
642
646
  return;
643
647
  }
644
648
  catch (retryError) {
@@ -661,14 +665,14 @@ export function createSqliteProvider(options) {
661
665
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
662
666
  `);
663
667
  for (const event of events) {
664
- stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || 'success', event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
668
+ stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || "success", event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
665
669
  }
666
670
  });
667
671
  transaction(events);
668
672
  }
669
673
  catch (error) {
670
674
  console.error(`Failed to insert batch (${events.length} events):`, error);
671
- if (error.message?.includes('no such table')) {
675
+ if (error.message?.includes("no such table")) {
672
676
  tableEnsured = false;
673
677
  await ensureTableSync();
674
678
  try {
@@ -679,7 +683,7 @@ export function createSqliteProvider(options) {
679
683
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
680
684
  `);
681
685
  for (const event of events) {
682
- stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || 'success', event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
686
+ stmt.run(event.id, event.type, event.timestamp.toISOString(), event.status || "success", event.userId || null, event.sessionId || null, event.organizationId || null, JSON.stringify(event.metadata || {}), event.ipAddress || null, event.userAgent || null, event.source, event.display?.message || null, event.display?.severity || null);
683
687
  }
684
688
  });
685
689
  transaction(events);
@@ -694,7 +698,7 @@ export function createSqliteProvider(options) {
694
698
  }
695
699
  },
696
700
  async query(options) {
697
- const { limit = 20, after, sort = 'desc', type, userId } = options;
701
+ const { limit = 20, after, sort = "desc", type, userId, since } = options;
698
702
  await ensureTableSync();
699
703
  try {
700
704
  let query = `SELECT * FROM ${tableName} WHERE 1=1`;
@@ -707,11 +711,15 @@ export function createSqliteProvider(options) {
707
711
  query += ` AND user_id = ?`;
708
712
  params.push(userId);
709
713
  }
714
+ if (since) {
715
+ query += ` AND timestamp >= ?`;
716
+ params.push(since instanceof Date ? since.toISOString() : since);
717
+ }
710
718
  if (after) {
711
719
  query += ` AND id > ?`;
712
720
  params.push(after);
713
721
  }
714
- query += ` ORDER BY timestamp ${sort === 'desc' ? 'DESC' : 'ASC'}`;
722
+ query += ` ORDER BY timestamp ${sort === "desc" ? "DESC" : "ASC"}`;
715
723
  query += ` LIMIT ?`;
716
724
  params.push(limit + 1); // Fetch one extra to check if there are more
717
725
  const stmt = actualClient.prepare(query);
@@ -725,10 +733,10 @@ export function createSqliteProvider(options) {
725
733
  userId: row.user_id || undefined,
726
734
  sessionId: row.session_id || undefined,
727
735
  organizationId: row.organization_id || undefined,
728
- metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata || {},
736
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata || {},
729
737
  ipAddress: row.ip_address || undefined,
730
738
  userAgent: row.user_agent || undefined,
731
- source: row.source || 'app',
739
+ source: row.source || "app",
732
740
  display: row.display_message || row.display_severity
733
741
  ? {
734
742
  message: row.display_message || undefined,
@@ -743,7 +751,7 @@ export function createSqliteProvider(options) {
743
751
  };
744
752
  }
745
753
  catch (error) {
746
- if (error.message?.includes('no such table')) {
754
+ if (error.message?.includes("no such table")) {
747
755
  await ensureTableSync();
748
756
  return { events: [], hasMore: false, nextCursor: null };
749
757
  }
@@ -754,7 +762,7 @@ export function createSqliteProvider(options) {
754
762
  };
755
763
  }
756
764
  export function createClickHouseProvider(options) {
757
- const { client, table = 'auth_events', database } = options;
765
+ const { client, table = "auth_events", database } = options;
758
766
  const ensureTable = async () => {
759
767
  if (!client)
760
768
  return;
@@ -793,7 +801,7 @@ export function createClickHouseProvider(options) {
793
801
  }
794
802
  }
795
803
  catch (error) {
796
- if (error?.message?.includes('already exists') || error?.code === 57) {
804
+ if (error?.message?.includes("already exists") || error?.code === 57) {
797
805
  return;
798
806
  }
799
807
  console.error(`Failed to ensure ${table} table in ClickHouse:`, error);
@@ -810,7 +818,7 @@ export function createClickHouseProvider(options) {
810
818
  tableEnsured = true;
811
819
  }
812
820
  catch (error) {
813
- console.error('Failed to ensure table:', error);
821
+ console.error("Failed to ensure table:", error);
814
822
  }
815
823
  finally {
816
824
  tableEnsuring = false;
@@ -828,30 +836,30 @@ export function createClickHouseProvider(options) {
828
836
  id: event.id,
829
837
  type: event.type,
830
838
  timestamp: event.timestamp,
831
- status: event.status || 'success',
832
- user_id: event.userId || '',
833
- session_id: event.sessionId || '',
834
- organization_id: event.organizationId || '',
839
+ status: event.status || "success",
840
+ user_id: event.userId || "",
841
+ session_id: event.sessionId || "",
842
+ organization_id: event.organizationId || "",
835
843
  metadata: JSON.stringify(event.metadata || {}),
836
- ip_address: event.ipAddress || '',
837
- user_agent: event.userAgent || '',
844
+ ip_address: event.ipAddress || "",
845
+ user_agent: event.userAgent || "",
838
846
  source: event.source,
839
- display_message: event.display?.message || '',
840
- display_severity: event.display?.severity || '',
847
+ display_message: event.display?.message || "",
848
+ display_severity: event.display?.severity || "",
841
849
  }));
842
850
  try {
843
851
  if (client.insert) {
844
852
  await client.insert({
845
853
  table: tableFullName,
846
854
  values: rows,
847
- format: 'JSONEachRow',
855
+ format: "JSONEachRow",
848
856
  });
849
857
  console.log(`✅ Inserted ${rows.length} event(s) into ClickHouse ${tableFullName}`);
850
858
  }
851
859
  else {
852
860
  const values = rows
853
- .map((row) => `('${row.id}', '${row.type}', '${new Date(row.timestamp).toISOString().replace('T', ' ').slice(0, 19)}', '${row.status || 'success'}', ${row.user_id ? `'${row.user_id.replace(/'/g, "''")}'` : 'NULL'}, ${row.session_id ? `'${row.session_id.replace(/'/g, "''")}'` : 'NULL'}, ${row.organization_id ? `'${row.organization_id.replace(/'/g, "''")}'` : 'NULL'}, '${row.metadata.replace(/'/g, "''")}', ${row.ip_address ? `'${row.ip_address.replace(/'/g, "''")}'` : 'NULL'}, ${row.user_agent ? `'${row.user_agent.replace(/'/g, "''")}'` : 'NULL'}, '${row.source}', ${row.display_message ? `'${row.display_message.replace(/'/g, "''")}'` : 'NULL'}, ${row.display_severity ? `'${row.display_severity}'` : 'NULL'})`)
854
- .join(', ');
861
+ .map((row) => `('${row.id}', '${row.type}', '${new Date(row.timestamp).toISOString().replace("T", " ").slice(0, 19)}', '${row.status || "success"}', ${row.user_id ? `'${row.user_id.replace(/'/g, "''")}'` : "NULL"}, ${row.session_id ? `'${row.session_id.replace(/'/g, "''")}'` : "NULL"}, ${row.organization_id ? `'${row.organization_id.replace(/'/g, "''")}'` : "NULL"}, '${row.metadata.replace(/'/g, "''")}', ${row.ip_address ? `'${row.ip_address.replace(/'/g, "''")}'` : "NULL"}, ${row.user_agent ? `'${row.user_agent.replace(/'/g, "''")}'` : "NULL"}, '${row.source}', ${row.display_message ? `'${row.display_message.replace(/'/g, "''")}'` : "NULL"}, ${row.display_severity ? `'${row.display_severity}'` : "NULL"})`)
862
+ .join(", ");
855
863
  const insertQuery = `
856
864
  INSERT INTO ${tableFullName}
857
865
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
@@ -864,7 +872,7 @@ export function createClickHouseProvider(options) {
864
872
  await client.query({ query: insertQuery });
865
873
  }
866
874
  else {
867
- throw new Error('ClickHouse client does not support insert, exec, or query methods');
875
+ throw new Error("ClickHouse client does not support insert, exec, or query methods");
868
876
  }
869
877
  console.log(`✅ Inserted ${rows.length} event(s) into ClickHouse ${tableFullName} via query`);
870
878
  }
@@ -882,7 +890,7 @@ export function createClickHouseProvider(options) {
882
890
  await ingestBatchFn(events);
883
891
  },
884
892
  async query(options) {
885
- const { limit = 20, after, sort = 'desc', type, userId } = options;
893
+ const { limit = 20, after, sort = "desc", type, userId, since } = options;
886
894
  const tableFullName = database ? `${database}.${table}` : table;
887
895
  try {
888
896
  const checkTableQuery = `EXISTS TABLE ${tableFullName}`;
@@ -891,7 +899,7 @@ export function createClickHouseProvider(options) {
891
899
  try {
892
900
  const checkResult = await client.query({
893
901
  query: checkTableQuery,
894
- format: 'JSONEachRow',
902
+ format: "JSONEachRow",
895
903
  });
896
904
  const rows = await checkResult.json();
897
905
  tableExists =
@@ -904,7 +912,7 @@ export function createClickHouseProvider(options) {
904
912
  else if (client.exec) {
905
913
  try {
906
914
  const checkResult = await client.exec({ query: checkTableQuery });
907
- tableExists = checkResult === '1' || String(checkResult).includes('1');
915
+ tableExists = checkResult === "1" || String(checkResult).includes("1");
908
916
  }
909
917
  catch {
910
918
  tableExists = false;
@@ -952,7 +960,7 @@ export function createClickHouseProvider(options) {
952
960
  try {
953
961
  const columnResult = await client.query({
954
962
  query: checkColumnQuery,
955
- format: 'JSONEachRow',
963
+ format: "JSONEachRow",
956
964
  });
957
965
  const columnRows = await columnResult.json();
958
966
  columnExists = columnRows && columnRows.length > 0 && columnRows[0]?.exists > 0;
@@ -964,7 +972,7 @@ export function createClickHouseProvider(options) {
964
972
  else if (client.exec) {
965
973
  try {
966
974
  const columnResult = await client.exec({ query: checkColumnQuery });
967
- columnExists = String(columnResult).includes('1') || columnResult === '1';
975
+ columnExists = String(columnResult).includes("1") || columnResult === "1";
968
976
  }
969
977
  catch {
970
978
  columnExists = false;
@@ -992,13 +1000,13 @@ export function createClickHouseProvider(options) {
992
1000
  }
993
1001
  }
994
1002
  catch (error) {
995
- if (!error?.message?.includes('already exists') && error?.code !== 57) {
1003
+ if (!error?.message?.includes("already exists") && error?.code !== 57) {
996
1004
  console.warn(`Failed to ensure ClickHouse table ${tableFullName}:`, error);
997
1005
  }
998
1006
  }
999
1007
  const whereClauses = [];
1000
1008
  if (after) {
1001
- if (sort === 'desc') {
1009
+ if (sort === "desc") {
1002
1010
  whereClauses.push(`id < '${String(after).replace(/'/g, "''")}'`);
1003
1011
  }
1004
1012
  else {
@@ -1011,8 +1019,15 @@ export function createClickHouseProvider(options) {
1011
1019
  if (userId) {
1012
1020
  whereClauses.push(`user_id = '${String(userId).replace(/'/g, "''")}'`);
1013
1021
  }
1014
- const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '';
1015
- const orderDirection = sort === 'desc' ? 'DESC' : 'ASC';
1022
+ if (since) {
1023
+ const sinceDate = since instanceof Date ? since : new Date(since);
1024
+ // ClickHouse requires DateTime type, so we need to cast the string to DateTime
1025
+ // Format: 'YYYY-MM-DD HH:MM:SS' or use toDateTime() function
1026
+ const isoString = sinceDate.toISOString().replace("T", " ").slice(0, 19);
1027
+ whereClauses.push(`timestamp >= toDateTime('${isoString.replace(/'/g, "''")}')`);
1028
+ }
1029
+ const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
1030
+ const orderDirection = sort === "desc" ? "DESC" : "ASC";
1016
1031
  let query = `
1017
1032
  SELECT id, type, timestamp, status, user_id, session_id, organization_id,
1018
1033
  metadata, ip_address, user_agent, source, display_message, display_severity
@@ -1025,20 +1040,20 @@ export function createClickHouseProvider(options) {
1025
1040
  let hasStatusColumn = true;
1026
1041
  try {
1027
1042
  if (client.query) {
1028
- const queryResult = await client.query({ query, format: 'JSONEachRow' });
1043
+ const queryResult = await client.query({ query, format: "JSONEachRow" });
1029
1044
  result = await queryResult.json();
1030
1045
  }
1031
1046
  else if (client.exec) {
1032
- const execResult = await client.exec({ query, format: 'JSONEachRow' });
1033
- result = typeof execResult === 'string' ? JSON.parse(execResult) : execResult;
1047
+ const execResult = await client.exec({ query, format: "JSONEachRow" });
1048
+ result = typeof execResult === "string" ? JSON.parse(execResult) : execResult;
1034
1049
  }
1035
1050
  else {
1036
- throw new Error('ClickHouse client does not support query or exec methods');
1051
+ throw new Error("ClickHouse client does not support query or exec methods");
1037
1052
  }
1038
1053
  }
1039
1054
  catch (error) {
1040
- if (error?.message?.includes('Unknown expression identifier') &&
1041
- error?.message?.includes('status')) {
1055
+ if (error?.message?.includes("Unknown expression identifier") &&
1056
+ error?.message?.includes("status")) {
1042
1057
  console.warn(`Status column not found in ${tableFullName}, querying without it`);
1043
1058
  hasStatusColumn = false;
1044
1059
  query = `
@@ -1051,12 +1066,12 @@ export function createClickHouseProvider(options) {
1051
1066
  `;
1052
1067
  try {
1053
1068
  if (client.query) {
1054
- const queryResult = await client.query({ query, format: 'JSONEachRow' });
1069
+ const queryResult = await client.query({ query, format: "JSONEachRow" });
1055
1070
  result = await queryResult.json();
1056
1071
  }
1057
1072
  else if (client.exec) {
1058
- const execResult = await client.exec({ query, format: 'JSONEachRow' });
1059
- result = typeof execResult === 'string' ? JSON.parse(execResult) : execResult;
1073
+ const execResult = await client.exec({ query, format: "JSONEachRow" });
1074
+ result = typeof execResult === "string" ? JSON.parse(execResult) : execResult;
1060
1075
  }
1061
1076
  }
1062
1077
  catch (retryError) {
@@ -1087,17 +1102,17 @@ export function createClickHouseProvider(options) {
1087
1102
  id: row.id,
1088
1103
  type: row.type,
1089
1104
  timestamp: new Date(row.timestamp),
1090
- status: hasStatusColumn ? row.status || 'success' : 'success',
1105
+ status: hasStatusColumn ? row.status || "success" : "success",
1091
1106
  userId: row.user_id || undefined,
1092
1107
  sessionId: row.session_id || undefined,
1093
1108
  organizationId: row.organization_id || undefined,
1094
- metadata: typeof row.metadata === 'string' ? JSON.parse(row.metadata) : row.metadata || {},
1109
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata || {},
1095
1110
  ipAddress: row.ip_address || undefined,
1096
1111
  userAgent: row.user_agent || undefined,
1097
- source: row.source || 'app',
1112
+ source: row.source || "app",
1098
1113
  display: {
1099
1114
  message: row.display_message || row.type,
1100
- severity: row.display_severity || 'info',
1115
+ severity: row.display_severity || "info",
1101
1116
  },
1102
1117
  }));
1103
1118
  return {
@@ -1114,23 +1129,23 @@ export function createHttpProvider(options) {
1114
1129
  async ingest(event) {
1115
1130
  const payload = transform ? transform(event) : event;
1116
1131
  await client(url, {
1117
- method: 'POST',
1118
- headers: { 'Content-Type': 'application/json', ...headers },
1132
+ method: "POST",
1133
+ headers: { "Content-Type": "application/json", ...headers },
1119
1134
  body: JSON.stringify(payload),
1120
1135
  });
1121
1136
  },
1122
1137
  async ingestBatch(events) {
1123
1138
  const payload = events.map((event) => (transform ? transform(event) : event));
1124
1139
  await client(url, {
1125
- method: 'POST',
1126
- headers: { 'Content-Type': 'application/json', ...headers },
1140
+ method: "POST",
1141
+ headers: { "Content-Type": "application/json", ...headers },
1127
1142
  body: JSON.stringify({ events: payload }),
1128
1143
  });
1129
1144
  },
1130
1145
  };
1131
1146
  }
1132
1147
  export function createStorageProvider(options) {
1133
- const { adapter, tableName = 'auth_events' } = options;
1148
+ const { adapter, tableName = "auth_events" } = options;
1134
1149
  let tableEnsured = false;
1135
1150
  let tableEnsuringPromise = null;
1136
1151
  const ensureTable = async () => {
@@ -1154,10 +1169,10 @@ export function createStorageProvider(options) {
1154
1169
  }
1155
1170
  catch (error) {
1156
1171
  // Table doesn't exist - try to create it if adapter supports raw SQL
1157
- if (error?.message?.includes('not found in schema') ||
1158
- error?.message?.includes('Model') ||
1159
- error?.code === 'P2025' ||
1160
- error?.code === '42P01') {
1172
+ if (error?.message?.includes("not found in schema") ||
1173
+ error?.message?.includes("Model") ||
1174
+ error?.code === "P2025" ||
1175
+ error?.code === "42P01") {
1161
1176
  // Try to create table using raw SQL if adapter supports it
1162
1177
  if (adapter.executeRaw || adapter.$executeRaw || adapter.queryRaw) {
1163
1178
  try {
@@ -1223,7 +1238,7 @@ export function createStorageProvider(options) {
1223
1238
  id: event.id,
1224
1239
  type: event.type,
1225
1240
  timestamp: event.timestamp,
1226
- status: event.status || 'success',
1241
+ status: event.status || "success",
1227
1242
  userId: event.userId,
1228
1243
  sessionId: event.sessionId,
1229
1244
  organizationId: event.organizationId,
@@ -1243,7 +1258,7 @@ export function createStorageProvider(options) {
1243
1258
  id: event.id,
1244
1259
  type: event.type,
1245
1260
  timestamp: event.timestamp,
1246
- status: event.status || 'success',
1261
+ status: event.status || "success",
1247
1262
  user_id: event.userId,
1248
1263
  session_id: event.sessionId,
1249
1264
  organization_id: event.organizationId,
@@ -1267,7 +1282,7 @@ export function createStorageProvider(options) {
1267
1282
  id: event.id,
1268
1283
  type: event.type,
1269
1284
  timestamp: event.timestamp,
1270
- status: event.status || 'success',
1285
+ status: event.status || "success",
1271
1286
  userId: event.userId,
1272
1287
  sessionId: event.sessionId,
1273
1288
  organizationId: event.organizationId,
@@ -1285,7 +1300,7 @@ export function createStorageProvider(options) {
1285
1300
  }
1286
1301
  },
1287
1302
  async query(options) {
1288
- const { limit = 20, after, sort = 'desc', type, userId } = options;
1303
+ const { limit = 20, after, sort = "desc", type, userId, since } = options;
1289
1304
  // Ensure table exists before querying
1290
1305
  await ensureTable();
1291
1306
  // First, try findMany (normal path)
@@ -1293,23 +1308,27 @@ export function createStorageProvider(options) {
1293
1308
  try {
1294
1309
  const where = [];
1295
1310
  if (after) {
1296
- if (sort === 'desc') {
1297
- where.push({ field: 'id', operator: '<', value: after });
1311
+ if (sort === "desc") {
1312
+ where.push({ field: "id", operator: "<", value: after });
1298
1313
  }
1299
1314
  else {
1300
- where.push({ field: 'id', operator: '>', value: after });
1315
+ where.push({ field: "id", operator: ">", value: after });
1301
1316
  }
1302
1317
  }
1303
1318
  if (type) {
1304
- where.push({ field: 'type', value: type });
1319
+ where.push({ field: "type", value: type });
1305
1320
  }
1306
1321
  if (userId) {
1307
- where.push({ field: 'userId', value: userId });
1322
+ where.push({ field: "userId", value: userId });
1323
+ }
1324
+ if (since) {
1325
+ const sinceDate = since instanceof Date ? since : new Date(since);
1326
+ where.push({ field: "timestamp", operator: ">=", value: sinceDate });
1308
1327
  }
1309
1328
  const events = await adapter.findMany({
1310
1329
  model: tableName,
1311
1330
  where,
1312
- orderBy: [{ field: 'timestamp', direction: sort === 'desc' ? 'desc' : 'asc' }],
1331
+ orderBy: [{ field: "timestamp", direction: sort === "desc" ? "desc" : "asc" }],
1313
1332
  limit: limit + 1, // Get one extra to check hasMore
1314
1333
  });
1315
1334
  const hasMore = events.length > limit;
@@ -1317,19 +1336,19 @@ export function createStorageProvider(options) {
1317
1336
  id: event.id,
1318
1337
  type: event.type,
1319
1338
  timestamp: new Date(event.timestamp || event.createdAt),
1320
- status: event.status || 'success',
1339
+ status: event.status || "success",
1321
1340
  userId: event.userId || event.user_id,
1322
1341
  sessionId: event.sessionId || event.session_id,
1323
1342
  organizationId: event.organizationId || event.organization_id,
1324
- metadata: typeof event.metadata === 'string'
1343
+ metadata: typeof event.metadata === "string"
1325
1344
  ? JSON.parse(event.metadata)
1326
1345
  : event.metadata || {},
1327
1346
  ipAddress: event.ipAddress || event.ip_address,
1328
1347
  userAgent: event.userAgent || event.user_agent,
1329
- source: event.source || 'app',
1348
+ source: event.source || "app",
1330
1349
  display: {
1331
1350
  message: event.displayMessage || event.display_message || event.type,
1332
- severity: event.displaySeverity || event.display_severity || 'info',
1351
+ severity: event.displaySeverity || event.display_severity || "info",
1333
1352
  },
1334
1353
  }));
1335
1354
  return {
@@ -1340,9 +1359,9 @@ export function createStorageProvider(options) {
1340
1359
  }
1341
1360
  catch (findManyError) {
1342
1361
  // If findMany fails with "model not found", try raw SQL
1343
- if (findManyError?.message?.includes('not found in schema') ||
1344
- findManyError?.message?.includes('Model') ||
1345
- findManyError?.code === 'P2025') {
1362
+ if (findManyError?.message?.includes("not found in schema") ||
1363
+ findManyError?.message?.includes("Model") ||
1364
+ findManyError?.code === "P2025") {
1346
1365
  // Fall through to raw SQL fallback
1347
1366
  }
1348
1367
  else {
@@ -1375,7 +1394,7 @@ export function createStorageProvider(options) {
1375
1394
  const params = [];
1376
1395
  let paramIndex = 1;
1377
1396
  if (after) {
1378
- if (sort === 'desc') {
1397
+ if (sort === "desc") {
1379
1398
  whereConditions.push(`id < $${paramIndex}`);
1380
1399
  }
1381
1400
  else {
@@ -1394,8 +1413,13 @@ export function createStorageProvider(options) {
1394
1413
  params.push(userId);
1395
1414
  paramIndex++;
1396
1415
  }
1397
- const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
1398
- const orderDirection = sort === 'desc' ? 'DESC' : 'ASC';
1416
+ if (since) {
1417
+ whereConditions.push(`timestamp >= $${paramIndex}`);
1418
+ params.push(since instanceof Date ? since : new Date(since));
1419
+ paramIndex++;
1420
+ }
1421
+ const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : "";
1422
+ const orderDirection = sort === "desc" ? "DESC" : "ASC";
1399
1423
  // Use appropriate raw SQL method
1400
1424
  let queryFn;
1401
1425
  if (sqlAdapter.$queryRawUnsafe || adapter.$queryRawUnsafe) {
@@ -1404,14 +1428,14 @@ export function createStorageProvider(options) {
1404
1428
  // Replace $1, $2, etc. with actual values for Prisma
1405
1429
  params.forEach((param, index) => {
1406
1430
  const placeholder = `$${index + 1}`;
1407
- const value = typeof param === 'string'
1431
+ const value = typeof param === "string"
1408
1432
  ? `'${param.replace(/'/g, "''")}'`
1409
1433
  : param === null
1410
- ? 'NULL'
1434
+ ? "NULL"
1411
1435
  : param instanceof Date
1412
1436
  ? `'${param.toISOString()}'`
1413
1437
  : String(param);
1414
- query = query.replace(new RegExp(`\\${placeholder}(?![0-9])`, 'g'), value);
1438
+ query = query.replace(new RegExp(`\\${placeholder}(?![0-9])`, "g"), value);
1415
1439
  });
1416
1440
  const queryMethod = sqlAdapter.$queryRawUnsafe || adapter.$queryRawUnsafe;
1417
1441
  queryFn = async (q) => await queryMethod(q);
@@ -1421,19 +1445,19 @@ export function createStorageProvider(options) {
1421
1445
  id: event.id,
1422
1446
  type: event.type,
1423
1447
  timestamp: new Date(event.timestamp || event.created_at),
1424
- status: event.status || 'success',
1448
+ status: event.status || "success",
1425
1449
  userId: event.user_id || event.userId,
1426
1450
  sessionId: event.session_id || event.sessionId,
1427
1451
  organizationId: event.organization_id || event.organizationId,
1428
- metadata: typeof event.metadata === 'string'
1452
+ metadata: typeof event.metadata === "string"
1429
1453
  ? JSON.parse(event.metadata)
1430
1454
  : event.metadata || {},
1431
1455
  ipAddress: event.ip_address || event.ipAddress,
1432
1456
  userAgent: event.user_agent || event.userAgent,
1433
- source: event.source || 'app',
1457
+ source: event.source || "app",
1434
1458
  display: {
1435
1459
  message: event.display_message || event.displayMessage || event.type,
1436
- severity: event.display_severity || event.displaySeverity || 'info',
1460
+ severity: event.display_severity || event.displaySeverity || "info",
1437
1461
  },
1438
1462
  }));
1439
1463
  return {
@@ -1455,7 +1479,7 @@ export function createStorageProvider(options) {
1455
1479
  queryFn = async (q) => await queryMethod(q, params);
1456
1480
  }
1457
1481
  else {
1458
- throw new Error('Raw SQL not supported');
1482
+ throw new Error("Raw SQL not supported");
1459
1483
  }
1460
1484
  const result = await queryFn(query);
1461
1485
  const hasMore = result.length > limit;
@@ -1463,19 +1487,19 @@ export function createStorageProvider(options) {
1463
1487
  id: event.id,
1464
1488
  type: event.type,
1465
1489
  timestamp: new Date(event.timestamp || event.created_at),
1466
- status: event.status || 'success',
1490
+ status: event.status || "success",
1467
1491
  userId: event.user_id || event.userId,
1468
1492
  sessionId: event.session_id || event.sessionId,
1469
1493
  organizationId: event.organization_id || event.organizationId,
1470
- metadata: typeof event.metadata === 'string'
1494
+ metadata: typeof event.metadata === "string"
1471
1495
  ? JSON.parse(event.metadata)
1472
1496
  : event.metadata || {},
1473
1497
  ipAddress: event.ip_address || event.ipAddress,
1474
1498
  userAgent: event.user_agent || event.userAgent,
1475
- source: event.source || 'app',
1499
+ source: event.source || "app",
1476
1500
  display: {
1477
1501
  message: event.display_message || event.displayMessage || event.type,
1478
- severity: event.display_severity || event.displaySeverity || 'info',
1502
+ severity: event.display_severity || event.displaySeverity || "info",
1479
1503
  },
1480
1504
  }));
1481
1505
  return {
@@ -1487,7 +1511,7 @@ export function createStorageProvider(options) {
1487
1511
  }
1488
1512
  catch (rawError) {
1489
1513
  // If raw SQL also fails, return empty result
1490
- console.warn('Raw SQL query failed:', rawError);
1514
+ console.warn("Raw SQL query failed:", rawError);
1491
1515
  return {
1492
1516
  events: [],
1493
1517
  hasMore: false,
@@ -1496,7 +1520,7 @@ export function createStorageProvider(options) {
1496
1520
  }
1497
1521
  }
1498
1522
  // If we get here, neither findMany nor raw SQL worked
1499
- throw new Error('Adapter does not support findMany or raw SQL');
1523
+ throw new Error("Adapter does not support findMany or raw SQL");
1500
1524
  },
1501
1525
  };
1502
1526
  }