feat(02-01): create database connection factory
- src/db/config.ts: getDbPath() returns ~/.cw/data/cw.db with CW_DB_PATH override - src/db/index.ts: createDatabase() factory with WAL mode and foreign keys - drizzle.config.ts: Drizzle Kit configuration for migrations Pattern: Factory function (not singleton) allows isolated test instances
This commit is contained in:
12
drizzle.config.ts
Normal file
12
drizzle.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from 'drizzle-kit';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: './src/db/schema.ts',
|
||||||
|
out: './drizzle',
|
||||||
|
dialect: 'sqlite',
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.CW_DB_PATH ?? join(homedir(), '.cw', 'data', 'cw.db'),
|
||||||
|
},
|
||||||
|
});
|
||||||
33
src/db/config.ts
Normal file
33
src/db/config.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { mkdirSync } from 'node:fs';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the database path.
|
||||||
|
*
|
||||||
|
* - Default: ~/.cw/data/cw.db
|
||||||
|
* - Override via CW_DB_PATH environment variable
|
||||||
|
* - For testing, pass ':memory:' as CW_DB_PATH
|
||||||
|
*/
|
||||||
|
export function getDbPath(): string {
|
||||||
|
const envPath = process.env.CW_DB_PATH;
|
||||||
|
if (envPath) {
|
||||||
|
return envPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return join(homedir(), '.cw', 'data', 'cw.db');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the parent directory for the database file exists.
|
||||||
|
* No-op for in-memory databases.
|
||||||
|
*/
|
||||||
|
export function ensureDbDirectory(dbPath: string): void {
|
||||||
|
// Skip for in-memory database
|
||||||
|
if (dbPath === ':memory:') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dir = dirname(dbPath);
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
43
src/db/index.ts
Normal file
43
src/db/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import Database from 'better-sqlite3';
|
||||||
|
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||||
|
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
||||||
|
|
||||||
|
import { getDbPath, ensureDbDirectory } from './config.js';
|
||||||
|
import * as schema from './schema.js';
|
||||||
|
|
||||||
|
export type DrizzleDatabase = BetterSQLite3Database<typeof schema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new database connection.
|
||||||
|
*
|
||||||
|
* This is a factory function (not a singleton) to allow multiple instances
|
||||||
|
* for testing with isolated databases.
|
||||||
|
*
|
||||||
|
* @param path - Optional path override. Defaults to getDbPath().
|
||||||
|
* Use ':memory:' for in-memory testing database.
|
||||||
|
* @returns Drizzle database instance with schema
|
||||||
|
*/
|
||||||
|
export function createDatabase(path?: string): DrizzleDatabase {
|
||||||
|
const dbPath = path ?? getDbPath();
|
||||||
|
|
||||||
|
// Ensure directory exists for file-based databases
|
||||||
|
ensureDbDirectory(dbPath);
|
||||||
|
|
||||||
|
// Create SQLite connection
|
||||||
|
const sqlite = new Database(dbPath);
|
||||||
|
|
||||||
|
// Enable WAL mode for better concurrent read performance
|
||||||
|
sqlite.pragma('journal_mode = WAL');
|
||||||
|
|
||||||
|
// Enable foreign keys (SQLite has them disabled by default)
|
||||||
|
sqlite.pragma('foreign_keys = ON');
|
||||||
|
|
||||||
|
// Create Drizzle instance with schema
|
||||||
|
return drizzle(sqlite, { schema });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export config utilities
|
||||||
|
export { getDbPath, ensureDbDirectory } from './config.js';
|
||||||
|
|
||||||
|
// Re-export schema and types
|
||||||
|
export * from './schema.js';
|
||||||
15
src/db/schema.ts
Normal file
15
src/db/schema.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Database schema for Codewalk District.
|
||||||
|
*
|
||||||
|
* Defines the four-level task hierarchy:
|
||||||
|
* - Initiative: Top-level project
|
||||||
|
* - Phase: Major milestone within initiative
|
||||||
|
* - Plan: Group of related tasks within phase
|
||||||
|
* - Task: Individual work item
|
||||||
|
*
|
||||||
|
* Schema will be defined in Task 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Placeholder export to allow index.ts to compile
|
||||||
|
// Full schema defined in Task 3
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user