feat(pulse): abstract PulseDatabase interface from bun:sqlite
CI / test (push) Has been cancelled

Phase 1 of pulseflare migration: decouple core logic from bun:sqlite.

- Add PulseDatabase/PulseStatement interfaces in database.ts
- Replace 'import { Database } from bun:sqlite' with PulseDatabase alias
  in defs.ts, projection-engine.ts, guard-projection.ts
- Update PulseStore.getDatabase() and ScopedStore.scopeDatabase() return
  types to PulseDatabase
- store.ts retains bun:sqlite as concrete implementation
- Export PulseDatabase/PulseStatement from package index
- All existing tests pass unchanged
This commit is contained in:
2026-04-20 01:10:52 +00:00
parent 75dc04a51d
commit 03ee151c72
9 changed files with 38 additions and 11 deletions
+21
View File
@@ -0,0 +1,21 @@
/**
* Abstract database interface for Pulse.
*
* Decouples core logic from bun:sqlite so alternative backends
* (e.g. Cloudflare D1) can provide their own implementation.
*/
export interface PulseStatement {
get(...params: any[]): any;
all(...params: any[]): any[];
run(...params: any[]): { changes: number; lastInsertRowid: number | bigint };
}
export interface PulseDatabase {
exec(sql: string): void;
prepare(sql: string): PulseStatement;
run(sql: string, ...params: any[]): void;
transaction<T>(fn: () => T): (...args: any[]) => T;
close(): void;
readonly inTransaction: boolean;
}
+1 -1
View File
@@ -4,7 +4,7 @@
* Append-only definition tables for objects, events, and projections.
* Content-addressed versioning with code_rev binding.
*/
import type { Database } from 'bun:sqlite';
import type { PulseDatabase as Database } from './database.js';
export interface ObjectDef {
name: string;
codeRev: string;
+1 -1
View File
@@ -5,9 +5,9 @@
* Content-addressed versioning with code_rev binding.
*/
import type { Database } from 'bun:sqlite';
import { createHash } from 'node:crypto';
import jsonata from 'jsonata';
import type { PulseDatabase as Database } from './database.js';
// ── Types ──────────────────────────────────────────────────────
+1 -1
View File
@@ -3,7 +3,7 @@
* Core logic delegated to guard-core.ts (pure, portable).
*/
import type { Database } from 'bun:sqlite';
import type { PulseDatabase as Database } from './database.js';
import {
checkGuardsCore,
clearGuardExpressionCache as clearCoreCache,
+4
View File
@@ -899,6 +899,10 @@ export function createRule<S, E, T>(
return (prev, curr, inner) => logic(accessor(prev), accessor(curr), inner);
}
// ── Database Abstraction ────────────────────────────────────────
export type { PulseDatabase, PulseStatement } from './database.js';
// ── Storage ────────────────────────────────────────────────────
export {
+1 -1
View File
@@ -4,7 +4,7 @@
* Incremental fold engine for projections with JSONata expressions.
* Projections are first-class citizens with their own state table.
*/
import type { Database } from 'bun:sqlite';
import type { PulseDatabase as Database } from './database.js';
/** Clear the compiled expression cache (useful for testing). */
export declare function clearExpressionCache(): void;
export interface ProjectionState {
+1 -1
View File
@@ -5,7 +5,7 @@
* Projections are first-class citizens with their own state table.
*/
import type { Database } from 'bun:sqlite';
import type { PulseDatabase as Database } from './database.js';
import jsonata, { type Expression } from 'jsonata';
import { getProjectionDef } from './defs.js';
+3 -2
View File
@@ -5,6 +5,7 @@
* content-addressed object store (CAS) on disk via SHA-256 hashes.
*/
import { Database } from 'bun:sqlite';
import type { PulseDatabase } from './database.js';
export interface EventRecord {
id: number;
occurredAt: number;
@@ -99,8 +100,8 @@ export interface CreateScopedStoreOptions {
export interface ScopedStore {
scope(name: string): PulseStore;
listScopes(): string[];
/** Get underlying Database for scope (used by projection engine) */
scopeDatabase(name: string): Database;
/** Get underlying database for scope (used by projection engine) */
scopeDatabase(name: string): PulseDatabase;
putObject(data: unknown): Promise<string>;
getObject(hash: string): Promise<unknown | null>;
close(): Promise<void>;
+5 -4
View File
@@ -6,6 +6,7 @@
*/
import { Database } from 'bun:sqlite';
import type { PulseDatabase } from './database.js';
import { createHash } from 'node:crypto';
import {
existsSync,
@@ -108,8 +109,8 @@ export interface PulseStore {
/** Read data from CAS store by hash. Returns null if not found. */
getObject(hash: string): Promise<unknown | null>;
/** Get the underlying bun:sqlite Database handle (for guard projections etc.) */
getDatabase(): Database;
/** Get the underlying database handle (for guard projections etc.) */
getDatabase(): PulseDatabase;
/** Close the database */
close(): Promise<void>;
@@ -544,8 +545,8 @@ export interface ScopedStore {
scope(name: string): PulseStore;
listScopes(): string[];
/** Get underlying Database for scope (used by projection engine) */
scopeDatabase(name: string): Database;
/** Get underlying database for scope (used by projection engine) */
scopeDatabase(name: string): PulseDatabase;
putObject(data: unknown): Promise<string>;
getObject(hash: string): Promise<unknown | null>;