// Template + Record CRUD — 樹狀 record 模型(v7 定稿,2026-08-15 confirm)。 // // 模型(leo 定案):「真身在 pool 的 entry 裡,所有的虛擬表虛擬欄位都是指向這個 entry 的指標。」 // · record = 池中一顆有身分的 entry(record_id 就是它的 id) // · 一格 = 一條關係列(src=record、rel=field entry、dst=value entry)——池上型別化指標欄 // · 歸屬 = 一條關係列(src=record、rel=sys_belongs、dst=sheet) // · 欄位是關係、歸屬也是關係——同一種機制。entry_values 表已拆(0007), // 它是「關係」的第二套實作(D92:同一件事兩個實作必然漂移)。 // // 遷移期雙軌(第二刀收):templates 表仍是「欄位定義」的真相源(slots_json/description), // sheet/field entry 是它們在池中的身分;createTemplate/updateTemplate 同步維護兩邊, // 維護語句全部 INSERT OR IGNORE(決定性 id)⇒ 冪等、可自癒。 import type { Template } from '../types'; import { createEntry } from './entry-crud'; function uid(prefix: string): string { return `${prefix}_${crypto.randomUUID()}`; } // ── 啟動常數(0007 seed;一組、極小、只讀)───────────────────────────── export const SYS_ROOT = 'sys_root'; // 屬於鏈的終點:entry ─屬於→ sys_root = 它是 sheet export const SYS_BELONGS = 'sys_belongs'; // 歸屬謂詞:record ─屬於→ sheet export const SYS_FIELD_OF = 'sys_field_of';// 欄位名冊謂詞:field ─field_of→ sheet /** field entry 的決定性 id(0007 遷移與執行期寫入共用同一條衍生規則,兩邊永遠對得上)。 */ export function fieldEntryId(templateId: string, slot: string): string { return `fld_${templateId}_${slot}`; } /** 啟動常數自癒(冪等;空庫或部分遷移的實例第一次寫入時補齊)。 */ async function ensureAnchors(db: D1Database): Promise { await db .prepare( `INSERT OR IGNORE INTO entries (id, content, entry_type, owner_id) VALUES ('${SYS_ROOT}', 'root', 'system', NULL), ('${SYS_BELONGS}', 'belongs', 'system', NULL), ('${SYS_FIELD_OF}', 'field_of', 'system', NULL)`, ) .run(); } /** 這批 slot 的 field entry + 名冊關係(field ─field_of→ sheet)自癒建立(冪等)。 */ async function ensureFieldEntries(db: D1Database, templateId: string, slots: string[]): Promise { for (const slot of slots) { const fid = fieldEntryId(templateId, slot); await db .prepare(`INSERT OR IGNORE INTO entries (id, content, entry_type) VALUES (?, ?, 'field')`) .bind(fid, slot) .run(); await db .prepare( `INSERT OR IGNORE INTO entries (id, entry_type, src_id, rel_id, dst_id) VALUES (?, 'relation', ?, '${SYS_FIELD_OF}', ?)`, ) .bind(`relf_${templateId}_${slot}`, fid, templateId) .run(); } } // ---- Templates(遷移期雙軌:表=欄位定義真相源,池中 sheet/field entry=身分)---- export interface CreateTemplateInput { name: string; description?: string | null; slots: string[]; created_by?: string | null; id?: string; } export async function createTemplate(db: D1Database, input: CreateTemplateInput): Promise