refactor(v026): #22 NoteRepository hydrate row type 통일

db.prepare().all() 의 row type cast s any[] / s unknown[] →
s Record<string, unknown>[] 일괄 통일. hydrate() signature 도 동일
(이미 그렇거나 갱신).

- TS strict 환경 친화 (any 보다 narrow)
- 향후 explicit row interface 추가 시 base 형 명확
- runtime 동작 변경 0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-05 01:33:30 +09:00
parent 05c45c1e10
commit 983306e004

View File

@@ -78,7 +78,7 @@ export class NoteRepository {
}
findById(id: string): Note | null {
const row = this.db.prepare('SELECT * FROM notes WHERE id=?').get(id) as any;
const row = this.db.prepare('SELECT * FROM notes WHERE id=?').get(id) as Record<string, unknown>;
if (!row) return null;
return this.hydrate(row);
}
@@ -92,21 +92,21 @@ export class NoteRepository {
WHERE deleted_at IS NULL AND created_at < ?
ORDER BY created_at DESC, id DESC LIMIT ?`
)
.all(opts.cursor, limit) as any[])
.all(opts.cursor, limit) as Record<string, unknown>[])
: (this.db
.prepare(
`SELECT * FROM notes
WHERE deleted_at IS NULL
ORDER BY created_at DESC, id DESC LIMIT ?`
)
.all(limit) as any[]);
.all(limit) as Record<string, unknown>[]);
return rows.map((r) => this.hydrate(r));
}
listAll(): Note[] {
const rows = this.db
.prepare(`SELECT * FROM notes WHERE deleted_at IS NULL ORDER BY created_at ASC, id ASC`)
.all() as any[];
.all() as Record<string, unknown>[];
return rows.map((r) => this.hydrate(r));
}
@@ -453,7 +453,7 @@ export class NoteRepository {
const limit = Math.max(1, Math.min(200, opts.limit));
const rows = this.db
.prepare(`SELECT * FROM notes WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC, id DESC LIMIT ?`)
.all(limit) as any[];
.all(limit) as Record<string, unknown>[];
return rows.map((r) => this.hydrate(r));
}
@@ -611,18 +611,18 @@ export class NoteRepository {
AND ai_status = 'done'
ORDER BY created_at DESC, id DESC`
)
.all(today) as any[];
.all(today) as Record<string, unknown>[];
return rows.map((r) => this.hydrate(r));
}
getAllPendingJobs(): Array<{ noteId: string; attempts: number; nextRunAt: string }> {
const rows = this.db
.prepare(`SELECT note_id, attempts, next_run_at FROM pending_jobs`)
.all() as any[];
.all() as Record<string, unknown>[];
return rows.map((r) => ({
noteId: r.note_id,
attempts: r.attempts,
nextRunAt: r.next_run_at
noteId: r.note_id as string,
attempts: r.attempts as number,
nextRunAt: r.next_run_at as string
}));
}
@@ -638,39 +638,39 @@ export class NoteRepository {
.run(nextRunAt, lastError.slice(0, 500), noteId);
}
private hydrate(row: any): Note {
private hydrate(row: Record<string, unknown>): Note {
const tags = this.db
.prepare(
`SELECT t.name, nt.source
FROM note_tags nt JOIN tags t ON t.id = nt.tag_id
WHERE nt.note_id = ? ORDER BY t.name`
)
.all(row.id) as Array<{ name: string; source: 'ai' | 'user' }>;
.all(row.id as string) as Array<{ name: string; source: 'ai' | 'user' }>;
const media = this.db
.prepare(
`SELECT id, kind, rel_path as relPath, mime, bytes FROM media WHERE note_id=?`
)
.all(row.id) as NoteMedia[];
.all(row.id as string) as NoteMedia[];
return {
id: row.id,
rawText: row.raw_text,
aiTitle: row.ai_title,
aiSummary: row.ai_summary,
aiStatus: row.ai_status,
aiError: row.ai_error,
aiProvider: row.ai_provider,
aiGeneratedAt: row.ai_generated_at,
titleEditedByUser: row.title_edited_by_user === 1,
summaryEditedByUser: row.summary_edited_by_user === 1,
userIntent: row.user_intent,
intentPromptedAt: row.intent_prompted_at,
dueDate: row.due_date ?? null,
dueDateEditedByUser: row.due_date_edited_by_user === 1,
deletedAt: row.deleted_at ?? null,
lastRecalledAt: row.last_recalled_at ?? null,
recallDismissedAt: row.recall_dismissed_at ?? null,
createdAt: row.created_at,
updatedAt: row.updated_at,
id: row.id as string,
rawText: row.raw_text as string,
aiTitle: row.ai_title as string | null,
aiSummary: row.ai_summary as string | null,
aiStatus: row.ai_status as 'pending' | 'done' | 'failed',
aiError: row.ai_error as string | null,
aiProvider: row.ai_provider as string | null,
aiGeneratedAt: row.ai_generated_at as string | null,
titleEditedByUser: (row.title_edited_by_user as number) === 1,
summaryEditedByUser: (row.summary_edited_by_user as number) === 1,
userIntent: row.user_intent as string | null,
intentPromptedAt: row.intent_prompted_at as string | null,
dueDate: (row.due_date as string | null) ?? null,
dueDateEditedByUser: (row.due_date_edited_by_user as number) === 1,
deletedAt: (row.deleted_at as string | null) ?? null,
lastRecalledAt: (row.last_recalled_at as string | null) ?? null,
recallDismissedAt: (row.recall_dismissed_at as string | null) ?? null,
createdAt: row.created_at as string,
updatedAt: row.updated_at as string,
tags: tags as NoteTag[],
media
};