@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.
- package/LICENSE +22 -0
- package/README.md +137 -0
- package/dist/audit-store.d.ts +56 -0
- package/dist/audit-store.d.ts.map +1 -0
- package/dist/audit-store.js +120 -0
- package/dist/audit-store.js.map +1 -0
- package/dist/client/index.d.ts +214 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +396 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +109 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +10 -0
- package/dist/client/types.js.map +1 -0
- package/dist/crypto.d.ts +27 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +68 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/log-transport.d.ts +20 -0
- package/dist/log-transport.d.ts.map +1 -0
- package/dist/log-transport.js +49 -0
- package/dist/log-transport.js.map +1 -0
- package/dist/pool.d.ts +36 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +218 -0
- package/dist/pool.js.map +1 -0
- package/dist/queries/boards.d.ts +138 -0
- package/dist/queries/boards.d.ts.map +1 -0
- package/dist/queries/boards.js +87 -0
- package/dist/queries/boards.js.map +1 -0
- package/dist/queries/code-provenance.d.ts +250 -0
- package/dist/queries/code-provenance.d.ts.map +1 -0
- package/dist/queries/code-provenance.js +130 -0
- package/dist/queries/code-provenance.js.map +1 -0
- package/dist/queries/optimized-queries.d.ts +89 -0
- package/dist/queries/optimized-queries.d.ts.map +1 -0
- package/dist/queries/optimized-queries.js +371 -0
- package/dist/queries/optimized-queries.js.map +1 -0
- package/dist/queries/ticket-comments.d.ts +37 -0
- package/dist/queries/ticket-comments.d.ts.map +1 -0
- package/dist/queries/ticket-comments.js +52 -0
- package/dist/queries/ticket-comments.js.map +1 -0
- package/dist/queries/ticket-labels.d.ts +69 -0
- package/dist/queries/ticket-labels.d.ts.map +1 -0
- package/dist/queries/ticket-labels.js +51 -0
- package/dist/queries/ticket-labels.js.map +1 -0
- package/dist/queries/tickets.d.ts +301 -0
- package/dist/queries/tickets.d.ts.map +1 -0
- package/dist/queries/tickets.js +89 -0
- package/dist/queries/tickets.js.map +1 -0
- package/dist/queries/todos.d.ts +37 -0
- package/dist/queries/todos.d.ts.map +1 -0
- package/dist/queries/todos.js +37 -0
- package/dist/queries/todos.js.map +1 -0
- package/dist/schema/agents.d.ts +1413 -0
- package/dist/schema/agents.d.ts.map +1 -0
- package/dist/schema/agents.js +207 -0
- package/dist/schema/agents.js.map +1 -0
- package/dist/schema/api-keys.d.ts +298 -0
- package/dist/schema/api-keys.d.ts.map +1 -0
- package/dist/schema/api-keys.js +53 -0
- package/dist/schema/api-keys.js.map +1 -0
- package/dist/schema/app-logs.d.ts +168 -0
- package/dist/schema/app-logs.d.ts.map +1 -0
- package/dist/schema/app-logs.js +25 -0
- package/dist/schema/app-logs.js.map +1 -0
- package/dist/schema/audit-log.d.ts +174 -0
- package/dist/schema/audit-log.d.ts.map +1 -0
- package/dist/schema/audit-log.js +37 -0
- package/dist/schema/audit-log.js.map +1 -0
- package/dist/schema/cms.d.ts +1015 -0
- package/dist/schema/cms.d.ts.map +1 -0
- package/dist/schema/cms.js +137 -0
- package/dist/schema/cms.js.map +1 -0
- package/dist/schema/code-provenance.d.ts +488 -0
- package/dist/schema/code-provenance.d.ts.map +1 -0
- package/dist/schema/code-provenance.js +72 -0
- package/dist/schema/code-provenance.js.map +1 -0
- package/dist/schema/collab-edits.d.ts +165 -0
- package/dist/schema/collab-edits.d.ts.map +1 -0
- package/dist/schema/collab-edits.js +21 -0
- package/dist/schema/collab-edits.js.map +1 -0
- package/dist/schema/crdt-operations.d.ts +153 -0
- package/dist/schema/crdt-operations.d.ts.map +1 -0
- package/dist/schema/crdt-operations.js +30 -0
- package/dist/schema/crdt-operations.js.map +1 -0
- package/dist/schema/error-events.d.ts +223 -0
- package/dist/schema/error-events.d.ts.map +1 -0
- package/dist/schema/error-events.js +44 -0
- package/dist/schema/error-events.js.map +1 -0
- package/dist/schema/index.d.ts +130 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +310 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/licenses.d.ts +189 -0
- package/dist/schema/licenses.d.ts.map +1 -0
- package/dist/schema/licenses.js +39 -0
- package/dist/schema/licenses.js.map +1 -0
- package/dist/schema/node-ids.d.ts +122 -0
- package/dist/schema/node-ids.d.ts.map +1 -0
- package/dist/schema/node-ids.js +25 -0
- package/dist/schema/node-ids.js.map +1 -0
- package/dist/schema/pages.d.ts +488 -0
- package/dist/schema/pages.d.ts.map +1 -0
- package/dist/schema/pages.js +70 -0
- package/dist/schema/pages.js.map +1 -0
- package/dist/schema/password-reset-tokens.d.ts +137 -0
- package/dist/schema/password-reset-tokens.d.ts.map +1 -0
- package/dist/schema/password-reset-tokens.js +26 -0
- package/dist/schema/password-reset-tokens.js.map +1 -0
- package/dist/schema/query.d.ts +11 -0
- package/dist/schema/query.d.ts.map +1 -0
- package/dist/schema/query.js +11 -0
- package/dist/schema/query.js.map +1 -0
- package/dist/schema/rate-limits.d.ts +212 -0
- package/dist/schema/rate-limits.d.ts.map +1 -0
- package/dist/schema/rate-limits.js +38 -0
- package/dist/schema/rate-limits.js.map +1 -0
- package/dist/schema/rest.d.ts +31 -0
- package/dist/schema/rest.d.ts.map +1 -0
- package/dist/schema/rest.js +37 -0
- package/dist/schema/rest.js.map +1 -0
- package/dist/schema/sites.d.ts +365 -0
- package/dist/schema/sites.d.ts.map +1 -0
- package/dist/schema/sites.js +62 -0
- package/dist/schema/sites.js.map +1 -0
- package/dist/schema/tickets.d.ts +1118 -0
- package/dist/schema/tickets.d.ts.map +1 -0
- package/dist/schema/tickets.js +150 -0
- package/dist/schema/tickets.js.map +1 -0
- package/dist/schema/todos.d.ts +98 -0
- package/dist/schema/todos.d.ts.map +1 -0
- package/dist/schema/todos.js +12 -0
- package/dist/schema/todos.js.map +1 -0
- package/dist/schema/users.d.ts +503 -0
- package/dist/schema/users.d.ts.map +1 -0
- package/dist/schema/users.js +75 -0
- package/dist/schema/users.js.map +1 -0
- package/dist/schema/vector.d.ts +9 -0
- package/dist/schema/vector.d.ts.map +1 -0
- package/dist/schema/vector.js +9 -0
- package/dist/schema/vector.js.map +1 -0
- package/dist/schema/waitlist.d.ts +151 -0
- package/dist/schema/waitlist.d.ts.map +1 -0
- package/dist/schema/waitlist.js +17 -0
- package/dist/schema/waitlist.js.map +1 -0
- package/dist/schema/yjs-documents.d.ts +116 -0
- package/dist/schema/yjs-documents.d.ts.map +1 -0
- package/dist/schema/yjs-documents.js +15 -0
- package/dist/schema/yjs-documents.js.map +1 -0
- package/dist/types/database.d.ts +740 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +151 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/discover.d.ts +83 -0
- package/dist/types/discover.d.ts.map +1 -0
- package/dist/types/discover.js +271 -0
- package/dist/types/discover.js.map +1 -0
- package/dist/types/extract-relationships.d.ts +115 -0
- package/dist/types/extract-relationships.d.ts.map +1 -0
- package/dist/types/extract-relationships.js +455 -0
- package/dist/types/extract-relationships.js.map +1 -0
- package/dist/types/generate-contracts.d.ts +19 -0
- package/dist/types/generate-contracts.d.ts.map +1 -0
- package/dist/types/generate-contracts.js +128 -0
- package/dist/types/generate-contracts.js.map +1 -0
- package/dist/types/generate-zod-schemas.d.ts +20 -0
- package/dist/types/generate-zod-schemas.d.ts.map +1 -0
- package/dist/types/generate-zod-schemas.js +128 -0
- package/dist/types/generate-zod-schemas.js.map +1 -0
- package/dist/types/generate.d.ts +17 -0
- package/dist/types/generate.d.ts.map +1 -0
- package/dist/types/generate.js +298 -0
- package/dist/types/generate.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/introspect.d.ts +75 -0
- package/dist/types/introspect.d.ts.map +1 -0
- package/dist/types/introspect.js +187 -0
- package/dist/types/introspect.js.map +1 -0
- package/dist/types/stripe-schema.d.ts +893 -0
- package/dist/types/stripe-schema.d.ts.map +1 -0
- package/dist/types/stripe-schema.js +112 -0
- package/dist/types/stripe-schema.js.map +1 -0
- 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"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|