@earth-app/collegedb 1.1.4 → 1.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/README.md CHANGED
@@ -19,6 +19,7 @@ A TypeScript library for **true horizontal scaling** of SQLite-style databases p
19
19
  - [Provider Adapters](#provider-adapters)
20
20
  - [NuxtHub + Drizzle Recipes](#nuxthub--drizzle-recipes)
21
21
  - [Sandbox Benchmarks (Docker Compose)](#sandbox-benchmarks-docker-compose)
22
+ - [In-Memory Providers for Testing & Development](#in-memory-providers-for-testing--development)
22
23
  - [Basic Usage](#basic-usage)
23
24
  - [Multi-Key Shard Mappings](#multi-key-shard-mappings)
24
25
  - [Drop-in Replacement for Existing Databases](#drop-in-replacement-for-existing-databases)
@@ -456,6 +457,264 @@ How to read benchmark rows:
456
457
  - `N/A` means the scenario was intentionally skipped in that environment.
457
458
  - Use the detailed section for full `avg`, `p50`, `p95`, `min`, `max`, and sample count (`n`).
458
459
 
460
+ ## In-Memory Providers for Testing & Development
461
+
462
+ CollegeDB includes lightweight, zero-dependency in-memory mock implementations of the `KVStorage` and `SQLDatabase` interfaces. These are ideal for:
463
+
464
+ - **Unit testing** without external dependencies
465
+ - **Integration testing** with multiple shard combinations
466
+ - **Local development** and rapid iteration
467
+ - **Sandboxed playtesting** of routing logic
468
+
469
+ The in-memory providers work in Cloudflare Workers, Node.js, and Deno environments.
470
+
471
+ ### Quick Start with In-Memory Providers
472
+
473
+ ```typescript
474
+ import { createInMemoryKVProvider, createInMemorySQLProvider, initialize, run, first } from '@earth-app/collegedb';
475
+
476
+ // Create fresh in-memory providers for each test
477
+ const config = {
478
+ kv: createInMemoryKVProvider(),
479
+ shards: {
480
+ 'shard-1': createInMemorySQLProvider(),
481
+ 'shard-2': createInMemorySQLProvider(),
482
+ 'shard-3': createInMemorySQLProvider()
483
+ },
484
+ strategy: 'hash'
485
+ };
486
+
487
+ initialize(config);
488
+
489
+ // Use as normal - all operations happen in-memory
490
+ await run('user-1', 'INSERT INTO users (id, name, email) VALUES (?, ?, ?)', ['user-1', 'Alice', 'alice@example.com']);
491
+
492
+ const user = await first<{ id: string; name: string }>('user-1', 'SELECT id, name FROM users WHERE id = ?', ['user-1']);
493
+ console.log(user); // { id: 'user-1', name: 'Alice' }
494
+ ```
495
+
496
+ ### Unit Testing Example
497
+
498
+ ```typescript
499
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
500
+ import { createInMemoryKVProvider, createInMemorySQLProvider, initialize, resetConfig, run, first } from '@earth-app/collegedb';
501
+
502
+ describe('User Shard Routing', () => {
503
+ beforeEach(() => {
504
+ // Fresh providers for each test
505
+ initialize({
506
+ kv: createInMemoryKVProvider(),
507
+ shards: {
508
+ 'shard-1': createInMemorySQLProvider(),
509
+ 'shard-2': createInMemorySQLProvider(),
510
+ 'shard-3': createInMemorySQLProvider()
511
+ },
512
+ strategy: 'hash'
513
+ });
514
+ });
515
+
516
+ afterEach(() => {
517
+ resetConfig();
518
+ });
519
+
520
+ it('should insert and retrieve a user', async () => {
521
+ await run('user-1', 'INSERT INTO users (id, name, email) VALUES (?, ?, ?)', ['user-1', 'Alice', 'alice@example.com']);
522
+
523
+ const user = await first<{ name: string }>('user-1', 'SELECT name FROM users WHERE id = ?', ['user-1']);
524
+
525
+ expect(user?.name).toBe('Alice');
526
+ });
527
+
528
+ it('should distribute users across shards', async () => {
529
+ // Insert multiple users
530
+ for (let i = 0; i < 9; i++) {
531
+ await run(`user-${i}`, 'INSERT INTO users (id, name) VALUES (?, ?)', [`user-${i}`, `User ${i}`]);
532
+ }
533
+
534
+ // Verify each can be retrieved
535
+ for (let i = 0; i < 9; i++) {
536
+ const user = await first(`user-${i}`, 'SELECT id FROM users WHERE id = ?', [`user-${i}`]);
537
+ expect(user).toBeDefined();
538
+ }
539
+ });
540
+
541
+ it('should handle updates correctly', async () => {
542
+ await run('user-1', 'INSERT INTO users (id, name) VALUES (?, ?)', ['user-1', 'Alice']);
543
+
544
+ await run('user-1', 'UPDATE users SET name = ? WHERE id = ?', ['Alice Updated', 'user-1']);
545
+
546
+ const user = await first<{ name: string }>('user-1', 'SELECT name FROM users WHERE id = ?', ['user-1']);
547
+
548
+ expect(user?.name).toBe('Alice Updated');
549
+ });
550
+ });
551
+ ```
552
+
553
+ ### Integration Testing with Multiple Providers
554
+
555
+ Test different combinations without Docker or external services:
556
+
557
+ ```typescript
558
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
559
+ import {
560
+ createInMemoryKVProvider,
561
+ createInMemorySQLProvider,
562
+ initialize,
563
+ resetConfig,
564
+ run,
565
+ first,
566
+ KVShardMapper
567
+ } from '@earth-app/collegedb';
568
+
569
+ describe('Multi-Provider Integration', () => {
570
+ it('should work with different KV/SQL combinations', async () => {
571
+ const combinations = [{ kvName: 'memory', sqlName: 'memory' }];
572
+
573
+ for (const combo of combinations) {
574
+ resetConfig();
575
+
576
+ initialize({
577
+ kv: createInMemoryKVProvider(),
578
+ shards: {
579
+ 'shard-1': createInMemorySQLProvider(),
580
+ 'shard-2': createInMemorySQLProvider()
581
+ },
582
+ strategy: 'hash'
583
+ });
584
+
585
+ // Test basic operations
586
+ await run('key-1', 'INSERT INTO data (id, value) VALUES (?, ?)', ['key-1', 'test-value']);
587
+
588
+ const row = await first('key-1', 'SELECT value FROM data WHERE id = ?', ['key-1']);
589
+
590
+ expect(row?.value).toBe('test-value');
591
+ }
592
+ });
593
+
594
+ it('should support lookup key mapping', async () => {
595
+ initialize({
596
+ kv: createInMemoryKVProvider(),
597
+ shards: { 'shard-1': createInMemorySQLProvider() },
598
+ strategy: 'hash'
599
+ });
600
+
601
+ const mapper = new KVShardMapper(createInMemoryKVProvider());
602
+
603
+ // Add lookup keys
604
+ await mapper.addLookupKeys('user-123', ['email:alice@example.com', 'username:alice']);
605
+
606
+ // Retrieve via lookup key
607
+ const mapping = await mapper.getShardMapping('email:alice@example.com');
608
+ expect(mapping?.shard).toBeDefined();
609
+ });
610
+ });
611
+ ```
612
+
613
+ ### Performance Testing with In-Memory Providers
614
+
615
+ Run quick performance tests locally without external dependencies:
616
+
617
+ ```typescript
618
+ import { createInMemoryKVProvider, createInMemorySQLProvider, initialize, run } from '@earth-app/collegedb';
619
+
620
+ async function benchmarkInserts(iterations: number): Promise<number> {
621
+ initialize({
622
+ kv: createInMemoryKVProvider(),
623
+ shards: {
624
+ 'shard-1': createInMemorySQLProvider(),
625
+ 'shard-2': createInMemorySQLProvider(),
626
+ 'shard-3': createInMemorySQLProvider()
627
+ },
628
+ strategy: 'hash'
629
+ });
630
+
631
+ const startTime = performance.now();
632
+
633
+ for (let i = 0; i < iterations; i++) {
634
+ const id = `perf-user-${i}`;
635
+ await run(id, 'INSERT INTO users (id, name) VALUES (?, ?)', [id, `User ${i}`]);
636
+ }
637
+
638
+ return performance.now() - startTime;
639
+ }
640
+
641
+ const duration = await benchmarkInserts(1000);
642
+ console.log(`1000 inserts: ${duration.toFixed(2)}ms (${((1000 / duration) * 1000).toFixed(0)} ops/sec)`);
643
+ ```
644
+
645
+ ### Running the Memory Provider Sandbox
646
+
647
+ CollegeDB includes a ready-made sandbox example demonstrating multiple scenarios:
648
+
649
+ ```bash
650
+ bun run test:memory
651
+ ```
652
+
653
+ This runs comprehensive benchmarks including:
654
+
655
+ - Basic CRUD operations
656
+ - Multi-shard data distribution
657
+ - KV storage operations
658
+ - Round-robin strategy testing
659
+ - JOIN query performance
660
+
661
+ ### Supported Operations
662
+
663
+ Both in-memory providers support the complete CollegeDB API:
664
+
665
+ **SQLDatabase features:**
666
+
667
+ - CREATE TABLE / DROP TABLE
668
+ - INSERT / UPDATE / DELETE
669
+ - SELECT with WHERE clauses
670
+ - COUNT(\*) queries
671
+ - JOIN queries
672
+ - PRAGMA queries (basic support)
673
+
674
+ **KVStorage features:**
675
+
676
+ - `get()` / `put()` / `delete()`
677
+ - `list()` with prefix filtering and cursor-based pagination
678
+ - TTL/expiration support
679
+
680
+ ### Limitations
681
+
682
+ The in-memory providers are intentionally simple to avoid dependencies:
683
+
684
+ - **SQL Parser**: Basic pattern matching instead of full SQL parsing; works well for standard CollegeDB patterns but may not handle complex SQL edge cases
685
+ - **Joins**: Supported at application level; cross-shard joins work via multiple routed queries
686
+ - **Transactions**: Not supported; operations are atomic per-statement
687
+ - **Indexes**: Created but not actually used for query optimization
688
+ - **Schema**: Inferred from CREATE TABLE statements; dynamic column detection based on binding order
689
+
690
+ For production use, migrate to appropriate providers (D1, Redis, PostgreSQL, etc.). For testing/development, these limitations are intentional to keep the implementation lightweight and zero-dependency.
691
+
692
+ ### Migrating from In-Memory to Production Providers
693
+
694
+ When ready to migrate from testing to production:
695
+
696
+ ```typescript
697
+ // Before (testing)
698
+ import { createInMemoryKVProvider, createInMemorySQLProvider } from '@earth-app/collegedb';
699
+
700
+ const config = {
701
+ kv: createInMemoryKVProvider(),
702
+ shards: { 'shard-1': createInMemorySQLProvider() }
703
+ };
704
+
705
+ // After (production)
706
+ import { createRedisKVProvider, createPostgreSQLProvider } from '@earth-app/collegedb';
707
+
708
+ const config = {
709
+ kv: createRedisKVProvider(redisClient),
710
+ shards: { 'shard-1': createPostgreSQLProvider(pgPool) }
711
+ };
712
+
713
+ // Rest of configuration stays the same!
714
+ ```
715
+
716
+ The API remains identical - only the provider initialization changes.
717
+
459
718
  ## Basic Usage
460
719
 
461
720
  ```typescript
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export { ShardCoordinator } from './durable';
14
14
  export { CollegeDBError } from './errors';
15
15
  export { KVShardMapper } from './kvmap';
16
16
  export { createDrizzleSQLProvider, createHyperdriveMySQLProvider, createHyperdrivePostgresProvider, createMySQLProvider, createNuxtHubKVProvider, createPostgreSQLProvider, createRedisKVProvider, createSQLiteProvider, createValkeyKVProvider, isKVStorage, isSQLDatabase, type DrizzleClientLike, type DrizzleSqlChunkLike, type DrizzleSqlTagLike, type HyperdriveBindingLike, type HyperdriveMySQLClientFactory, type HyperdrivePostgresClientFactory, type MySQLClientLike, type NuxtHubKVLike, type PostgresClientLike, type RedisLikeClient, type SQLiteClientLike } from './providers';
17
+ export { InMemoryKVStorage, InMemorySQLDatabase, createInMemoryKVProvider, createInMemorySQLProvider } from './providers-memory';
17
18
  export { autoDetectAndMigrate, checkMigrationNeeded, clearMigrationCache, clearShardMigrationCache, createMappingsForExistingKeys, createSchemaAcrossShards, discoverExistingPrimaryKeys, discoverExistingRecordsWithColumns, dropSchema, integrateExistingDatabase, listTables, migrateRecord, schemaExists, validateTableForSharding, type IntegrationOptions, type IntegrationResult, type ValidationResult } from './migrations';
18
19
  export type { CollegeDBConfig, D1Region, Env, KVListResult, KVStorage, MixedShardingStrategy, OperationType, PreparedStatement, QueryResult, QueryResultMeta, SQLDatabase, ShardCoordinatorState, ShardLocation, ShardMapping, ShardStats, ShardingStrategy } from './types';
19
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACN,GAAG,EACH,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,SAAS,EACT,KAAK,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,EACzB,aAAa,EACb,oBAAoB,EACpB,KAAK,EACL,cAAc,EACd,UAAU,EACV,UAAU,EACV,eAAe,EACf,MAAM,EACN,WAAW,EACX,eAAe,EACf,OAAO,EACP,aAAa,EACb,WAAW,EACX,GAAG,EACH,YAAY,EACZ,QAAQ,EACR,MAAM,UAAU,CAAC;AAElB,YAAY,EACX,kBAAkB,EAClB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EACN,wBAAwB,EACxB,6BAA6B,EAC7B,gCAAgC,EAChC,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,kCAAkC,EAClC,UAAU,EACV,yBAAyB,EACzB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACX,eAAe,EACf,QAAQ,EACR,GAAG,EACH,YAAY,EACZ,SAAS,EACT,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACN,GAAG,EACH,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,SAAS,EACT,KAAK,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,EACzB,aAAa,EACb,oBAAoB,EACpB,KAAK,EACL,cAAc,EACd,UAAU,EACV,UAAU,EACV,eAAe,EACf,MAAM,EACN,WAAW,EACX,eAAe,EACf,OAAO,EACP,aAAa,EACb,WAAW,EACX,GAAG,EACH,YAAY,EACZ,QAAQ,EACR,MAAM,UAAU,CAAC;AAElB,YAAY,EACX,kBAAkB,EAClB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,OAAO,EACN,wBAAwB,EACxB,6BAA6B,EAC7B,gCAAgC,EAChC,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAGjI,OAAO,EACN,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,kCAAkC,EAClC,UAAU,EACV,yBAAyB,EACzB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACX,eAAe,EACf,QAAQ,EACR,GAAG,EACH,YAAY,EACZ,SAAS,EACT,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,MAAM,SAAS,CAAC"}