Finalize Dexie-to-SQLite migration prep: docs, circular dep removal, SQL helpers, tests

- Removed all vestigial Dexie/USE_DEXIE_DB references from code and docs
- Centralized DB logic in PlatformServiceMixin; resolved logger/databaseUtil circular dependency
- Modularized SQL helpers (`$generateInsertStatement`, `$generateUpdateStatement`) and added unit tests
- Created/updated migration tracking docs and helper script for cross-machine progress
- Confirmed all lint/type checks and tests pass; ready for systematic file migration
This commit is contained in:
Matthew Raymer
2025-07-06 09:44:20 +00:00
parent 72041f29e1
commit 64e78fdbce
27 changed files with 8854 additions and 295 deletions

View File

@@ -48,7 +48,10 @@ import { MASTER_SETTINGS_KEY, type Settings } from "@/db/tables/settings";
import { logger } from "@/utils/logger";
import { Contact } from "@/db/tables/contacts";
import { QueryExecResult, DatabaseExecResult } from "@/interfaces/database";
import { memoryLogs } from "@/db/databaseUtil";
import {
generateInsertStatement,
generateUpdateStatement,
} from "@/utils/sqlHelpers";
// =================================================
// TYPESCRIPT INTERFACES
@@ -93,6 +96,8 @@ const CACHE_DEFAULTS = {
default: 15000, // 15 seconds default TTL
} as const;
const _memoryLogs: string[] = [];
/**
* Enhanced mixin that provides cached platform service access and utility methods
* with smart caching layer for ultimate performance optimization
@@ -123,7 +128,7 @@ export const PlatformServiceMixin = {
* Provides direct access to memoryLogs without requiring databaseUtil import
*/
$memoryLogs(): string[] {
return memoryLogs;
return _memoryLogs;
},
/**
@@ -1117,6 +1122,40 @@ export const PlatformServiceMixin = {
async $logAndConsole(message: string, isError = false): Promise<void> {
return logger.toConsoleAndDb(message, isError);
},
$appendToMemoryLogs(message: string): void {
_memoryLogs.push(`${new Date().toISOString()}: ${message}`);
if (_memoryLogs.length > 1000) {
_memoryLogs.splice(0, _memoryLogs.length - 1000);
}
},
/**
* Public wrapper for generateInsertStatement
*/
$generateInsertStatement(
model: Record<string, unknown>,
tableName: string,
): { sql: string; params: unknown[] } {
return generateInsertStatement(model, tableName);
},
/**
* Public wrapper for generateUpdateStatement
*/
$generateUpdateStatement(
model: Record<string, unknown>,
tableName: string,
whereClause: string,
whereParams: unknown[] = [],
): { sql: string; params: unknown[] } {
return generateUpdateStatement(
model,
tableName,
whereClause,
whereParams,
);
},
},
};
@@ -1190,6 +1229,18 @@ export interface IPlatformServiceMixin {
// New additions
$logs(): Promise<Array<Record<string, unknown>>>;
// New additions
$generateInsertStatement(
model: Record<string, unknown>,
tableName: string,
): { sql: string; params: unknown[] };
$generateUpdateStatement(
model: Record<string, unknown>,
tableName: string,
whereClause: string,
whereParams?: unknown[],
): { sql: string; params: unknown[] };
}
// TypeScript declaration merging to eliminate (this as any) type assertions
@@ -1296,5 +1347,17 @@ declare module "@vue/runtime-core" {
// New additions
$logs(): Promise<Array<Record<string, unknown>>>;
// New additions
$generateInsertStatement(
model: Record<string, unknown>,
tableName: string,
): { sql: string; params: unknown[] };
$generateUpdateStatement(
model: Record<string, unknown>,
tableName: string,
whereClause: string,
whereParams?: unknown[],
): { sql: string; params: unknown[] };
}
}

View File

@@ -11,6 +11,19 @@
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
const _memoryLogs: string[] = [];
export function appendToMemoryLogs(message: string): void {
_memoryLogs.push(`${new Date().toISOString()}: ${message}`);
if (_memoryLogs.length > 1000) {
_memoryLogs.splice(0, _memoryLogs.length - 1000);
}
}
export function getMemoryLogs(): string[] {
return [..._memoryLogs];
}
export function safeStringify(obj: unknown) {
const seen = new WeakSet();

View File

@@ -276,3 +276,4 @@ export const NotificationMixin = {
},
},
};

67
src/utils/sqlHelpers.ts Normal file
View File

@@ -0,0 +1,67 @@
/**
* SQL Statement Generation Helpers
* Provides utility functions for generating parameterized SQL INSERT and UPDATE statements.
*
* Author: Matthew Raymer
*/
/**
* Generates SQL INSERT statement and parameters from a model object
* @param model - The object to insert
* @param tableName - The table name
* @returns { sql, params } - SQL string and parameter array
*/
export function generateInsertStatement(
model: Record<string, unknown>,
tableName: string,
): { sql: string; params: unknown[] } {
const columns = Object.keys(model).filter((key) => model[key] !== undefined);
const values = Object.values(model)
.filter((value) => value !== undefined)
.map((value) => {
if (value === null || value === undefined) return null;
if (typeof value === "object" && value !== null) {
return JSON.stringify(value);
}
if (typeof value === "boolean") return value ? 1 : 0;
return value;
});
const placeholders = values.map(() => "?").join(", ");
const insertSql = `INSERT INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`;
return { sql: insertSql, params: values };
}
/**
* Generates SQL UPDATE statement and parameters from a model object
* @param model - The object with fields to update
* @param tableName - The table name
* @param whereClause - The WHERE clause (e.g. "id = ?")
* @param whereParams - Parameters for the WHERE clause
* @returns { sql, params } - SQL string and parameter array
*/
export function generateUpdateStatement(
model: Record<string, unknown>,
tableName: string,
whereClause: string,
whereParams: unknown[] = [],
): { sql: string; params: unknown[] } {
const setClauses: string[] = [];
const params: unknown[] = [];
Object.entries(model).forEach(([key, value]) => {
setClauses.push(`${key} = ?`);
let convertedValue = value ?? null;
if (convertedValue !== null) {
if (typeof convertedValue === "object") {
convertedValue = JSON.stringify(convertedValue);
} else if (typeof convertedValue === "boolean") {
convertedValue = convertedValue ? 1 : 0;
}
}
params.push(convertedValue);
});
if (setClauses.length === 0) {
throw new Error("No valid fields to update");
}
const sql = `UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE ${whereClause}`;
return { sql, params: [...params, ...whereParams] };
}