better-auth-studio 1.1.1-beta.6 → 1.1.1-beta.8

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 (57) hide show
  1. package/dist/adapters/astro.d.ts.map +1 -1
  2. package/dist/adapters/astro.js +4 -0
  3. package/dist/adapters/astro.js.map +1 -1
  4. package/dist/adapters/elysia.d.ts.map +1 -1
  5. package/dist/adapters/elysia.js +4 -0
  6. package/dist/adapters/elysia.js.map +1 -1
  7. package/dist/adapters/express.d.ts.map +1 -1
  8. package/dist/adapters/express.js +4 -0
  9. package/dist/adapters/express.js.map +1 -1
  10. package/dist/adapters/hono.d.ts.map +1 -1
  11. package/dist/adapters/hono.js +0 -1
  12. package/dist/adapters/hono.js.map +1 -1
  13. package/dist/adapters/nextjs.d.ts.map +1 -1
  14. package/dist/adapters/nextjs.js +4 -0
  15. package/dist/adapters/nextjs.js.map +1 -1
  16. package/dist/adapters/nuxt.d.ts +6 -6
  17. package/dist/adapters/nuxt.d.ts.map +1 -1
  18. package/dist/adapters/nuxt.js +63 -44
  19. package/dist/adapters/nuxt.js.map +1 -1
  20. package/dist/adapters/remix.d.ts.map +1 -1
  21. package/dist/adapters/remix.js +4 -0
  22. package/dist/adapters/remix.js.map +1 -1
  23. package/dist/adapters/solid-start.d.ts.map +1 -1
  24. package/dist/adapters/solid-start.js +4 -0
  25. package/dist/adapters/solid-start.js.map +1 -1
  26. package/dist/adapters/svelte-kit.d.ts.map +1 -1
  27. package/dist/adapters/svelte-kit.js +4 -0
  28. package/dist/adapters/svelte-kit.js.map +1 -1
  29. package/dist/adapters/tanstack-start.d.ts.map +1 -1
  30. package/dist/adapters/tanstack-start.js +4 -0
  31. package/dist/adapters/tanstack-start.js.map +1 -1
  32. package/dist/core/handler.d.ts +5 -0
  33. package/dist/core/handler.d.ts.map +1 -1
  34. package/dist/core/handler.js +21 -21
  35. package/dist/core/handler.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/providers/events/helpers.d.ts +1 -0
  41. package/dist/providers/events/helpers.d.ts.map +1 -1
  42. package/dist/providers/events/helpers.js +295 -110
  43. package/dist/providers/events/helpers.js.map +1 -1
  44. package/dist/types/handler.d.ts +1 -1
  45. package/dist/types/handler.d.ts.map +1 -1
  46. package/dist/utils/event-ingestion.d.ts.map +1 -1
  47. package/dist/utils/event-ingestion.js +49 -13
  48. package/dist/utils/event-ingestion.js.map +1 -1
  49. package/dist/utils/hook-injector.d.ts +0 -3
  50. package/dist/utils/hook-injector.d.ts.map +1 -1
  51. package/dist/utils/hook-injector.js +59 -4
  52. package/dist/utils/hook-injector.js.map +1 -1
  53. package/dist/utils/server-init.d.ts +10 -0
  54. package/dist/utils/server-init.d.ts.map +1 -0
  55. package/dist/utils/server-init.js +40 -0
  56. package/dist/utils/server-init.js.map +1 -0
  57. package/package.json +1 -1
@@ -1,33 +1,75 @@
1
1
  export function createPostgresProvider(options) {
2
- const { client, tableName = 'auth_events', schema = 'public' } = options;
2
+ const { client, tableName = 'auth_events', schema = 'public', clientType } = options;
3
+ // Validate client is provided
4
+ if (!client) {
5
+ throw new Error('Postgres client is required. Provide a pg Pool, Client, or Prisma client instance.');
6
+ }
7
+ let actualClient = client;
8
+ if (clientType === 'drizzle') {
9
+ if (client.$client) {
10
+ actualClient = client.$client;
11
+ }
12
+ else if (client.client) {
13
+ // Fallback for older Drizzle versions
14
+ actualClient = client.client;
15
+ }
16
+ }
17
+ const hasQuery = actualClient?.query || client?.query;
18
+ const hasExecuteRaw = client?.$executeRaw;
19
+ const hasExecute = client?.execute;
20
+ const hasUnsafe = actualClient?.unsafe || client?.$client?.unsafe || client?.unsafe;
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).';
27
+ throw new Error(errorMessage);
28
+ }
3
29
  // Ensure table exists
4
30
  const ensureTable = async () => {
5
31
  if (!client)
6
32
  return;
7
33
  try {
8
- // Support different Postgres client types (pg, postgres.js, etc.)
9
- const queryFn = client.query || (typeof client === 'function' ? client : null);
10
- if (!queryFn) {
11
- console.warn(`⚠️ Postgres client doesn't support query method. Table ${schema}.${tableName} must be created manually.`);
12
- return;
13
- }
14
- // Support Prisma client ($executeRaw) or standard pg client (query)
34
+ // Support Prisma client ($executeRaw), Drizzle instance ($client), or standard pg client (query)
15
35
  let executeQuery;
16
- if (client.$executeRaw) {
36
+ if (clientType === 'prisma' || client.$executeRaw) {
17
37
  // Prisma client
18
38
  executeQuery = async (query) => {
19
39
  return await client.$executeRawUnsafe(query);
20
40
  };
21
41
  }
42
+ else if (clientType === 'drizzle') {
43
+ // Drizzle client - use $client which exposes the underlying client
44
+ if (actualClient?.unsafe) {
45
+ // postgres-js client via db.$client.unsafe()
46
+ executeQuery = async (query) => {
47
+ return await actualClient.unsafe(query);
48
+ };
49
+ }
50
+ else if (actualClient?.query) {
51
+ // pg Pool/Client via db.$client.query()
52
+ executeQuery = async (query) => {
53
+ return await actualClient.query(query);
54
+ };
55
+ }
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(', ')}`);
58
+ }
59
+ }
60
+ else if (actualClient?.query) {
61
+ // Standard pg client (Pool or Client)
62
+ executeQuery = async (query) => {
63
+ return await actualClient.query(query);
64
+ };
65
+ }
22
66
  else if (client.query) {
23
- // Standard pg client
24
67
  executeQuery = async (query) => {
25
68
  return await client.query(query);
26
69
  };
27
70
  }
28
71
  else {
29
- console.warn(`⚠️ Postgres client doesn't support $executeRaw or query method. Table ${schema}.${tableName} must be created manually.`);
30
- return;
72
+ throw new Error(`Postgres client doesn't support $executeRaw or query method. Available properties: ${Object.keys(client).join(', ')}`);
31
73
  }
32
74
  // Use CREATE TABLE IF NOT EXISTS (simpler and more reliable)
33
75
  const createTableQuery = `
@@ -60,123 +102,277 @@ export function createPostgresProvider(options) {
60
102
  try {
61
103
  await executeQuery(indexQuery);
62
104
  }
63
- catch (err) {
64
- // Index might already exist, ignore
65
- }
105
+ catch (err) { }
66
106
  }
67
- console.log(`✅ Ensured ${schema}.${tableName} table exists for events`);
68
107
  }
69
108
  catch (error) {
70
- // If table already exists, that's fine
71
109
  if (error?.message?.includes('already exists') || error?.code === '42P07') {
72
110
  return;
73
111
  }
74
112
  console.error(`Failed to ensure ${schema}.${tableName} table:`, error);
75
- // Don't throw - allow provider to work even if table creation fails
76
113
  }
77
114
  };
78
- // Track if table creation is in progress or completed
79
115
  let tableEnsured = false;
80
- let tableEnsuring = false;
116
+ let tableEnsuringPromise = null;
81
117
  const ensureTableSync = async () => {
82
- if (tableEnsured || tableEnsuring)
118
+ if (tableEnsured) {
83
119
  return;
84
- tableEnsuring = true;
85
- try {
86
- await ensureTable();
87
- tableEnsured = true;
88
- }
89
- catch (error) {
90
- console.error('Failed to ensure table:', error);
91
120
  }
92
- finally {
93
- tableEnsuring = false;
121
+ if (tableEnsuringPromise) {
122
+ return tableEnsuringPromise;
94
123
  }
124
+ tableEnsuringPromise = (async () => {
125
+ try {
126
+ await ensureTable();
127
+ tableEnsured = true;
128
+ }
129
+ catch (error) {
130
+ console.error('Failed to ensure table:', error);
131
+ tableEnsuringPromise = null;
132
+ throw error;
133
+ }
134
+ finally {
135
+ tableEnsuringPromise = null;
136
+ }
137
+ })();
138
+ return tableEnsuringPromise;
95
139
  };
96
- // Call ensureTable asynchronously (don't block initialization)
97
140
  ensureTableSync().catch(console.error);
98
141
  return {
99
142
  async ingest(event) {
100
- // Ensure table exists before ingesting
101
- if (!tableEnsured) {
102
- await ensureTableSync();
103
- }
104
- // Support Prisma client ($executeRaw) or standard pg client (query/Pool)
105
- if (client.$executeRaw) {
106
- // Prisma client - use $executeRawUnsafe for parameterized queries
143
+ await ensureTableSync();
144
+ if (clientType === 'prisma' || client.$executeRaw) {
107
145
  const query = `
108
146
  INSERT INTO ${schema}.${tableName}
109
147
  (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
110
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'})
111
149
  `;
112
- await client.$executeRawUnsafe(query);
150
+ try {
151
+ await client.$executeRawUnsafe(query);
152
+ }
153
+ catch (error) {
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') {
156
+ tableEnsured = false;
157
+ await ensureTableSync();
158
+ try {
159
+ await client.$executeRawUnsafe(query);
160
+ return;
161
+ }
162
+ catch (retryError) {
163
+ console.error(`Retry after table creation also failed:`, retryError);
164
+ throw retryError;
165
+ }
166
+ }
167
+ throw error;
168
+ }
113
169
  }
114
- else if (client.query) {
115
- // Standard pg client (Pool or Client) - use parameterized queries for safety
116
- await client.query(`INSERT INTO ${schema}.${tableName}
117
- (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
118
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
119
- event.id,
120
- event.type,
121
- event.timestamp,
122
- event.status || 'success',
123
- event.userId || null,
124
- event.sessionId || null,
125
- event.organizationId || null,
126
- JSON.stringify(event.metadata || {}),
127
- event.ipAddress || null,
128
- event.userAgent || null,
129
- event.source,
130
- event.display?.message || null,
131
- event.display?.severity || null,
132
- ]);
170
+ else if ((actualClient && (actualClient.query || actualClient.unsafe)) ||
171
+ client.query ||
172
+ client.unsafe) {
173
+ // Standard pg client (Pool/Client with query) or postgres-js client (with unsafe) or Drizzle with underlying client
174
+ const queryClient = actualClient || client;
175
+ const useUnsafe = queryClient.unsafe && !queryClient.query; // Use unsafe if postgres-js (has unsafe but no query)
176
+ if (useUnsafe) {
177
+ // postgres-js client - use unsafe() for raw SQL
178
+ const query = `
179
+ INSERT INTO ${schema}.${tableName}
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'})
182
+ `;
183
+ try {
184
+ await queryClient.unsafe(query);
185
+ }
186
+ catch (error) {
187
+ console.error(`Failed to insert event (${event.type}) into ${schema}.${tableName}:`, error);
188
+ if (error.code === '42P01') {
189
+ tableEnsured = false;
190
+ await ensureTableSync();
191
+ try {
192
+ await queryClient.unsafe(query);
193
+ return;
194
+ }
195
+ catch (retryError) {
196
+ console.error(`Retry after table creation also failed:`, retryError);
197
+ throw retryError;
198
+ }
199
+ }
200
+ throw error;
201
+ }
202
+ }
203
+ else {
204
+ // pg Pool/Client - use parameterized queries
205
+ try {
206
+ await queryClient.query(`INSERT INTO ${schema}.${tableName}
207
+ (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
208
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
209
+ event.id,
210
+ event.type,
211
+ event.timestamp,
212
+ event.status || 'success',
213
+ event.userId || null,
214
+ event.sessionId || null,
215
+ event.organizationId || null,
216
+ JSON.stringify(event.metadata || {}),
217
+ event.ipAddress || null,
218
+ event.userAgent || null,
219
+ event.source,
220
+ event.display?.message || null,
221
+ event.display?.severity || null,
222
+ ]);
223
+ }
224
+ catch (error) {
225
+ console.error(`Failed to insert event (${event.type}) into ${schema}.${tableName}:`, error);
226
+ if (error.code === '42P01') {
227
+ tableEnsured = false;
228
+ await ensureTableSync();
229
+ try {
230
+ await queryClient.query(`INSERT INTO ${schema}.${tableName}
231
+ (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
232
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
233
+ event.id,
234
+ event.type,
235
+ event.timestamp,
236
+ event.status || 'success',
237
+ event.userId || null,
238
+ event.sessionId || null,
239
+ event.organizationId || null,
240
+ JSON.stringify(event.metadata || {}),
241
+ event.ipAddress || null,
242
+ event.userAgent || null,
243
+ event.source,
244
+ event.display?.message || null,
245
+ event.display?.severity || null,
246
+ ]);
247
+ return;
248
+ }
249
+ catch (retryError) {
250
+ console.error(`Retry after table creation also failed:`, retryError);
251
+ throw retryError;
252
+ }
253
+ }
254
+ if (error.code === 'ECONNREFUSED' ||
255
+ error.code === 'ETIMEDOUT' ||
256
+ error.message?.includes('Connection terminated')) {
257
+ if (client.end) {
258
+ console.warn(`⚠️ Connection error with pg Pool. The pool will retry automatically on next query.`);
259
+ }
260
+ }
261
+ throw error;
262
+ }
263
+ }
264
+ }
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.');
133
267
  }
134
268
  },
135
269
  async ingestBatch(events) {
136
270
  if (events.length === 0)
137
271
  return;
138
- // Support Prisma client ($executeRaw) or standard pg client (query)
139
- if (client.$executeRaw) {
272
+ await ensureTableSync();
273
+ // Support Prisma client ($executeRaw), Drizzle instance, or standard pg client (query)
274
+ if (clientType === 'prisma' || client.$executeRaw) {
140
275
  // Prisma client - use $executeRawUnsafe for batch inserts
141
- const values = events
142
- .map((event) => `('${event.id}', '${event.type}', '${event.timestamp.toISOString()}', '${event.status || 'success'}', ${event.userId ? `'${event.userId}'` : 'NULL'}, ${event.sessionId ? `'${event.sessionId}'` : 'NULL'}, ${event.organizationId ? `'${event.organizationId}'` : 'NULL'}, '${JSON.stringify(event.metadata || {}).replace(/'/g, "''")}'::jsonb, ${event.ipAddress ? `'${event.ipAddress}'` : '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'})`)
143
- .join(', ');
144
- const query = `
145
- INSERT INTO ${schema}.${tableName}
146
- (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
147
- VALUES ${values}
148
- `;
149
- await client.$executeRawUnsafe(query);
276
+ const CHUNK_SIZE = 500; // Reasonable chunk size for string-based queries
277
+ for (let i = 0; i < events.length; i += CHUNK_SIZE) {
278
+ const chunk = events.slice(i, i + CHUNK_SIZE);
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(', ');
282
+ const query = `
283
+ INSERT INTO ${schema}.${tableName}
284
+ (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
285
+ VALUES ${values}
286
+ `;
287
+ try {
288
+ await client.$executeRawUnsafe(query);
289
+ }
290
+ catch (error) {
291
+ console.error(`Failed to insert batch chunk (${chunk.length} events):`, error);
292
+ throw error;
293
+ }
294
+ }
150
295
  }
151
- else if (client.query) {
152
- // Standard pg client
153
- const values = events
154
- .map((_, i) => {
155
- const base = i * 13;
156
- 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})`;
157
- })
158
- .join(', ');
159
- const query = `
160
- INSERT INTO ${schema}.${tableName}
161
- (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
162
- VALUES ${values}
163
- `;
164
- const params = events.flatMap((event) => [
165
- event.id,
166
- event.type,
167
- event.timestamp,
168
- event.status || 'success',
169
- event.userId || null,
170
- event.sessionId || null,
171
- event.organizationId || null,
172
- JSON.stringify(event.metadata || {}),
173
- event.ipAddress || null,
174
- event.userAgent || null,
175
- event.source,
176
- event.display?.message || null,
177
- event.display?.severity || null,
178
- ]);
179
- await client.query(query, params);
296
+ else if ((actualClient && (actualClient.query || actualClient.unsafe)) ||
297
+ client.query ||
298
+ client.unsafe) {
299
+ // Standard pg client (Pool/Client with query) or postgres-js client (with unsafe) or Drizzle with underlying client
300
+ const batchQueryClient = actualClient || client;
301
+ const useUnsafe = batchQueryClient.unsafe && !batchQueryClient.query; // Use unsafe if postgres-js
302
+ if (useUnsafe) {
303
+ // postgres-js client - use unsafe() for raw SQL (similar to Prisma)
304
+ const CHUNK_SIZE = 500; // Reasonable chunk size for string-based queries
305
+ for (let i = 0; i < events.length; i += CHUNK_SIZE) {
306
+ const chunk = events.slice(i, i + CHUNK_SIZE);
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(', ');
310
+ const query = `
311
+ INSERT INTO ${schema}.${tableName}
312
+ (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
313
+ VALUES ${values}
314
+ `;
315
+ try {
316
+ await batchQueryClient.unsafe(query);
317
+ }
318
+ catch (error) {
319
+ console.error(`Failed to insert batch chunk (${chunk.length} events):`, error);
320
+ throw error;
321
+ }
322
+ }
323
+ }
324
+ else {
325
+ // pg Pool/Client - use parameterized queries
326
+ const PARAMS_PER_EVENT = 13;
327
+ const MAX_PARAMS = 65535;
328
+ const CHUNK_SIZE = Math.floor(MAX_PARAMS / PARAMS_PER_EVENT) - 1; // ~5000, but use 1000 for safety
329
+ for (let chunkStart = 0; chunkStart < events.length; chunkStart += CHUNK_SIZE) {
330
+ const chunk = events.slice(chunkStart, chunkStart + CHUNK_SIZE);
331
+ const values = chunk
332
+ .map((_, i) => {
333
+ const base = i * PARAMS_PER_EVENT;
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
+ })
336
+ .join(', ');
337
+ const query = `
338
+ INSERT INTO ${schema}.${tableName}
339
+ (id, type, timestamp, status, user_id, session_id, organization_id, metadata, ip_address, user_agent, source, display_message, display_severity)
340
+ VALUES ${values}
341
+ `;
342
+ const params = chunk.flatMap((event) => [
343
+ event.id,
344
+ event.type,
345
+ event.timestamp,
346
+ event.status || 'success',
347
+ event.userId || null,
348
+ event.sessionId || null,
349
+ event.organizationId || null,
350
+ JSON.stringify(event.metadata || {}),
351
+ event.ipAddress || null,
352
+ event.userAgent || null,
353
+ event.source,
354
+ event.display?.message || null,
355
+ event.display?.severity || null,
356
+ ]);
357
+ try {
358
+ await batchQueryClient.query(query, params);
359
+ }
360
+ catch (error) {
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')) {
365
+ if (client.end) {
366
+ console.warn(`⚠️ Connection error with pg Pool. The pool will retry automatically on next query.`);
367
+ }
368
+ }
369
+ throw error;
370
+ }
371
+ }
372
+ }
373
+ }
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.');
180
376
  }
181
377
  },
182
378
  async query(options) {
@@ -310,7 +506,6 @@ export function createPostgresProvider(options) {
310
506
  };
311
507
  }
312
508
  catch (error) {
313
- // If table doesn't exist, return empty result instead of throwing
314
509
  if (error?.message?.includes('does not exist') || error?.code === '42P01') {
315
510
  return {
316
511
  events: [],
@@ -386,7 +581,6 @@ export function createClickHouseProvider(options) {
386
581
  tableEnsuring = false;
387
582
  }
388
583
  };
389
- // Call ensureTable asynchronously (don't block initialization)
390
584
  ensureTableSync().catch(console.error);
391
585
  const ingestBatchFn = async (events) => {
392
586
  if (events.length === 0)
@@ -420,7 +614,6 @@ export function createClickHouseProvider(options) {
420
614
  console.log(`✅ Inserted ${rows.length} event(s) into ClickHouse ${tableFullName}`);
421
615
  }
422
616
  else {
423
- // Fallback: use INSERT query
424
617
  const values = rows
425
618
  .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'})`)
426
619
  .join(', ');
@@ -482,7 +675,6 @@ export function createClickHouseProvider(options) {
482
675
  tableExists = false;
483
676
  }
484
677
  }
485
- // If table doesn't exist, try to create it
486
678
  if (!tableExists) {
487
679
  const createTableQuery = `
488
680
  CREATE TABLE IF NOT EXISTS ${tableFullName} (
@@ -512,7 +704,6 @@ export function createClickHouseProvider(options) {
512
704
  }
513
705
  }
514
706
  else {
515
- // Table exists, check if status column exists
516
707
  try {
517
708
  const checkColumnQuery = `
518
709
  SELECT count() as exists
@@ -557,7 +748,6 @@ export function createClickHouseProvider(options) {
557
748
  }
558
749
  catch (alterError) {
559
750
  console.warn(`Failed to add status column to ${tableFullName}:`, alterError);
560
- // Continue anyway - we'll handle missing column in query
561
751
  }
562
752
  }
563
753
  }
@@ -588,7 +778,6 @@ export function createClickHouseProvider(options) {
588
778
  }
589
779
  const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '';
590
780
  const orderDirection = sort === 'desc' ? 'DESC' : 'ASC';
591
- // Try to query with status column first, fallback if it doesn't exist
592
781
  let query = `
593
782
  SELECT id, type, timestamp, status, user_id, session_id, organization_id,
594
783
  metadata, ip_address, user_agent, source, display_message, display_severity
@@ -613,12 +802,10 @@ export function createClickHouseProvider(options) {
613
802
  }
614
803
  }
615
804
  catch (error) {
616
- // If error is about missing status column, retry without it
617
805
  if (error?.message?.includes('Unknown expression identifier') &&
618
806
  error?.message?.includes('status')) {
619
807
  console.warn(`Status column not found in ${tableFullName}, querying without it`);
620
808
  hasStatusColumn = false;
621
- // Retry query without status column
622
809
  query = `
623
810
  SELECT id, type, timestamp, user_id, session_id, organization_id,
624
811
  metadata, ip_address, user_agent, source, display_message, display_severity
@@ -659,14 +846,13 @@ export function createClickHouseProvider(options) {
659
846
  throw error;
660
847
  }
661
848
  }
662
- // Handle case where result might be an array or object
663
849
  const rows = Array.isArray(result) ? result : result?.data || [];
664
850
  const hasMore = rows.length > limit;
665
851
  const events = rows.slice(0, limit).map((row) => ({
666
852
  id: row.id,
667
853
  type: row.type,
668
854
  timestamp: new Date(row.timestamp),
669
- status: hasStatusColumn ? row.status || 'success' : 'success', // Default to 'success' if column doesn't exist
855
+ status: hasStatusColumn ? row.status || 'success' : 'success',
670
856
  userId: row.user_id || undefined,
671
857
  sessionId: row.session_id || undefined,
672
858
  organizationId: row.organization_id || undefined,
@@ -710,7 +896,6 @@ export function createHttpProvider(options) {
710
896
  }
711
897
  export function createStorageProvider(options) {
712
898
  const { adapter, tableName = 'auth_events' } = options;
713
- // Ensure table exists (for Prisma/Drizzle adapters)
714
899
  const ensureTable = async () => {
715
900
  if (!adapter)
716
901
  return;