feat: first cut at the full renderer

This commit is contained in:
2026-02-20 17:54:04 +01:00
parent 22cb63e0a7
commit 3bbc5281e8
25 changed files with 4989 additions and 976 deletions

View File

@@ -0,0 +1,36 @@
import { getDatabase } from './connection';
export async function getGeneratedFileHash(projectId: string, relativePath: string): Promise<string | null> {
const client = getDatabase().getLocalClient();
if (!client) {
throw new Error('Database client not available');
}
const result = await client.execute({
sql: 'SELECT content_hash FROM generated_file_hashes WHERE project_id = ? AND relative_path = ? LIMIT 1',
args: [projectId, relativePath],
});
if (!result.rows[0] || typeof result.rows[0].content_hash !== 'string') {
return null;
}
return result.rows[0].content_hash;
}
export async function setGeneratedFileHash(projectId: string, relativePath: string, hash: string): Promise<void> {
const client = getDatabase().getLocalClient();
if (!client) {
throw new Error('Database client not available');
}
await client.execute({
sql: `
INSERT INTO generated_file_hashes (project_id, relative_path, content_hash, updated_at)
VALUES (?, ?, ?, ?)
ON CONFLICT(project_id, relative_path)
DO UPDATE SET content_hash = excluded.content_hash, updated_at = excluded.updated_at
`,
args: [projectId, relativePath, hash, Date.now()],
});
}

View File

@@ -1,2 +1,3 @@
export * from './schema';
export * from './connection';
export * from './generatedFileHashStore';

View File

@@ -73,6 +73,16 @@ export const settings = sqliteTable('settings', {
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
});
// Generated file hashes - tracks html/xml output content hashes to skip unchanged writes
export const generatedFileHashes = sqliteTable('generated_file_hashes', {
projectId: text('project_id').notNull(),
relativePath: text('relative_path').notNull(),
contentHash: text('content_hash').notNull(),
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
}, (table) => ({
projectPathIdx: uniqueIndex('generated_file_hashes_project_path_idx').on(table.projectId, table.relativePath),
}));
// Post links - tracks internal links between posts
export const postLinks = sqliteTable('post_links', {
id: text('id').primaryKey(),
@@ -150,6 +160,8 @@ export type Media = typeof media.$inferSelect;
export type NewMedia = typeof media.$inferInsert;
export type Setting = typeof settings.$inferSelect;
export type NewSetting = typeof settings.$inferInsert;
export type GeneratedFileHash = typeof generatedFileHashes.$inferSelect;
export type NewGeneratedFileHash = typeof generatedFileHashes.$inferInsert;
export type PostLink = typeof postLinks.$inferSelect;
export type NewPostLink = typeof postLinks.$inferInsert;
export type PostMediaLink = typeof postMedia.$inferSelect;