Use node:Sqlite
This commit is contained in:
Generated
+11
-1
@@ -16,7 +16,8 @@
|
||||
"debug": "^4.4.3",
|
||||
"dotenv": "^17.4.2",
|
||||
"hono": "^4.12.26",
|
||||
"kysely": "^0.29.2",
|
||||
"kysely": "^0.29.4",
|
||||
"kysely-node-native-sqlite": "^1.0.1",
|
||||
"ws": "^8.21.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
@@ -8314,6 +8315,15 @@
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/kysely-node-native-sqlite": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/kysely-node-native-sqlite/-/kysely-node-native-sqlite-1.0.1.tgz",
|
||||
"integrity": "sha512-FHty4bi885waQMMMw2P9F+mqZUdozd1OBWQI72CffsQzbZhcOgsz3jpZRTUM1xAtG1wkz/VQAj3JavQvCdhL2w==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"kysely": ">=0.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/language-subtag-registry": {
|
||||
"version": "0.3.23",
|
||||
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
|
||||
|
||||
+2
-1
@@ -51,7 +51,8 @@
|
||||
"debug": "^4.4.3",
|
||||
"dotenv": "^17.4.2",
|
||||
"hono": "^4.12.26",
|
||||
"kysely": "^0.29.2",
|
||||
"kysely": "^0.29.4",
|
||||
"kysely-node-native-sqlite": "^1.0.1",
|
||||
"ws": "^8.21.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { Config } from './services/config.ts';
|
||||
import { Database, MigrationService } from './services/storage/index.ts';
|
||||
import { Logger } from './utils/logger.ts';
|
||||
|
||||
/** Application composition root. */
|
||||
export class App {
|
||||
/**
|
||||
* Construct infrastructure and validate all routes before returning a runnable app.
|
||||
*/
|
||||
static async create(): Promise<App> {
|
||||
const config = Config.fromEnv();
|
||||
const debug = new Logger('sync-server-v2');
|
||||
debug('config loaded: %O', config);
|
||||
|
||||
// Persistence must be ready before routes accept traffic.
|
||||
const database = new Database({ path: config.database.path, debug });
|
||||
const migrations = new MigrationService(database, debug);
|
||||
await migrations.migrateToLatest();
|
||||
|
||||
return new App(database);
|
||||
}
|
||||
|
||||
private stopPromise: Promise<void> | undefined;
|
||||
|
||||
constructor(private readonly database: Database) {}
|
||||
|
||||
async start(): Promise<void> {}
|
||||
|
||||
/** Stop transports before releasing the database they may still use. */
|
||||
async stop(): Promise<void> {
|
||||
this.stopPromise ??= this.database.destroy();
|
||||
await this.stopPromise;
|
||||
}
|
||||
}
|
||||
|
||||
const app = await App.create();
|
||||
let shuttingDown = false;
|
||||
|
||||
const shutdown = (signal: NodeJS.Signals): void => {
|
||||
if (shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
shuttingDown = true;
|
||||
void app.stop().catch((error) => {
|
||||
console.error('Graceful shutdown failed after ' + signal, error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
};
|
||||
|
||||
process.once('SIGINT', (): void => shutdown('SIGINT'));
|
||||
process.once('SIGTERM', (): void => shutdown('SIGTERM'));
|
||||
|
||||
try {
|
||||
await app.start();
|
||||
} catch (error: unknown) {
|
||||
await app.stop();
|
||||
throw error;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import DatabaseConstructor from 'better-sqlite3';
|
||||
import { Kysely, SqliteDialect } from 'kysely';
|
||||
import { CompiledQuery, Kysely } from 'kysely';
|
||||
import { NodeNativeSqliteDialect } from 'kysely-node-native-sqlite';
|
||||
|
||||
import type { DatabaseTables } from './tables.ts';
|
||||
import type { Logger } from '../../utils/logger.ts';
|
||||
|
||||
@@ -13,13 +14,13 @@ export type DatabaseOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Thin wrapper around better-sqlite3 and Kysely.
|
||||
* Thin wrapper around Kysely and NodeNativeSqliteDialect (which uses node:sqlite).
|
||||
*
|
||||
* Owns connection setup, pragma configuration, and graceful teardown.
|
||||
*/
|
||||
export class Database {
|
||||
private readonly debug: Logger;
|
||||
private readonly sqlite: DatabaseConstructor.Database;
|
||||
private readonly dialect: NodeNativeSqliteDialect;
|
||||
private readonly kysely: Kysely<DatabaseTables>;
|
||||
|
||||
/**
|
||||
@@ -32,13 +33,15 @@ export class Database {
|
||||
this.debug = options.debug.extend('database');
|
||||
|
||||
// Create the SQLite database.
|
||||
this.sqlite = new DatabaseConstructor(options.path);
|
||||
this.configurePragmas();
|
||||
this.dialect = new NodeNativeSqliteDialect(options.path);
|
||||
|
||||
// Create the Kysely database.
|
||||
this.kysely = new Kysely<DatabaseTables>({
|
||||
dialect: new SqliteDialect({ database: this.sqlite }),
|
||||
dialect: this.dialect,
|
||||
});
|
||||
|
||||
// Configure the SQLite pragmas.
|
||||
this.configurePragmas();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +68,8 @@ export class Database {
|
||||
*/
|
||||
private configurePragmas(): void {
|
||||
this.debug('configuring SQLite pragmas');
|
||||
this.sqlite.pragma('journal_mode = WAL');
|
||||
this.sqlite.pragma('foreign_keys = ON');
|
||||
|
||||
this.kysely.executeQuery(CompiledQuery.raw('PRAGMA journal_mode = WAL'));
|
||||
this.kysely.executeQuery(CompiledQuery.raw('PRAGMA foreign_keys = ON'));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
"declarationMap": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": ["node_modules/**/*", "dist/**/*", "test"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user