duckpond 0.1.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +453 -0
  3. package/dist/DuckPond.d.mts +94 -0
  4. package/dist/DuckPond.d.ts +94 -0
  5. package/dist/DuckPond.js +2 -0
  6. package/dist/DuckPond.js.map +1 -0
  7. package/dist/DuckPond.mjs +2 -0
  8. package/dist/DuckPond.mjs.map +1 -0
  9. package/dist/cache/LRUCache.d.mts +75 -0
  10. package/dist/cache/LRUCache.d.ts +75 -0
  11. package/dist/cache/LRUCache.js +2 -0
  12. package/dist/cache/LRUCache.js.map +1 -0
  13. package/dist/cache/LRUCache.mjs +2 -0
  14. package/dist/cache/LRUCache.mjs.map +1 -0
  15. package/dist/chunk-24M54WUC.mjs +2 -0
  16. package/dist/chunk-24M54WUC.mjs.map +1 -0
  17. package/dist/chunk-4NKFJCEP.mjs +27 -0
  18. package/dist/chunk-4NKFJCEP.mjs.map +1 -0
  19. package/dist/chunk-5XGN7UAV.js +2 -0
  20. package/dist/chunk-5XGN7UAV.js.map +1 -0
  21. package/dist/chunk-DTZ5B6AO.mjs +2 -0
  22. package/dist/chunk-DTZ5B6AO.mjs.map +1 -0
  23. package/dist/chunk-E5ZZH3QB.js +2 -0
  24. package/dist/chunk-E5ZZH3QB.js.map +1 -0
  25. package/dist/chunk-J2OQ62DV.js +2 -0
  26. package/dist/chunk-J2OQ62DV.js.map +1 -0
  27. package/dist/chunk-MZTKR3LR.js +3 -0
  28. package/dist/chunk-MZTKR3LR.js.map +1 -0
  29. package/dist/chunk-PCQEPXO3.mjs +3 -0
  30. package/dist/chunk-PCQEPXO3.mjs.map +1 -0
  31. package/dist/chunk-Q6UFPTQC.js +2 -0
  32. package/dist/chunk-Q6UFPTQC.js.map +1 -0
  33. package/dist/chunk-SZJXSB7U.mjs +2 -0
  34. package/dist/chunk-SZJXSB7U.mjs.map +1 -0
  35. package/dist/chunk-TLGHSO3F.js +27 -0
  36. package/dist/chunk-TLGHSO3F.js.map +1 -0
  37. package/dist/chunk-V57JCP3U.mjs +2 -0
  38. package/dist/chunk-V57JCP3U.mjs.map +1 -0
  39. package/dist/index.d.mts +12 -0
  40. package/dist/index.d.ts +12 -0
  41. package/dist/index.js +2 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/index.mjs +2 -0
  44. package/dist/index.mjs.map +1 -0
  45. package/dist/types.d.mts +182 -0
  46. package/dist/types.d.ts +182 -0
  47. package/dist/types.js +2 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/types.mjs +2 -0
  50. package/dist/types.mjs.map +1 -0
  51. package/dist/utils/errors.d.mts +40 -0
  52. package/dist/utils/errors.d.ts +40 -0
  53. package/dist/utils/errors.js +2 -0
  54. package/dist/utils/errors.js.map +1 -0
  55. package/dist/utils/errors.mjs +2 -0
  56. package/dist/utils/errors.mjs.map +1 -0
  57. package/dist/utils/logger.d.mts +25 -0
  58. package/dist/utils/logger.d.ts +25 -0
  59. package/dist/utils/logger.js +2 -0
  60. package/dist/utils/logger.js.map +1 -0
  61. package/dist/utils/logger.mjs +2 -0
  62. package/dist/utils/logger.mjs.map +1 -0
  63. package/package.json +80 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Jordan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,453 @@
1
+ # DuckPond
2
+
3
+ [![Node.js CI](https://github.com/jordanburke/duckpond/actions/workflows/node.js.yml/badge.svg)](https://github.com/jordanburke/duckpond/actions/workflows/node.js.yml)
4
+ [![CodeQL](https://github.com/jordanburke/duckpond/actions/workflows/codeql.yml/badge.svg)](https://github.com/jordanburke/duckpond/actions/workflows/codeql.yml)
5
+
6
+ Multi-tenant DuckDB manager with R2/S3 storage and functional programming patterns.
7
+
8
+ ## Features
9
+
10
+ - 🏢 **Multi-Tenant Isolation** - Per-user database instances with automatic resource management
11
+ - ☁️ **Cloud Storage** - Native Cloudflare R2 and AWS S3 integration
12
+ - 🛡️ **Type-Safe Functional Programming** - Built with [functype](https://github.com/jordanburke/functype) for robust error handling
13
+ - 🚀 **LRU Caching** - Intelligent caching with automatic eviction of idle users
14
+ - 📊 **Storage Strategies** - Flexible parquet, duckdb, or hybrid storage options
15
+ - 🔧 **TypeScript-First** - Full type safety with comprehensive TypeScript declarations
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install duckpond
21
+ # or
22
+ pnpm add duckpond
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { DuckPond } from "duckpond"
29
+
30
+ // Configure with Cloudflare R2
31
+ const pond = new DuckPond({
32
+ r2: {
33
+ accountId: process.env.R2_ACCOUNT_ID!,
34
+ accessKeyId: process.env.R2_ACCESS_KEY_ID!,
35
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
36
+ bucket: "my-bucket",
37
+ },
38
+ maxActiveUsers: 10,
39
+ })
40
+
41
+ // Initialize
42
+ await pond.init()
43
+
44
+ // Query with functional error handling
45
+ const result = await pond.query("user123", "SELECT * FROM orders")
46
+ result.fold(
47
+ (error) => console.error("Query failed:", error.message),
48
+ (rows) => console.log("Results:", rows),
49
+ )
50
+
51
+ // Cleanup
52
+ await pond.close()
53
+ ```
54
+
55
+ ## Core Concepts
56
+
57
+ ### Functional Error Handling
58
+
59
+ DuckPond uses [functype](https://github.com/jordanburke/functype) for type-safe error handling without exceptions:
60
+
61
+ ```typescript
62
+ import { Either } from "duckpond"
63
+
64
+ // All operations return Either<Error, Success>
65
+ const result = await pond.query<{ id: number; name: string }>("user123", "SELECT * FROM users")
66
+
67
+ // Pattern match on success/failure
68
+ result.fold(
69
+ (error) => {
70
+ // Handle error case
71
+ console.error(`[${error.code}] ${error.message}`)
72
+ if (error.cause) console.error("Caused by:", error.cause)
73
+ },
74
+ (rows) => {
75
+ // Handle success case
76
+ rows.forEach((user) => console.log(`${user.id}: ${user.name}`))
77
+ },
78
+ )
79
+
80
+ // Or check explicitly
81
+ if (result.isLeft()) {
82
+ const error = result.fold(
83
+ (err) => err,
84
+ () => null,
85
+ )
86
+ // Handle error
87
+ } else {
88
+ const rows = result.fold(
89
+ () => [],
90
+ (data) => data,
91
+ )
92
+ // Process rows
93
+ }
94
+ ```
95
+
96
+ ### Multi-Tenant Isolation
97
+
98
+ Each user gets an isolated database instance:
99
+
100
+ ```typescript
101
+ // User A's queries don't affect User B
102
+ await pond.query("userA", "CREATE TABLE orders (id INT)")
103
+ await pond.query("userB", "SELECT * FROM orders") // Error: table doesn't exist
104
+
105
+ // Check user status
106
+ const isActive = pond.isAttached("userA") // true if cached
107
+
108
+ // Get user statistics
109
+ const stats = await pond.getUserStats("userA")
110
+ stats.fold(
111
+ (error) => console.error(error.message),
112
+ (info) => console.log(`User: ${info.userId}, Last access: ${info.lastAccess}`),
113
+ )
114
+ ```
115
+
116
+ ### Storage Strategies
117
+
118
+ DuckPond supports multiple storage strategies:
119
+
120
+ ```typescript
121
+ // Parquet files (default) - best for analytics
122
+ const pond = new DuckPond({
123
+ r2: {
124
+ /* ... */
125
+ },
126
+ strategy: "parquet",
127
+ })
128
+
129
+ // DuckDB files - full database persistence
130
+ const pond = new DuckPond({
131
+ r2: {
132
+ /* ... */
133
+ },
134
+ strategy: "duckdb",
135
+ })
136
+
137
+ // Hybrid - mix both approaches
138
+ const pond = new DuckPond({
139
+ r2: {
140
+ /* ... */
141
+ },
142
+ strategy: "hybrid",
143
+ })
144
+ ```
145
+
146
+ ## API Reference
147
+
148
+ ### DuckPond Class
149
+
150
+ #### `constructor(config: DuckPondConfig)`
151
+
152
+ Creates a new DuckPond instance.
153
+
154
+ ```typescript
155
+ const pond = new DuckPond({
156
+ // R2 Configuration (Cloudflare)
157
+ r2: {
158
+ accountId: string
159
+ accessKeyId: string
160
+ secretAccessKey: string
161
+ bucket: string
162
+ },
163
+
164
+ // OR S3 Configuration (AWS)
165
+ s3: {
166
+ region: string
167
+ accessKeyId: string
168
+ secretAccessKey: string
169
+ bucket: string
170
+ endpoint?: string // For S3-compatible services
171
+ },
172
+
173
+ // Optional settings
174
+ memoryLimit: '4GB', // DuckDB memory limit
175
+ threads: 4, // Number of threads
176
+ maxActiveUsers: 10, // LRU cache size
177
+ evictionTimeout: 300000, // Idle timeout (5 min)
178
+ cacheType: 'disk', // 'disk' | 'memory' | 'noop'
179
+ strategy: 'parquet' // 'parquet' | 'duckdb' | 'hybrid'
180
+ })
181
+ ```
182
+
183
+ #### `async init(): AsyncDuckPondResult<void>`
184
+
185
+ Initialize DuckPond. Must be called before any operations.
186
+
187
+ ```typescript
188
+ const result = await pond.init()
189
+ result.fold(
190
+ (error) => console.error("Initialization failed:", error.message),
191
+ () => console.log("Ready!"),
192
+ )
193
+ ```
194
+
195
+ #### `async query<T>(userId: string, sql: string): AsyncDuckPondResult<T[]>`
196
+
197
+ Execute a SQL query for a specific user.
198
+
199
+ ```typescript
200
+ const result = await pond.query<{ id: number; total: number }>(
201
+ "user123",
202
+ "SELECT id, SUM(amount) as total FROM orders GROUP BY id",
203
+ )
204
+ ```
205
+
206
+ #### `async execute(userId: string, sql: string): AsyncDuckPondResult<void>`
207
+
208
+ Execute SQL without returning results (DDL, DML).
209
+
210
+ ```typescript
211
+ await pond.execute(
212
+ "user123",
213
+ `
214
+ CREATE TABLE products (
215
+ id INTEGER PRIMARY KEY,
216
+ name VARCHAR,
217
+ price DECIMAL(10,2)
218
+ )
219
+ `,
220
+ )
221
+ ```
222
+
223
+ #### `async getUserStats(userId: string): AsyncDuckPondResult<UserStats>`
224
+
225
+ Get statistics about a user's database.
226
+
227
+ ```typescript
228
+ const result = await pond.getUserStats("user123")
229
+ result.fold(
230
+ (error) => console.error(error.message),
231
+ (stats) =>
232
+ console.log({
233
+ userId: stats.userId,
234
+ attached: stats.attached,
235
+ lastAccess: stats.lastAccess,
236
+ memoryUsage: stats.memoryUsage,
237
+ }),
238
+ )
239
+ ```
240
+
241
+ #### `isAttached(userId: string): boolean`
242
+
243
+ Check if a user is currently cached.
244
+
245
+ ```typescript
246
+ if (pond.isAttached("user123")) {
247
+ console.log("User database is active")
248
+ }
249
+ ```
250
+
251
+ #### `async detachUser(userId: string): AsyncDuckPondResult<void>`
252
+
253
+ Manually detach a user's database from the cache.
254
+
255
+ ```typescript
256
+ await pond.detachUser("user123")
257
+ ```
258
+
259
+ #### `async close(): AsyncDuckPondResult<void>`
260
+
261
+ Close DuckPond and cleanup all resources.
262
+
263
+ ```typescript
264
+ await pond.close()
265
+ ```
266
+
267
+ ### Error Codes
268
+
269
+ ```typescript
270
+ import { ErrorCode } from "duckpond"
271
+
272
+ ErrorCode.CONNECTION_FAILED
273
+ ErrorCode.R2_CONNECTION_ERROR
274
+ ErrorCode.S3_CONNECTION_ERROR
275
+ ErrorCode.USER_NOT_FOUND
276
+ ErrorCode.QUERY_EXECUTION_ERROR
277
+ ErrorCode.QUERY_TIMEOUT
278
+ ErrorCode.MEMORY_LIMIT_EXCEEDED
279
+ ErrorCode.STORAGE_ERROR
280
+ ErrorCode.INVALID_CONFIG
281
+ ErrorCode.NOT_INITIALIZED
282
+ ErrorCode.UNKNOWN_ERROR
283
+ ```
284
+
285
+ ## Examples
286
+
287
+ ### AWS S3 Configuration
288
+
289
+ ```typescript
290
+ const pond = new DuckPond({
291
+ s3: {
292
+ region: "us-east-1",
293
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
294
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
295
+ bucket: "my-duckdb-bucket",
296
+ },
297
+ })
298
+ ```
299
+
300
+ ### MinIO or S3-Compatible Storage
301
+
302
+ ```typescript
303
+ const pond = new DuckPond({
304
+ s3: {
305
+ region: "us-east-1",
306
+ accessKeyId: "minioadmin",
307
+ secretAccessKey: "minioadmin",
308
+ bucket: "duckdb",
309
+ endpoint: "http://localhost:9000",
310
+ },
311
+ })
312
+ ```
313
+
314
+ ### Advanced Error Handling
315
+
316
+ ```typescript
317
+ import { ErrorCode } from "duckpond"
318
+
319
+ const result = await pond.query("user123", "SELECT * FROM orders")
320
+
321
+ result.fold(
322
+ (error) => {
323
+ switch (error.code) {
324
+ case ErrorCode.QUERY_EXECUTION_ERROR:
325
+ console.error("SQL error:", error.message)
326
+ if (error.context?.sql) {
327
+ console.error("Query:", error.context.sql)
328
+ }
329
+ break
330
+
331
+ case ErrorCode.USER_NOT_FOUND:
332
+ console.error("User not found:", error.context?.userId)
333
+ break
334
+
335
+ case ErrorCode.MEMORY_LIMIT_EXCEEDED:
336
+ console.error("Out of memory:", error.context?.limit)
337
+ break
338
+
339
+ default:
340
+ console.error("Unexpected error:", error)
341
+ }
342
+ },
343
+ (rows) => {
344
+ console.log(`Fetched ${rows.length} rows`)
345
+ },
346
+ )
347
+ ```
348
+
349
+ ### Using Functype Utilities
350
+
351
+ ```typescript
352
+ import { Option, List } from "duckpond"
353
+
354
+ // Safe null handling with Option
355
+ const maybeUser = Option(user)
356
+ const userName = maybeUser.map((u) => u.name).orElse("Anonymous")
357
+
358
+ // Immutable collections with List
359
+ const users = List([
360
+ { id: 1, name: "Alice" },
361
+ { id: 2, name: "Bob" },
362
+ ])
363
+
364
+ const names = users
365
+ .map((u) => u.name)
366
+ .filter((name) => name.startsWith("A"))
367
+ .toArray()
368
+ ```
369
+
370
+ ## Development
371
+
372
+ ### Pre-Checkin Command
373
+
374
+ ```bash
375
+ pnpm validate # 🚀 Format, lint, test, and build
376
+ ```
377
+
378
+ ### Individual Commands
379
+
380
+ ```bash
381
+ # Formatting
382
+ pnpm format # Format code with Prettier
383
+ pnpm format:check # Check formatting without writing
384
+
385
+ # Linting
386
+ pnpm lint # Fix ESLint issues
387
+ pnpm lint:check # Check ESLint issues without fixing
388
+
389
+ # Testing
390
+ pnpm test # Run tests once
391
+ pnpm test:watch # Run tests in watch mode
392
+ pnpm test:coverage # Run tests with coverage
393
+ pnpm test:ui # Launch Vitest UI
394
+
395
+ # Building
396
+ pnpm build # Production build
397
+ pnpm dev # Development mode with watch
398
+ ```
399
+
400
+ ## Architecture
401
+
402
+ ```
403
+ ┌─────────────────────────────────────────┐
404
+ │ DuckPond Manager │
405
+ │ - User isolation & lifecycle │
406
+ │ - Connection pooling │
407
+ │ - Functional error handling │
408
+ └──────────────┬──────────────────────────┘
409
+
410
+ ┌───────┴────────┐
411
+ │ LRU Cache │
412
+ │ - Max active │
413
+ │ - Auto-evict │
414
+ └───────┬────────┘
415
+
416
+ ┌───────▼────────┐
417
+ │ DuckDB Inst │
418
+ │ - Per-user DB │
419
+ │ - R2/S3 mount │
420
+ └───────┬────────┘
421
+
422
+ ┌───────▼────────┐
423
+ │ Cloud Storage │
424
+ │ - R2 / S3 │
425
+ │ - Parquet files│
426
+ └────────────────┘
427
+ ```
428
+
429
+ ### Key Components
430
+
431
+ - **DuckPond**: Main manager class handling user lifecycle and queries
432
+ - **LRUCache**: Generic LRU cache with functype Option/List integration
433
+ - **Error Utilities**: Functional error creation and handling with Either
434
+ - **Types**: Comprehensive TypeScript definitions for all APIs
435
+
436
+ ## Contributing
437
+
438
+ Contributions are welcome! Please ensure:
439
+
440
+ 1. All tests pass: `pnpm test`
441
+ 2. Code is formatted: `pnpm format`
442
+ 3. No lint errors: `pnpm lint:check`
443
+ 4. Build succeeds: `pnpm build`
444
+
445
+ Or simply run: `pnpm validate`
446
+
447
+ ## License
448
+
449
+ MIT - see LICENSE file for details
450
+
451
+ ---
452
+
453
+ Built with [functype](https://github.com/jordanburke/functype) for functional TypeScript
@@ -0,0 +1,94 @@
1
+ import { DuckDBConnection } from '@duckdb/node-api';
2
+ import { DuckPondConfig, AsyncDuckPondResult, UserStats } from './types.mjs';
3
+ import 'functype/either';
4
+
5
+ /**
6
+ * DuckPond - Multi-tenant DuckDB manager with R2/S3 storage
7
+ *
8
+ * Manages per-user DuckDB instances with:
9
+ * - LRU caching for active users
10
+ * - R2/S3 object storage integration
11
+ * - Functional error handling with functype Either
12
+ * - Automatic resource cleanup
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const pond = new DuckPond({
17
+ * r2: {
18
+ * accountId: 'xxx',
19
+ * accessKeyId: 'yyy',
20
+ * secretAccessKey: 'zzz',
21
+ * bucket: 'my-bucket'
22
+ * }
23
+ * })
24
+ *
25
+ * await pond.init()
26
+ *
27
+ * const result = await pond.query('user123', 'SELECT * FROM orders')
28
+ * result.fold(
29
+ * error => console.error('Query failed:', error),
30
+ * rows => console.log('Results:', rows)
31
+ * )
32
+ * ```
33
+ */
34
+ declare class DuckPond {
35
+ private instance;
36
+ private cache;
37
+ private config;
38
+ private evictionTimer;
39
+ private initialized;
40
+ constructor(config: DuckPondConfig);
41
+ /**
42
+ * Initialize DuckPond
43
+ * Must be called before any other operations
44
+ */
45
+ init(): AsyncDuckPondResult<void>;
46
+ /**
47
+ * Configure R2/S3 access and DuckDB extensions
48
+ */
49
+ private setupCloudStorage;
50
+ /**
51
+ * Get a connection for a user
52
+ * Loads from cache or attaches new database
53
+ */
54
+ getUserConnection(userId: string): AsyncDuckPondResult<DuckDBConnection>;
55
+ /**
56
+ * Attach a user's database based on storage strategy
57
+ */
58
+ private attachUserDatabase;
59
+ /**
60
+ * Execute a SQL query for a user
61
+ * Returns Either<Error, results>
62
+ */
63
+ query<T = unknown>(userId: string, sql: string): AsyncDuckPondResult<T[]>;
64
+ /**
65
+ * Execute SQL without returning results (DDL, DML)
66
+ */
67
+ execute(userId: string, sql: string): AsyncDuckPondResult<void>;
68
+ /**
69
+ * Detach a user's database and free resources
70
+ */
71
+ detachUser(userId: string): AsyncDuckPondResult<void>;
72
+ /**
73
+ * Evict the least recently used user
74
+ */
75
+ private evictLRU;
76
+ /**
77
+ * Start background timer to evict idle users
78
+ */
79
+ private startEvictionTimer;
80
+ /**
81
+ * Check if a user is currently attached
82
+ */
83
+ isAttached(userId: string): boolean;
84
+ /**
85
+ * Get statistics about a user's database
86
+ */
87
+ getUserStats(userId: string): AsyncDuckPondResult<UserStats>;
88
+ /**
89
+ * Close DuckPond and cleanup all resources
90
+ */
91
+ close(): AsyncDuckPondResult<void>;
92
+ }
93
+
94
+ export { DuckPond };
@@ -0,0 +1,94 @@
1
+ import { DuckDBConnection } from '@duckdb/node-api';
2
+ import { DuckPondConfig, AsyncDuckPondResult, UserStats } from './types.js';
3
+ import 'functype/either';
4
+
5
+ /**
6
+ * DuckPond - Multi-tenant DuckDB manager with R2/S3 storage
7
+ *
8
+ * Manages per-user DuckDB instances with:
9
+ * - LRU caching for active users
10
+ * - R2/S3 object storage integration
11
+ * - Functional error handling with functype Either
12
+ * - Automatic resource cleanup
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const pond = new DuckPond({
17
+ * r2: {
18
+ * accountId: 'xxx',
19
+ * accessKeyId: 'yyy',
20
+ * secretAccessKey: 'zzz',
21
+ * bucket: 'my-bucket'
22
+ * }
23
+ * })
24
+ *
25
+ * await pond.init()
26
+ *
27
+ * const result = await pond.query('user123', 'SELECT * FROM orders')
28
+ * result.fold(
29
+ * error => console.error('Query failed:', error),
30
+ * rows => console.log('Results:', rows)
31
+ * )
32
+ * ```
33
+ */
34
+ declare class DuckPond {
35
+ private instance;
36
+ private cache;
37
+ private config;
38
+ private evictionTimer;
39
+ private initialized;
40
+ constructor(config: DuckPondConfig);
41
+ /**
42
+ * Initialize DuckPond
43
+ * Must be called before any other operations
44
+ */
45
+ init(): AsyncDuckPondResult<void>;
46
+ /**
47
+ * Configure R2/S3 access and DuckDB extensions
48
+ */
49
+ private setupCloudStorage;
50
+ /**
51
+ * Get a connection for a user
52
+ * Loads from cache or attaches new database
53
+ */
54
+ getUserConnection(userId: string): AsyncDuckPondResult<DuckDBConnection>;
55
+ /**
56
+ * Attach a user's database based on storage strategy
57
+ */
58
+ private attachUserDatabase;
59
+ /**
60
+ * Execute a SQL query for a user
61
+ * Returns Either<Error, results>
62
+ */
63
+ query<T = unknown>(userId: string, sql: string): AsyncDuckPondResult<T[]>;
64
+ /**
65
+ * Execute SQL without returning results (DDL, DML)
66
+ */
67
+ execute(userId: string, sql: string): AsyncDuckPondResult<void>;
68
+ /**
69
+ * Detach a user's database and free resources
70
+ */
71
+ detachUser(userId: string): AsyncDuckPondResult<void>;
72
+ /**
73
+ * Evict the least recently used user
74
+ */
75
+ private evictLRU;
76
+ /**
77
+ * Start background timer to evict idle users
78
+ */
79
+ private startEvictionTimer;
80
+ /**
81
+ * Check if a user is currently attached
82
+ */
83
+ isAttached(userId: string): boolean;
84
+ /**
85
+ * Get statistics about a user's database
86
+ */
87
+ getUserStats(userId: string): AsyncDuckPondResult<UserStats>;
88
+ /**
89
+ * Close DuckPond and cleanup all resources
90
+ */
91
+ close(): AsyncDuckPondResult<void>;
92
+ }
93
+
94
+ export { DuckPond };
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkTLGHSO3Fjs = require('./chunk-TLGHSO3F.js');require('./chunk-J2OQ62DV.js');require('./chunk-MZTKR3LR.js');require('./chunk-E5ZZH3QB.js');require('./chunk-Q6UFPTQC.js');require('./chunk-5XGN7UAV.js');exports.DuckPond = _chunkTLGHSO3Fjs.a;
2
+ //# sourceMappingURL=DuckPond.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/jordanburke/IdeaProjects/duckpond/dist/DuckPond.js"],"names":[],"mappings":"AAAA,+HAAkC,+BAA4B,+BAA4B,+BAA4B,+BAA4B,+BAA4B,sCAAsB","file":"/home/jordanburke/IdeaProjects/duckpond/dist/DuckPond.js"}
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-4NKFJCEP.mjs";import"./chunk-24M54WUC.mjs";import"./chunk-PCQEPXO3.mjs";import"./chunk-DTZ5B6AO.mjs";import"./chunk-SZJXSB7U.mjs";import"./chunk-V57JCP3U.mjs";export{a as DuckPond};
2
+ //# sourceMappingURL=DuckPond.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}