autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
125 lines (124 loc) • 4.82 kB
JavaScript
/**
* SearchRepoAdapter — SearchEngine 用的轻量级仓储适配器
*
* 当完整的 KnowledgeRepositoryImpl / RecipeSourceRefRepositoryImpl 不可用时
* (例如单元测试中只传入 raw db),SearchEngine 自动使用这些适配器。
* 放在 lib/repository/ 下,允许使用 raw SQL(lint 白名单目录)。
*/
/** 解包 DatabaseConnection → raw SearchDb(若已是 raw db 则直接返回) */
export function unwrapSearchDb(db) {
return typeof db.getDb === 'function' ? db.getDb() : db;
}
/**
* 通用 db 解包:接受 raw db 或 { getDb() } wrapper,返回 raw db。
* 可用于 SearchDb、DatabaseLike 等不同 db 类型的构造函数。
*/
export function unwrapRawDb(db) {
if (db !== null &&
db !== undefined &&
typeof db === 'object' &&
'getDb' in db &&
typeof db.getDb === 'function') {
return db.getDb();
}
return db;
}
/**
* Raw-db 适配器:实现 SearchKnowledgeRepo 接口
* 仅在 KnowledgeRepositoryImpl 不可用时降级使用。
*/
export class RawDbKnowledgeAdapter {
#db;
constructor(db) {
this.#db = db;
}
findNonDeprecatedSync() {
return this.#db
.prepare(`SELECT id, title, description, language, category, knowledgeType, kind,
content, lifecycle, tags, trigger, difficulty, quality, stats,
updatedAt, createdAt
FROM knowledge_entries WHERE lifecycle != 'deprecated'`)
.all();
}
keywordSearchSync(pattern, limit) {
return this.#db
.prepare(`SELECT id, title, description, language, category, knowledgeType, kind, lifecycle as status, content, trigger, headers, moduleName, 'knowledge' as type
FROM knowledge_entries
WHERE lifecycle != 'deprecated' AND (title LIKE ? ESCAPE '\\' OR description LIKE ? ESCAPE '\\' OR trigger LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
LIMIT ?`)
.all(pattern, pattern, pattern, pattern, limit);
}
findByIdsDetailSync(ids) {
if (ids.length === 0) {
return [];
}
const placeholders = ids.map(() => '?').join(',');
return this.#db
.prepare(`SELECT id, content, description, trigger, headers, moduleName,
tags, language, category, updatedAt, createdAt, quality, stats, difficulty,
whenClause, doClause
FROM knowledge_entries WHERE id IN (${placeholders})`)
.all(...ids);
}
findUpdatedSinceSync(sinceIso) {
return this.#db
.prepare(`SELECT id, title, description, language, category, knowledgeType, kind,
content, lifecycle, tags, trigger, difficulty, quality, stats,
updatedAt, createdAt
FROM knowledge_entries WHERE updatedAt > ?`)
.all(sinceIso);
}
}
/**
* Raw-db 适配器:实现 SearchSourceRefRepo 接口
* 仅在 RecipeSourceRefRepositoryImpl 不可用时降级使用。
*/
export class RawDbSourceRefAdapter {
#db;
constructor(db) {
this.#db = db;
}
findActiveByRecipeIds(ids) {
if (ids.length === 0) {
return [];
}
const placeholders = ids.map(() => '?').join(',');
return this.#db
.prepare(`SELECT recipe_id, source_path, status, new_path
FROM recipe_source_refs
WHERE recipe_id IN (${placeholders}) AND status != 'stale'`)
.all(...ids);
}
}
/**
* Raw-db 适配器:实现 GuardKnowledgeRepo 接口
* 仅在 KnowledgeRepositoryImpl 不可用时降级使用。
*/
export class RawDbGuardAdapter {
#db;
constructor(db) {
this.#db = db;
}
findGuardRulesSync(lifecycles) {
const placeholders = lifecycles.map(() => '?').join(',');
return this.#db
.prepare(`SELECT id, title, description, language, scope, constraints, lifecycle
FROM knowledge_entries WHERE kind = 'rule' AND lifecycle IN (${placeholders})`)
.all(...lifecycles);
}
incrementGuardHitsSync(id, hits) {
this.#db
.prepare(`UPDATE knowledge_entries
SET stats = json_set(COALESCE(stats, '{}'), '$.guardHits', COALESCE(json_extract(stats, '$.guardHits'), 0) + ?),
updatedAt = strftime('%s', 'now')
WHERE id = ?`)
.run(hits, id);
}
}
/* ═══ Vector Sync 适配器 ═══════════════════════════════════ */
/** 从 raw db 查询非 deprecated 的基本条目信息(SyncCoordinator 对账用) */
export function queryNonDeprecatedEntries(db) {
return db
.prepare(`SELECT id, title, content, kind FROM knowledge_entries WHERE lifecycle != 'deprecated'`)
.all();
}