@revealui/db 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +137 -0
  3. package/dist/audit-store.d.ts +56 -0
  4. package/dist/audit-store.d.ts.map +1 -0
  5. package/dist/audit-store.js +120 -0
  6. package/dist/audit-store.js.map +1 -0
  7. package/dist/client/index.d.ts +214 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +396 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/types.d.ts +109 -0
  12. package/dist/client/types.d.ts.map +1 -0
  13. package/dist/client/types.js +10 -0
  14. package/dist/client/types.js.map +1 -0
  15. package/dist/crypto.d.ts +27 -0
  16. package/dist/crypto.d.ts.map +1 -0
  17. package/dist/crypto.js +68 -0
  18. package/dist/crypto.js.map +1 -0
  19. package/dist/index.d.ts +29 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +31 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/log-transport.d.ts +20 -0
  24. package/dist/log-transport.d.ts.map +1 -0
  25. package/dist/log-transport.js +49 -0
  26. package/dist/log-transport.js.map +1 -0
  27. package/dist/pool.d.ts +36 -0
  28. package/dist/pool.d.ts.map +1 -0
  29. package/dist/pool.js +218 -0
  30. package/dist/pool.js.map +1 -0
  31. package/dist/queries/boards.d.ts +138 -0
  32. package/dist/queries/boards.d.ts.map +1 -0
  33. package/dist/queries/boards.js +87 -0
  34. package/dist/queries/boards.js.map +1 -0
  35. package/dist/queries/code-provenance.d.ts +250 -0
  36. package/dist/queries/code-provenance.d.ts.map +1 -0
  37. package/dist/queries/code-provenance.js +130 -0
  38. package/dist/queries/code-provenance.js.map +1 -0
  39. package/dist/queries/optimized-queries.d.ts +89 -0
  40. package/dist/queries/optimized-queries.d.ts.map +1 -0
  41. package/dist/queries/optimized-queries.js +371 -0
  42. package/dist/queries/optimized-queries.js.map +1 -0
  43. package/dist/queries/ticket-comments.d.ts +37 -0
  44. package/dist/queries/ticket-comments.d.ts.map +1 -0
  45. package/dist/queries/ticket-comments.js +52 -0
  46. package/dist/queries/ticket-comments.js.map +1 -0
  47. package/dist/queries/ticket-labels.d.ts +69 -0
  48. package/dist/queries/ticket-labels.d.ts.map +1 -0
  49. package/dist/queries/ticket-labels.js +51 -0
  50. package/dist/queries/ticket-labels.js.map +1 -0
  51. package/dist/queries/tickets.d.ts +301 -0
  52. package/dist/queries/tickets.d.ts.map +1 -0
  53. package/dist/queries/tickets.js +89 -0
  54. package/dist/queries/tickets.js.map +1 -0
  55. package/dist/queries/todos.d.ts +37 -0
  56. package/dist/queries/todos.d.ts.map +1 -0
  57. package/dist/queries/todos.js +37 -0
  58. package/dist/queries/todos.js.map +1 -0
  59. package/dist/schema/agents.d.ts +1413 -0
  60. package/dist/schema/agents.d.ts.map +1 -0
  61. package/dist/schema/agents.js +207 -0
  62. package/dist/schema/agents.js.map +1 -0
  63. package/dist/schema/api-keys.d.ts +298 -0
  64. package/dist/schema/api-keys.d.ts.map +1 -0
  65. package/dist/schema/api-keys.js +53 -0
  66. package/dist/schema/api-keys.js.map +1 -0
  67. package/dist/schema/app-logs.d.ts +168 -0
  68. package/dist/schema/app-logs.d.ts.map +1 -0
  69. package/dist/schema/app-logs.js +25 -0
  70. package/dist/schema/app-logs.js.map +1 -0
  71. package/dist/schema/audit-log.d.ts +174 -0
  72. package/dist/schema/audit-log.d.ts.map +1 -0
  73. package/dist/schema/audit-log.js +37 -0
  74. package/dist/schema/audit-log.js.map +1 -0
  75. package/dist/schema/cms.d.ts +1015 -0
  76. package/dist/schema/cms.d.ts.map +1 -0
  77. package/dist/schema/cms.js +137 -0
  78. package/dist/schema/cms.js.map +1 -0
  79. package/dist/schema/code-provenance.d.ts +488 -0
  80. package/dist/schema/code-provenance.d.ts.map +1 -0
  81. package/dist/schema/code-provenance.js +72 -0
  82. package/dist/schema/code-provenance.js.map +1 -0
  83. package/dist/schema/collab-edits.d.ts +165 -0
  84. package/dist/schema/collab-edits.d.ts.map +1 -0
  85. package/dist/schema/collab-edits.js +21 -0
  86. package/dist/schema/collab-edits.js.map +1 -0
  87. package/dist/schema/crdt-operations.d.ts +153 -0
  88. package/dist/schema/crdt-operations.d.ts.map +1 -0
  89. package/dist/schema/crdt-operations.js +30 -0
  90. package/dist/schema/crdt-operations.js.map +1 -0
  91. package/dist/schema/error-events.d.ts +223 -0
  92. package/dist/schema/error-events.d.ts.map +1 -0
  93. package/dist/schema/error-events.js +44 -0
  94. package/dist/schema/error-events.js.map +1 -0
  95. package/dist/schema/index.d.ts +130 -0
  96. package/dist/schema/index.d.ts.map +1 -0
  97. package/dist/schema/index.js +310 -0
  98. package/dist/schema/index.js.map +1 -0
  99. package/dist/schema/licenses.d.ts +189 -0
  100. package/dist/schema/licenses.d.ts.map +1 -0
  101. package/dist/schema/licenses.js +39 -0
  102. package/dist/schema/licenses.js.map +1 -0
  103. package/dist/schema/node-ids.d.ts +122 -0
  104. package/dist/schema/node-ids.d.ts.map +1 -0
  105. package/dist/schema/node-ids.js +25 -0
  106. package/dist/schema/node-ids.js.map +1 -0
  107. package/dist/schema/pages.d.ts +488 -0
  108. package/dist/schema/pages.d.ts.map +1 -0
  109. package/dist/schema/pages.js +70 -0
  110. package/dist/schema/pages.js.map +1 -0
  111. package/dist/schema/password-reset-tokens.d.ts +137 -0
  112. package/dist/schema/password-reset-tokens.d.ts.map +1 -0
  113. package/dist/schema/password-reset-tokens.js +26 -0
  114. package/dist/schema/password-reset-tokens.js.map +1 -0
  115. package/dist/schema/query.d.ts +11 -0
  116. package/dist/schema/query.d.ts.map +1 -0
  117. package/dist/schema/query.js +11 -0
  118. package/dist/schema/query.js.map +1 -0
  119. package/dist/schema/rate-limits.d.ts +212 -0
  120. package/dist/schema/rate-limits.d.ts.map +1 -0
  121. package/dist/schema/rate-limits.js +38 -0
  122. package/dist/schema/rate-limits.js.map +1 -0
  123. package/dist/schema/rest.d.ts +31 -0
  124. package/dist/schema/rest.d.ts.map +1 -0
  125. package/dist/schema/rest.js +37 -0
  126. package/dist/schema/rest.js.map +1 -0
  127. package/dist/schema/sites.d.ts +365 -0
  128. package/dist/schema/sites.d.ts.map +1 -0
  129. package/dist/schema/sites.js +62 -0
  130. package/dist/schema/sites.js.map +1 -0
  131. package/dist/schema/tickets.d.ts +1118 -0
  132. package/dist/schema/tickets.d.ts.map +1 -0
  133. package/dist/schema/tickets.js +150 -0
  134. package/dist/schema/tickets.js.map +1 -0
  135. package/dist/schema/todos.d.ts +98 -0
  136. package/dist/schema/todos.d.ts.map +1 -0
  137. package/dist/schema/todos.js +12 -0
  138. package/dist/schema/todos.js.map +1 -0
  139. package/dist/schema/users.d.ts +503 -0
  140. package/dist/schema/users.d.ts.map +1 -0
  141. package/dist/schema/users.js +75 -0
  142. package/dist/schema/users.js.map +1 -0
  143. package/dist/schema/vector.d.ts +9 -0
  144. package/dist/schema/vector.d.ts.map +1 -0
  145. package/dist/schema/vector.js +9 -0
  146. package/dist/schema/vector.js.map +1 -0
  147. package/dist/schema/waitlist.d.ts +151 -0
  148. package/dist/schema/waitlist.d.ts.map +1 -0
  149. package/dist/schema/waitlist.js +17 -0
  150. package/dist/schema/waitlist.js.map +1 -0
  151. package/dist/schema/yjs-documents.d.ts +116 -0
  152. package/dist/schema/yjs-documents.d.ts.map +1 -0
  153. package/dist/schema/yjs-documents.js +15 -0
  154. package/dist/schema/yjs-documents.js.map +1 -0
  155. package/dist/types/database.d.ts +740 -0
  156. package/dist/types/database.d.ts.map +1 -0
  157. package/dist/types/database.js +151 -0
  158. package/dist/types/database.js.map +1 -0
  159. package/dist/types/discover.d.ts +83 -0
  160. package/dist/types/discover.d.ts.map +1 -0
  161. package/dist/types/discover.js +271 -0
  162. package/dist/types/discover.js.map +1 -0
  163. package/dist/types/extract-relationships.d.ts +115 -0
  164. package/dist/types/extract-relationships.d.ts.map +1 -0
  165. package/dist/types/extract-relationships.js +455 -0
  166. package/dist/types/extract-relationships.js.map +1 -0
  167. package/dist/types/generate-contracts.d.ts +19 -0
  168. package/dist/types/generate-contracts.d.ts.map +1 -0
  169. package/dist/types/generate-contracts.js +128 -0
  170. package/dist/types/generate-contracts.js.map +1 -0
  171. package/dist/types/generate-zod-schemas.d.ts +20 -0
  172. package/dist/types/generate-zod-schemas.d.ts.map +1 -0
  173. package/dist/types/generate-zod-schemas.js +128 -0
  174. package/dist/types/generate-zod-schemas.js.map +1 -0
  175. package/dist/types/generate.d.ts +17 -0
  176. package/dist/types/generate.d.ts.map +1 -0
  177. package/dist/types/generate.js +298 -0
  178. package/dist/types/generate.js.map +1 -0
  179. package/dist/types/index.d.ts +19 -0
  180. package/dist/types/index.d.ts.map +1 -0
  181. package/dist/types/index.js +19 -0
  182. package/dist/types/index.js.map +1 -0
  183. package/dist/types/introspect.d.ts +75 -0
  184. package/dist/types/introspect.d.ts.map +1 -0
  185. package/dist/types/introspect.js +187 -0
  186. package/dist/types/introspect.js.map +1 -0
  187. package/dist/types/stripe-schema.d.ts +893 -0
  188. package/dist/types/stripe-schema.d.ts.map +1 -0
  189. package/dist/types/stripe-schema.js +112 -0
  190. package/dist/types/stripe-schema.js.map +1 -0
  191. package/package.json +154 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * @revealui/db - Database Client
3
+ *
4
+ * Provides a configured Drizzle ORM client for PostgreSQL databases.
5
+ * Supports dual database architecture:
6
+ * - REST Database (NeonDB): Uses @neondatabase/serverless with drizzle-orm/neon-http
7
+ * - Vector Database (Supabase): Uses postgres-js with drizzle-orm/postgres-js
8
+ *
9
+ * This dual-driver approach avoids the Neon driver's compatibility issue with Supabase,
10
+ * where it incorrectly transforms Supabase hostnames (aws-0-*.pooler.supabase.com → api.pooler.supabase.com).
11
+ *
12
+ * Connection String Format:
13
+ * - NeonDB: postgresql://...@neon.tech/...
14
+ * - Supabase: postgresql://...@*.supabase.co:6543/postgres (transaction pooler)
15
+ * - Supabase: postgresql://...@*.supabase.co:5432/postgres (direct/session pooler)
16
+ *
17
+ * Reference:
18
+ * - Neon: https://orm.drizzle.team/docs/connect-neon
19
+ * - Supabase: https://orm.drizzle.team/docs/tutorials/drizzle-with-supabase
20
+ */
21
+ import { neon } from '@neondatabase/serverless';
22
+ // Import config module (ESM)
23
+ // Config uses proxy for lazy loading, so import is safe - validation only happens on property access
24
+ // Direct ESM import - the Proxy ensures no validation occurs until properties are accessed
25
+ import configModule from '@revealui/config';
26
+ import { getSSLConfig } from '@revealui/utils/database';
27
+ import { drizzle as drizzleNeon } from 'drizzle-orm/neon-http';
28
+ import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';
29
+ import { Pool } from 'pg';
30
+ import * as schema from '../schema/index.js'; // Full schema for backward compatibility
31
+ import * as restSchema from '../schema/rest.js';
32
+ import * as vectorSchema from '../schema/vector.js';
33
+ // =============================================================================
34
+ // Client Creation
35
+ // =============================================================================
36
+ /**
37
+ * Creates a Drizzle database client for Neon Postgres.
38
+ *
39
+ * Uses the official Drizzle pattern for neon-http driver:
40
+ * https://orm.drizzle.team/docs/connect-neon
41
+ *
42
+ * @param config - Database configuration
43
+ * @param dbSchema - Optional schema to use (defaults to full schema for backward compatibility)
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { createClient } from '@revealui/db/client'
48
+ *
49
+ * const db = createClient({
50
+ * connectionString: process.env.POSTGRES_URL!,
51
+ * })
52
+ *
53
+ * // Query users
54
+ * const users = await db.query.users.findMany()
55
+ * ```
56
+ */
57
+ /**
58
+ * Detects if a connection string requires node-postgres driver.
59
+ * Returns true for Supabase connections and localhost/test connections.
60
+ * Supabase connection strings contain '.supabase.co' or 'pooler.supabase.com'.
61
+ * Localhost connections are used for testing and development.
62
+ */
63
+ function isSupabaseConnection(connectionString) {
64
+ return (connectionString.includes('.supabase.co') ||
65
+ connectionString.includes('pooler.supabase.com') ||
66
+ connectionString.includes('localhost') ||
67
+ connectionString.includes('127.0.0.1'));
68
+ }
69
+ /**
70
+ * Creates a Drizzle database client, automatically selecting the appropriate driver:
71
+ * - Supabase/localhost connections: Uses node-postgres with drizzle-orm/node-postgres
72
+ * - NeonDB connections: Uses @neondatabase/serverless with drizzle-orm/neon-http
73
+ *
74
+ * This dual-driver approach fixes the Neon driver's compatibility issue with Supabase,
75
+ * where it incorrectly transforms Supabase hostnames. It also enables local testing
76
+ * with localhost PostgreSQL databases.
77
+ *
78
+ * @param config - Database configuration
79
+ * @param dbSchema - Optional schema to use (defaults to full schema for backward compatibility)
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * import { createClient } from '@revealui/db/client'
84
+ *
85
+ * // Automatically uses node-postgres for Supabase
86
+ * const supabaseDb = createClient({
87
+ * connectionString: process.env.DATABASE_URL!, // Supabase URL
88
+ * })
89
+ *
90
+ * // Automatically uses node-postgres for localhost (testing)
91
+ * const testDb = createClient({
92
+ * connectionString: 'postgresql://test:test@localhost:5432/test',
93
+ * })
94
+ *
95
+ * // Automatically uses Neon driver for NeonDB
96
+ * const neonDb = createClient({
97
+ * connectionString: process.env.POSTGRES_URL!, // NeonDB URL
98
+ * })
99
+ * ```
100
+ */
101
+ export function createClient(config, dbSchema = schema) {
102
+ const isSupabase = isSupabaseConnection(config.connectionString);
103
+ if (isSupabase) {
104
+ // Use pg for Supabase connections
105
+ // This avoids the Neon driver's hostname transformation bug
106
+ const pool = new Pool({
107
+ connectionString: config.connectionString,
108
+ ssl: getSSLConfig(config.connectionString), // Auto-detect SSL from connection string
109
+ max: 10, // Connection limit
110
+ idleTimeoutMillis: 30_000, // 30 seconds
111
+ connectionTimeoutMillis: 10_000, // 10 seconds
112
+ });
113
+ // Track pool and register cleanup
114
+ const poolId = `pool-${activePools.size + 1}`;
115
+ activePools.set(poolId, pool);
116
+ registerPoolCleanup();
117
+ return drizzlePg({
118
+ client: pool,
119
+ schema: dbSchema,
120
+ logger: config.logger ?? false,
121
+ });
122
+ }
123
+ else {
124
+ // Use Neon serverless driver for NeonDB connections
125
+ const sql = neon(config.connectionString);
126
+ return drizzleNeon({
127
+ client: sql,
128
+ schema: dbSchema,
129
+ logger: config.logger ?? false,
130
+ });
131
+ }
132
+ }
133
+ // =============================================================================
134
+ // Global Client (for singleton usage)
135
+ // =============================================================================
136
+ let restClient = null;
137
+ let vectorClient = null;
138
+ // Track all pg.Pool instances for monitoring and cleanup
139
+ const activePools = new Map();
140
+ // Register cleanup handler
141
+ let cleanupHandlerRegistered = false;
142
+ function registerPoolCleanup() {
143
+ if (cleanupHandlerRegistered)
144
+ return;
145
+ // Monitoring integration removed to avoid circular dependency
146
+ // Application layer should handle cleanup registration instead
147
+ // const monitoring = await getMonitoring()
148
+ // if (monitoring?.registerCleanupHandler) {
149
+ // monitoring.registerCleanupHandler(
150
+ // 'database-pools',
151
+ // async () => {
152
+ // await closeAllPools()
153
+ // },
154
+ // 'Close all database connection pools',
155
+ // 100, // High priority
156
+ // )
157
+ // }
158
+ cleanupHandlerRegistered = true;
159
+ }
160
+ /**
161
+ * Gets or creates a global database client.
162
+ * Supports dual database architecture with separate clients for REST and Vector operations.
163
+ * Uses config module if available, otherwise falls back to process.env for backward compatibility.
164
+ *
165
+ * @param typeOrConnectionString - Database type ('rest' | 'vector') or connection string (legacy API)
166
+ * @returns Database client instance
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * import { getClient } from '@revealui/db/client'
171
+ *
172
+ * // New API: Specify database type
173
+ * const restDb = getClient('rest')
174
+ * const vectorDb = getClient('vector')
175
+ *
176
+ * // Legacy API: Still supported for backward compatibility
177
+ * const db = getClient() // defaults to 'rest'
178
+ * const db2 = getClient('postgresql://...') // uses provided connection string as 'rest'
179
+ * ```
180
+ */
181
+ // Note: DatabaseType | string union is intentional for backward compatibility (allows both type strings and connection strings)
182
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
183
+ export function getClient(typeOrConnectionString) {
184
+ // Legacy API: If first argument is a string and not 'rest' or 'vector', treat as connection string
185
+ if (typeOrConnectionString && typeof typeOrConnectionString === 'string') {
186
+ if (typeOrConnectionString === 'rest' || typeOrConnectionString === 'vector') {
187
+ // New API: Type specified
188
+ const type = typeOrConnectionString;
189
+ return getClientByType(type);
190
+ }
191
+ else if (typeOrConnectionString.startsWith('postgresql://') ||
192
+ typeOrConnectionString.startsWith('postgres://')) {
193
+ // Legacy API: Connection string provided, use as REST client
194
+ if (!restClient) {
195
+ restClient = createClient({ connectionString: typeOrConnectionString });
196
+ }
197
+ return restClient;
198
+ }
199
+ }
200
+ // Default to 'rest' for backward compatibility
201
+ return getClientByType('rest');
202
+ }
203
+ /**
204
+ * Internal function to get client by type
205
+ */
206
+ function getClientByType(type) {
207
+ if (type === 'vector') {
208
+ if (!vectorClient) {
209
+ const url = process.env.DATABASE_URL;
210
+ if (!url || typeof url !== 'string') {
211
+ throw new Error('DATABASE_URL environment variable is required for vector database. ' +
212
+ 'Set DATABASE_URL to your Supabase connection string.');
213
+ }
214
+ vectorClient = createClient({ connectionString: url }, vectorSchema);
215
+ }
216
+ return vectorClient;
217
+ }
218
+ // type === 'rest'
219
+ if (!restClient) {
220
+ // Try to get from config module (ESM - lazy validation via Proxy)
221
+ let url;
222
+ try {
223
+ const configUrl = configModule.database?.url;
224
+ if (typeof configUrl === 'string') {
225
+ url = configUrl;
226
+ }
227
+ }
228
+ catch {
229
+ // Config validation failed or module unavailable - will use process.env fallback
230
+ url = undefined;
231
+ }
232
+ // Fallback to process.env (use || to also catch empty strings)
233
+ url = url || process.env.POSTGRES_URL || process.env.DATABASE_URL;
234
+ if (!url || typeof url !== 'string') {
235
+ throw new Error('Database connection string not provided for REST database. ' +
236
+ 'Either use @revealui/config, or set POSTGRES_URL (or DATABASE_URL) environment variable.');
237
+ }
238
+ restClient = createClient({ connectionString: url }, restSchema);
239
+ }
240
+ return restClient;
241
+ }
242
+ /**
243
+ * Gets or creates the REST database client (NeonDB).
244
+ * Convenience function for accessing the REST database.
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * import { getRestClient } from '@revealui/db/client'
249
+ *
250
+ * const db = getRestClient()
251
+ * const users = await db.query.users.findMany()
252
+ * ```
253
+ */
254
+ export function getRestClient() {
255
+ return getClient('rest');
256
+ }
257
+ /**
258
+ * Gets or creates the Vector database client (Supabase).
259
+ * Convenience function for accessing the vector database.
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * import { getVectorClient } from '@revealui/db/client'
264
+ *
265
+ * const db = getVectorClient()
266
+ * const memories = await db.query.agentMemories.findMany()
267
+ * ```
268
+ */
269
+ export function getVectorClient() {
270
+ return getClient('vector');
271
+ }
272
+ /**
273
+ * Resets the global clients (useful for testing).
274
+ * Clears both REST and Vector client instances.
275
+ */
276
+ export function resetClient() {
277
+ restClient = null;
278
+ vectorClient = null;
279
+ }
280
+ // =============================================================================
281
+ // Pool Monitoring and Cleanup
282
+ // =============================================================================
283
+ /**
284
+ * Gets metrics for all active database connection pools.
285
+ *
286
+ * @returns Array of pool metrics
287
+ *
288
+ * @example
289
+ * ```typescript
290
+ * import { getPoolMetrics } from '@revealui/db/client'
291
+ *
292
+ * const metrics = getPoolMetrics()
293
+ * for (const pool of metrics) {
294
+ * // Log pool statistics
295
+ * logger.info(`${pool.name}: ${pool.totalCount} total, ${pool.idleCount} idle`)
296
+ * }
297
+ * ```
298
+ */
299
+ export function getPoolMetrics() {
300
+ const metrics = [];
301
+ for (const [name, pool] of activePools) {
302
+ metrics.push({
303
+ name,
304
+ totalCount: pool.totalCount,
305
+ idleCount: pool.idleCount,
306
+ waitingCount: pool.waitingCount,
307
+ });
308
+ }
309
+ return metrics;
310
+ }
311
+ /**
312
+ * Closes all active database connection pools.
313
+ * This should be called during graceful shutdown.
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * import { closeAllPools } from '@revealui/db/client'
318
+ *
319
+ * process.on('SIGTERM', async () => {
320
+ * await closeAllPools()
321
+ * process.exit(0)
322
+ * })
323
+ * ```
324
+ */
325
+ export async function closeAllPools() {
326
+ const closePromises = [];
327
+ for (const [_name, pool] of activePools) {
328
+ closePromises.push(pool.end().catch((_error) => {
329
+ // Silently handle pool close errors during shutdown
330
+ // Pool is being removed from activePools regardless
331
+ }));
332
+ }
333
+ await Promise.all(closePromises);
334
+ activePools.clear();
335
+ // Reset global clients
336
+ restClient = null;
337
+ vectorClient = null;
338
+ }
339
+ // =============================================================================
340
+ // Transaction Helper
341
+ // =============================================================================
342
+ /**
343
+ * Execute a database transaction with automatic BEGIN/COMMIT/ROLLBACK.
344
+ *
345
+ * ⚠️ IMPORTANT: Transaction support depends on the database driver:
346
+ * - ✅ Supabase/localhost (pg Pool): Full transaction support
347
+ * - ❌ NeonDB (HTTP driver): Transactions NOT supported
348
+ *
349
+ * The Neon HTTP driver (@neondatabase/serverless with neon-http) does not support
350
+ * transactions because it uses stateless HTTP requests. Each query is independent.
351
+ *
352
+ * For transaction support, use:
353
+ * 1. Supabase with connection pooling (recommended for production)
354
+ * 2. Localhost PostgreSQL (for development/testing)
355
+ * 3. Neon with WebSocket driver (coming in future versions)
356
+ *
357
+ * @param db - Database client (must be created with pg Pool driver)
358
+ * @param fn - Transaction callback that receives a transaction context
359
+ * @returns Result from the transaction callback
360
+ * @throws {Error} If using Neon HTTP driver (no transaction support)
361
+ * @throws {Error} If transaction fails (automatic ROLLBACK is performed)
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * // ✅ Works with Supabase/localhost (pg Pool driver)
366
+ * const supabaseDb = getClient('vector') // Uses pg Pool
367
+ * const result = await withTransaction(supabaseDb, async (tx) => {
368
+ * const site = await tx.insert(sites).values({ ... }).returning()
369
+ * await tx.insert(pages).values({ siteId: site[0].id, ... })
370
+ * return site[0]
371
+ * })
372
+ *
373
+ * // ❌ Throws error with NeonDB (HTTP driver)
374
+ * const neonDb = getClient('rest') // Uses Neon HTTP
375
+ * await withTransaction(neonDb, async (tx) => { ... }) // Error!
376
+ * ```
377
+ */
378
+ export async function withTransaction(db, fn) {
379
+ // Check if this is a pg Pool-based client (supports transactions)
380
+ // The pg Pool client has a 'transaction' method from drizzle-orm/node-postgres
381
+ const hasPgTransaction = 'transaction' in db && typeof db.transaction === 'function';
382
+ if (!hasPgTransaction) {
383
+ throw new Error('Transaction not supported: Database client is using Neon HTTP driver which does not support transactions. ' +
384
+ 'To use transactions, configure your database with Supabase or localhost connection string. ' +
385
+ 'Neon HTTP driver uses stateless requests and cannot maintain transaction state. ' +
386
+ 'See docs/PRODUCTION_BLOCKERS.md for migration guide.');
387
+ }
388
+ // Use Drizzle's built-in transaction API
389
+ // This automatically handles BEGIN/COMMIT/ROLLBACK
390
+ return db.transaction(fn);
391
+ }
392
+ // =============================================================================
393
+ // Re-exports
394
+ // =============================================================================
395
+ export { schema };
396
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Database Client Type Utilities
3
+ *
4
+ * Provides type utilities for working with the database client,
5
+ * including query builder types, transaction types, and other utilities.
6
+ *
7
+ * @module @revealui/db/client/types
8
+ */
9
+ import type { NeonHttpDatabase } from 'drizzle-orm/neon-http';
10
+ import type * as schema from '../schema/index.js';
11
+ import type { Database } from '../types/index.js';
12
+ /**
13
+ * Re-export the centralized Database type for convenience
14
+ */
15
+ export type { Database };
16
+ /**
17
+ * Database client type (Drizzle ORM client)
18
+ *
19
+ * This is the actual database client returned by createClient/getClient.
20
+ */
21
+ export type DatabaseClient = NeonHttpDatabase<typeof schema>;
22
+ /**
23
+ * Extract query result type for a table
24
+ *
25
+ * @template T - The Database type
26
+ * @template N - The table name
27
+ */
28
+ export type QueryResult<T extends Database, N extends keyof T['public']['Tables']> = T['public']['Tables'][N] extends {
29
+ Row: infer R;
30
+ } ? R : never;
31
+ /**
32
+ * Extract query results type for multiple tables
33
+ *
34
+ * @template T - The Database type
35
+ * @template N - Array of table names
36
+ */
37
+ export type QueryResults<T extends Database, N extends Array<keyof T['public']['Tables']>> = {
38
+ [K in N[number]]: T['public']['Tables'][K] extends {
39
+ Row: infer R;
40
+ } ? R : never;
41
+ };
42
+ /**
43
+ * Transaction type for database operations
44
+ *
45
+ * Note: Neon HTTP driver doesn't support true transactions,
46
+ * but this type provides API consistency for future migration.
47
+ */
48
+ export type Transaction = DatabaseClient;
49
+ /**
50
+ * Type-safe query utilities
51
+ *
52
+ * Note: Drizzle ORM already provides excellent type safety through its native API.
53
+ * Use Drizzle's native API directly for type-safe queries - no wrapper interface needed.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { getClient } from '@revealui/db/client'
58
+ * import { users } from '@revealui/db/schema'
59
+ * import { eq } from 'drizzle-orm'
60
+ * import type { Database } from '@revealui/db/types'
61
+ *
62
+ * const db = getClient()
63
+ *
64
+ * // Drizzle provides full type safety
65
+ * const allUsers = await db.query.users.findMany()
66
+ * const user = await db.query.users.findFirst({
67
+ * where: eq(users.id, 'user-123')
68
+ * })
69
+ *
70
+ * // Type-safe inserts
71
+ * const newUser: Database['public']['Tables']['users']['Insert'] = {
72
+ * id: 'user-123',
73
+ * email: 'user@example.com',
74
+ * name: 'User',
75
+ * schemaVersion: '1',
76
+ * type: 'human',
77
+ * }
78
+ * await db.insert(users).values(newUser)
79
+ *
80
+ * // Type-safe updates
81
+ * await db.update(users)
82
+ * .set({ name: 'Updated Name' })
83
+ * .where(eq(users.id, 'user-123'))
84
+ * ```
85
+ *
86
+ * Drizzle's native API is fully type-safe and provides all the functionality
87
+ * you need. No wrapper interface is necessary.
88
+ */
89
+ /**
90
+ * Extract table relationships for a specific table
91
+ *
92
+ * @template T - The Database type
93
+ * @template N - The table name
94
+ */
95
+ export type TableRelationships<T extends Database, N extends keyof T['public']['Tables']> = T['public']['Tables'][N] extends {
96
+ Relationships: infer R;
97
+ } ? R : never;
98
+ /**
99
+ * Helper type to extract all related table names for a table
100
+ *
101
+ * @template T - The Database type
102
+ * @template N - The table name
103
+ */
104
+ export type RelatedTables<T extends Database, N extends keyof T['public']['Tables']> = T['public']['Tables'][N] extends {
105
+ Relationships: infer R;
106
+ } ? R extends ReadonlyArray<{
107
+ referencedRelation: infer Rel;
108
+ }> ? Rel extends keyof T['public']['Tables'] ? Rel : never : never : never;
109
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,KAAK,KAAK,MAAM,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEjD;;GAEG;AACH,YAAY,EAAE,QAAQ,EAAE,CAAA;AAExB;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,gBAAgB,CAAC,OAAO,MAAM,CAAC,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CACrB,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,IACnC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;IAEnC,GAAG,EAAE,MAAM,CAAC,CAAA;CACb,GACG,CAAC,GACD,KAAK,CAAA;AAET;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI;KAC1F,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;QAEjD,GAAG,EAAE,MAAM,CAAC,CAAA;KACb,GACG,CAAC,GACD,KAAK;CACV,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,CAAA;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,CAC5B,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,IACnC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;IAEnC,aAAa,EAAE,MAAM,CAAC,CAAA;CACvB,GACG,CAAC,GACD,KAAK,CAAA;AAET;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CACvB,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,IACnC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;IAEnC,aAAa,EAAE,MAAM,CAAC,CAAA;CACvB,GACG,CAAC,SAAS,aAAa,CAAC;IAAE,kBAAkB,EAAE,MAAM,GAAG,CAAA;CAAE,CAAC,GACxD,GAAG,SAAS,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,GACrC,GAAG,GACH,KAAK,GACP,KAAK,GACP,KAAK,CAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Database Client Type Utilities
3
+ *
4
+ * Provides type utilities for working with the database client,
5
+ * including query builder types, transaction types, and other utilities.
6
+ *
7
+ * @module @revealui/db/client/types
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @revealui/db/crypto — AES-256-GCM envelope encryption for BYOK API keys
3
+ *
4
+ * Uses a Key Encryption Key (KEK) sourced from the REVEALUI_KEK environment
5
+ * variable (64 hex chars = 32 bytes). Each key is encrypted with a random
6
+ * 96-bit IV; the output is a dot-separated base64url string:
7
+ *
8
+ * <iv>.<authTag>.<ciphertext>
9
+ *
10
+ * The auth tag provides tamper detection (GCM authenticated encryption).
11
+ */
12
+ /**
13
+ * Encrypt a plaintext API key using AES-256-GCM.
14
+ * Returns a dot-separated base64url string: `<iv>.<authTag>.<ciphertext>`
15
+ */
16
+ export declare function encryptApiKey(plaintext: string): string;
17
+ /**
18
+ * Decrypt an encrypted API key produced by `encryptApiKey`.
19
+ * Throws if tampered (GCM auth tag mismatch) or if KEK is wrong.
20
+ */
21
+ export declare function decryptApiKey(encrypted: string): string;
22
+ /**
23
+ * Return a redacted hint showing only the last 4 characters of an API key.
24
+ * Safe to store in plaintext and display in the UI.
25
+ */
26
+ export declare function redactApiKey(plaintext: string): string;
27
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkBH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAavD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGtD"}
package/dist/crypto.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @revealui/db/crypto — AES-256-GCM envelope encryption for BYOK API keys
3
+ *
4
+ * Uses a Key Encryption Key (KEK) sourced from the REVEALUI_KEK environment
5
+ * variable (64 hex chars = 32 bytes). Each key is encrypted with a random
6
+ * 96-bit IV; the output is a dot-separated base64url string:
7
+ *
8
+ * <iv>.<authTag>.<ciphertext>
9
+ *
10
+ * The auth tag provides tamper detection (GCM authenticated encryption).
11
+ */
12
+ import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
13
+ const ALGORITHM = 'aes-256-gcm';
14
+ const IV_LENGTH = 12; // 96-bit IV — recommended for AES-GCM
15
+ function getKek() {
16
+ const kekHex = process.env.REVEALUI_KEK;
17
+ if (!kekHex) {
18
+ throw new Error('REVEALUI_KEK environment variable is not set');
19
+ }
20
+ if (kekHex.length !== 64) {
21
+ throw new Error('REVEALUI_KEK must be exactly 64 hex characters (32 bytes / 256 bits)');
22
+ }
23
+ return Buffer.from(kekHex, 'hex');
24
+ }
25
+ /**
26
+ * Encrypt a plaintext API key using AES-256-GCM.
27
+ * Returns a dot-separated base64url string: `<iv>.<authTag>.<ciphertext>`
28
+ */
29
+ export function encryptApiKey(plaintext) {
30
+ const kek = getKek();
31
+ const iv = randomBytes(IV_LENGTH);
32
+ const cipher = createCipheriv(ALGORITHM, kek, iv);
33
+ const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
34
+ const authTag = cipher.getAuthTag();
35
+ return [
36
+ iv.toString('base64url'),
37
+ authTag.toString('base64url'),
38
+ ciphertext.toString('base64url'),
39
+ ].join('.');
40
+ }
41
+ /**
42
+ * Decrypt an encrypted API key produced by `encryptApiKey`.
43
+ * Throws if tampered (GCM auth tag mismatch) or if KEK is wrong.
44
+ */
45
+ export function decryptApiKey(encrypted) {
46
+ const kek = getKek();
47
+ const parts = encrypted.split('.');
48
+ if (parts.length !== 3) {
49
+ throw new Error('Invalid encrypted key format — expected <iv>.<authTag>.<ciphertext>');
50
+ }
51
+ const [ivB64, authTagB64, ciphertextB64] = parts;
52
+ const iv = Buffer.from(ivB64, 'base64url');
53
+ const authTag = Buffer.from(authTagB64, 'base64url');
54
+ const ciphertext = Buffer.from(ciphertextB64, 'base64url');
55
+ const decipher = createDecipheriv(ALGORITHM, kek, iv);
56
+ decipher.setAuthTag(authTag);
57
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
58
+ }
59
+ /**
60
+ * Return a redacted hint showing only the last 4 characters of an API key.
61
+ * Safe to store in plaintext and display in the UI.
62
+ */
63
+ export function redactApiKey(plaintext) {
64
+ if (plaintext.length <= 4)
65
+ return '...';
66
+ return `...${plaintext.slice(-4)}`;
67
+ }
68
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE3E,MAAM,SAAS,GAAG,aAAa,CAAA;AAC/B,MAAM,SAAS,GAAG,EAAE,CAAA,CAAC,sCAAsC;AAE3D,SAAS,MAAM;IACb,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAA;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACjE,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;IACzF,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;IACjC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACpF,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;IACnC,OAAO;QACL,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC;KACjC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAA;IACxF,CAAC;IACD,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,CAAC,GAAG,KAAiC,CAAA;IAC5E,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;IACpD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACrD,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IAC5B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACxF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACpC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @revealui/db - Database Package
3
+ *
4
+ * Provides Drizzle ORM schema definitions and database client for RevealUI.
5
+ * Designed for Neon Postgres with pgvector extension.
6
+ *
7
+ * ## Usage
8
+ *
9
+ * ### Core (Schema)
10
+ * ```typescript
11
+ * import { users, sites, pages } from '@revealui/db/schema'
12
+ * ```
13
+ *
14
+ * ### Client
15
+ * ```typescript
16
+ * import { getClient } from '@revealui/db/client'
17
+ * const db = getClient()
18
+ * ```
19
+ *
20
+ * ### Full Package
21
+ * ```typescript
22
+ * import { getClient, users, sites, pages } from '@revealui/db'
23
+ * ```
24
+ */
25
+ export { DrizzleAuditStore } from './audit-store.js';
26
+ export { closeAllPools, createClient, type Database as DatabaseClient, type DatabaseConfig, type DatabaseType, getClient, getPoolMetrics, getRestClient, getVectorClient, resetClient, schema, withTransaction, } from './client/index.js';
27
+ export * from './schema/index.js';
28
+ export type { Database, TableInsert, TableRelationships, TableRow, TableUpdate, } from './types/index.js';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEpD,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,QAAQ,IAAI,cAAc,EAC/B,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,SAAS,EACT,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,MAAM,EACN,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAE1B,cAAc,mBAAmB,CAAA;AAGjC,YAAY,EACV,QAAQ,EACR,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,WAAW,GACZ,MAAM,kBAAkB,CAAA"}