4190fbcf90
- kbdb-client requireOwner 守衛:漏 owner 插件端即 throw,不再靜默送 owner:'' - owner 一路 thread:persistNodes/ingestEnvelope/entity-crud/triplet-crud 全必填 - 病灶修:POST /triplets/ingest 原本沒傳 owner→加 owner_id query+400 - 寫入 route 缺 owner→400;vitest 23→27 - 註:gloss-bridge 分支的 backfill/gloss-entry 另需套 owner 必填(附說明),不跨支疊改
71 lines
2.9 KiB
TypeScript
71 lines
2.9 KiB
TypeScript
// Pending Alias — 零 SQL / 零 D1。全走基本盤 API(template=entity_pending record)。
|
||
//
|
||
// base 缺口 [→arcrun]:無 DELETE /records/:id → pending record 無法硬刪。
|
||
// confirm/reject 採 soft:執行動作但不刪 pending(待 base 補 DELETE)。
|
||
// 故 getPendingAliases 須由 caller 自行過濾已處理者,或待 DELETE 補上後在此硬刪。
|
||
|
||
import type { Entity, PendingAlias } from '../types';
|
||
import type { KbdbClient } from '../lib/kbdb-client';
|
||
import { TPL_ENTITY_PENDING, ensurePluginTemplates } from '../lib/templates';
|
||
import { createEntity, addAlias } from './entity-crud';
|
||
|
||
/** 建立 Pending Alias 記錄(一筆 entity_pending record)。 */
|
||
export async function createPendingAlias(
|
||
client: KbdbClient,
|
||
rawName: string,
|
||
candidateEntityId: string,
|
||
candidateCanonical: string,
|
||
similarity: number,
|
||
owner: string, // 必經:pending record 也不可無主
|
||
): Promise<PendingAlias> {
|
||
await ensurePluginTemplates(client);
|
||
const id = await client.createRecord(
|
||
TPL_ENTITY_PENDING,
|
||
{
|
||
raw_name: rawName,
|
||
candidate_entity_id: candidateEntityId,
|
||
candidate_canonical: candidateCanonical,
|
||
similarity: String(similarity),
|
||
},
|
||
owner, // 必經:createRecord 內 requireOwner 守衛
|
||
);
|
||
return {
|
||
id,
|
||
raw_name: rawName,
|
||
candidate_entity_id: candidateEntityId,
|
||
candidate_canonical: candidateCanonical,
|
||
similarity,
|
||
created_at: Math.floor(Date.now() / 1000),
|
||
};
|
||
}
|
||
|
||
/** 列出所有 Pending Aliases。 */
|
||
export async function getPendingAliases(client: KbdbClient, limit = 100, owner?: string): Promise<PendingAlias[]> {
|
||
const records = await client.listRecordsByTemplate(TPL_ENTITY_PENDING, owner);
|
||
return records
|
||
.filter((r) => r.values.raw_name)
|
||
.map((r) => ({
|
||
id: r.record_id,
|
||
raw_name: r.values.raw_name,
|
||
candidate_entity_id: r.values.candidate_entity_id ?? '',
|
||
candidate_canonical: r.values.candidate_canonical ?? '',
|
||
similarity: parseFloat(r.values.similarity ?? '0'),
|
||
created_at: 0,
|
||
}))
|
||
.slice(0, limit);
|
||
}
|
||
|
||
/** 確認 → addAlias 到候選 entity。pending soft 保留([→arcrun] base 缺 DELETE record)。 */
|
||
export async function confirmPendingAlias(client: KbdbClient, pendingId: string, owner: string): Promise<void> {
|
||
const rec = await client.getRecord(pendingId);
|
||
if (!rec || !rec.values.raw_name) throw new Error(`Pending alias ${pendingId} not found`);
|
||
await addAlias(client, rec.values.candidate_entity_id, rec.values.raw_name, owner);
|
||
}
|
||
|
||
/** 拒絕 → 以 raw_name 建新 entity。pending soft 保留([→arcrun] base 缺 DELETE record)。 */
|
||
export async function rejectPendingAlias(client: KbdbClient, pendingId: string, owner: string): Promise<Entity> {
|
||
const rec = await client.getRecord(pendingId);
|
||
if (!rec || !rec.values.raw_name) throw new Error(`Pending alias ${pendingId} not found`);
|
||
return createEntity(client, rec.values.raw_name, owner);
|
||
}
|