@whispering233/ai-editor-db 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connection.d.ts +24 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +39 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.d.ts +10 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +34 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/queries/atomic.d.ts +15 -0
- package/dist/queries/atomic.d.ts.map +1 -0
- package/dist/queries/atomic.js +44 -0
- package/dist/queries/atomic.js.map +1 -0
- package/dist/queries/chat.d.ts +74 -0
- package/dist/queries/chat.d.ts.map +1 -0
- package/dist/queries/chat.js +188 -0
- package/dist/queries/chat.js.map +1 -0
- package/dist/queries/compute-state.d.ts +32 -0
- package/dist/queries/compute-state.d.ts.map +1 -0
- package/dist/queries/compute-state.js +135 -0
- package/dist/queries/compute-state.js.map +1 -0
- package/dist/queries/delta.d.ts +76 -0
- package/dist/queries/delta.d.ts.map +1 -0
- package/dist/queries/delta.js +287 -0
- package/dist/queries/delta.js.map +1 -0
- package/dist/queries/entity.d.ts +110 -0
- package/dist/queries/entity.d.ts.map +1 -0
- package/dist/queries/entity.js +285 -0
- package/dist/queries/entity.js.map +1 -0
- package/dist/queries/migration.d.ts +153 -0
- package/dist/queries/migration.d.ts.map +1 -0
- package/dist/queries/migration.js +260 -0
- package/dist/queries/migration.js.map +1 -0
- package/dist/queries/outline-ops.d.ts +126 -0
- package/dist/queries/outline-ops.d.ts.map +1 -0
- package/dist/queries/outline-ops.js +346 -0
- package/dist/queries/outline-ops.js.map +1 -0
- package/dist/queries/outline.d.ts +48 -0
- package/dist/queries/outline.d.ts.map +1 -0
- package/dist/queries/outline.js +134 -0
- package/dist/queries/outline.js.map +1 -0
- package/dist/queries/project.d.ts +21 -0
- package/dist/queries/project.d.ts.map +1 -0
- package/dist/queries/project.js +43 -0
- package/dist/queries/project.js.map +1 -0
- package/dist/queries/relation.d.ts +83 -0
- package/dist/queries/relation.d.ts.map +1 -0
- package/dist/queries/relation.js +418 -0
- package/dist/queries/relation.js.map +1 -0
- package/dist/queries/trash.d.ts +65 -0
- package/dist/queries/trash.d.ts.map +1 -0
- package/dist/queries/trash.js +121 -0
- package/dist/queries/trash.js.map +1 -0
- package/dist/schema.d.ts +29 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +102 -0
- package/dist/schema.js.map +1 -0
- package/dist/storage/atomic.d.ts +32 -0
- package/dist/storage/atomic.d.ts.map +1 -0
- package/dist/storage/atomic.js +112 -0
- package/dist/storage/atomic.js.map +1 -0
- package/dist/storage/outline.d.ts +68 -0
- package/dist/storage/outline.d.ts.map +1 -0
- package/dist/storage/outline.js +154 -0
- package/dist/storage/outline.js.map +1 -0
- package/dist/storage/project.d.ts +24 -0
- package/dist/storage/project.d.ts.map +1 -0
- package/dist/storage/project.js +55 -0
- package/dist/storage/project.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// @whispering233/ai-editor-db 状态计算(S5.2 computeState):沿大纲树父链累积 Delta 得到实体到达状态
|
|
2
|
+
//
|
|
3
|
+
// 单一事实来源:
|
|
4
|
+
// - doc/api/endpoints.md 第 464-510 行(POST /delta/compute:Req/Res、累积规则四段——
|
|
5
|
+
// 双层排序、set/update/add/remove 语义、update from 不匹配跳过 + conflicts、软删过滤)
|
|
6
|
+
// - 决策 9(只沿大纲树父链累积已确认 Delta、双层排序、op=update 校验当前值等于 from、
|
|
7
|
+
// 不匹配跳过 + skipped/conflicts 标注不返回 409、plot_edge 不参与、推演不落库)
|
|
8
|
+
// - doc/database/schema.md 第 107 行注记(状态计算规则摘要)
|
|
9
|
+
//
|
|
10
|
+
// 复用边界(不重复实现):
|
|
11
|
+
// - listDeltasByNode(delta.ts):按节点查询 + 可见性三态过滤(决策 12 修订:delta 自身 /
|
|
12
|
+
// 触发节点 / 目标实体任一软删即不可见)+ 节点内按 order 升序——computeState 的软删过滤
|
|
13
|
+
// 与「节点内 order 序」由此获得
|
|
14
|
+
// - getOutlinePathIds(storage/outline.ts):根 → at_node 树路径(严格三层下路径唯一);
|
|
15
|
+
// 节点不存在时抛错——视为调用方 bug 不捕获
|
|
16
|
+
// - getEntity(entity.ts):目标实体行(data 已解析为对象);不存在/已软删返回 null
|
|
17
|
+
//
|
|
18
|
+
// plot_edge 不参与(决策 9):本实现只读 delta_records,relation_records 的剧情连线
|
|
19
|
+
// 天然不进入累积,无需额外过滤。
|
|
20
|
+
import { listDeltasByNode } from "./delta.js";
|
|
21
|
+
import { getEntity } from "./entity.js";
|
|
22
|
+
import { getOutlinePathIds, readOutlineFile } from "../storage/outline.js";
|
|
23
|
+
/**
|
|
24
|
+
* 应用单条 change 到 state(endpoints.md 第 500-510 行四段规则 + 决策 9 修订):
|
|
25
|
+
* - `set`:state[field] = to(直接替换)
|
|
26
|
+
* - `update`:state[field] === from → state[field] = to;否则**跳过该 change**——
|
|
27
|
+
* 在 skipped 追加 { index, field, expected: from, actual } 且 conflicts 追加
|
|
28
|
+
* { deltaId, field, expected: from, actual }(继续累积后续 change/delta,不抛 409——
|
|
29
|
+
* 手动编辑 data 不产生 Delta 属正常用户行为)
|
|
30
|
+
* - `add`:state[field] 为数组 → 按 value 追加;非数组**静默跳过**(防御,不标 conflicts——
|
|
31
|
+
* 契约仅定义 update 冲突)
|
|
32
|
+
* - `remove`:state[field] 为数组 → 按值匹配移除**首个**匹配;值不存在静默忽略;
|
|
33
|
+
* 非数组静默跳过
|
|
34
|
+
* 字段约定(endpoints.md 第 407-413 行):add/remove 用 value,set/update 用 to。
|
|
35
|
+
* 防御:非对象 change 静默忽略(changes 列来自 JSON 解析,坏项不打挂整条计算)。
|
|
36
|
+
*/
|
|
37
|
+
function applyChange(state, change, index, deltaId, skipped, conflicts) {
|
|
38
|
+
if (change === null || typeof change !== "object")
|
|
39
|
+
return;
|
|
40
|
+
const { field, op } = change;
|
|
41
|
+
switch (op) {
|
|
42
|
+
case "set": // set:直接替换(to)
|
|
43
|
+
state[field] = change.to;
|
|
44
|
+
break;
|
|
45
|
+
case "update": // update:旧值 → 新值,校验当前值 === from
|
|
46
|
+
if (state[field] === change.from) {
|
|
47
|
+
state[field] = change.to;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const actual = state[field];
|
|
51
|
+
skipped.push({ index, field, expected: change.from, actual });
|
|
52
|
+
conflicts.push({ deltaId, field, expected: change.from, actual });
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case "add": // add:按 value 向数组追加(非数组静默跳过)
|
|
56
|
+
if (Array.isArray(state[field])) {
|
|
57
|
+
state[field].push(change.value);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case "remove": // remove:按值匹配移除首个(值不存在静默忽略;非数组静默跳过)
|
|
61
|
+
if (Array.isArray(state[field])) {
|
|
62
|
+
const arr = state[field];
|
|
63
|
+
const idx = arr.findIndex((v) => v === change.value);
|
|
64
|
+
if (idx !== -1)
|
|
65
|
+
arr.splice(idx, 1);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 计算实体到达指定大纲节点时的累积状态(POST /api/v1/delta/compute,endpoints.md 第 464-510 行)。
|
|
72
|
+
*
|
|
73
|
+
* **前置约定**:
|
|
74
|
+
* - atNodeId 必须存在——路由层先校验节点存在性并映射 404 OUTLINE_NODE_NOT_FOUND 后调用;
|
|
75
|
+
* getOutlinePathIds 对缺失节点抛错,视为调用方 bug,本模块不捕获。
|
|
76
|
+
* - 目标实体不存在(或已软删,getEntity 过滤)→ 返回 **null**,路由层映射 404 ENTITY_NOT_FOUND。
|
|
77
|
+
*
|
|
78
|
+
* 累积流程(决策 9 + schema.md 第 107 行):
|
|
79
|
+
* 1. state 基座 = 实体初始 data 深拷贝(structuredClone,不污染 getEntity 返回行)
|
|
80
|
+
* 2. 树路径 = getOutlinePathIds(tree, atNodeId)(根 → at_node,含 root 哨兵——无 delta 挂它,
|
|
81
|
+
* 无害);路径上每节点调 listDeltasByNode(内置可见性三态过滤与 order ASC),
|
|
82
|
+
* 过滤 target_id === targetId——天然满足「节点间树路径序 + 节点内 order 序」双层排序
|
|
83
|
+
* 与软删过滤
|
|
84
|
+
* 3. 逐 change 应用(applyChange,决策 9 修订四段语义);update 冲突跳过不打断后续累积
|
|
85
|
+
* 4. 响应:appliedDeltas 每项 { nodeId, description, changes(原样数组), skipped?(仅该 delta
|
|
86
|
+
* 有跳过时出现)};conflicts 为跨全部 delta 的扁平数组
|
|
87
|
+
*
|
|
88
|
+
* target_type 不参与过滤(仅回显):id 前缀体系(char-/set-/loc-/hook-/sc-/ch-/vol- 等)保证
|
|
89
|
+
* target_id 全局唯一,targetId 即足以定位目标;endpoints.md Req 携带 target_type 用于响应回显。
|
|
90
|
+
*
|
|
91
|
+
* @param outlineDir 项目根目录(outline.json 读取:树路径 + 节点软删校验)
|
|
92
|
+
* @returns ComputeStateResult;目标实体不存在/已软删返回 null
|
|
93
|
+
*/
|
|
94
|
+
export function computeState(db, outlineDir, input) {
|
|
95
|
+
// 1. 目标实体:不存在或已软删 → null(决策 12 过滤;路由层映射 404 ENTITY_NOT_FOUND)
|
|
96
|
+
const entity = getEntity(db, input.targetId);
|
|
97
|
+
if (entity === null)
|
|
98
|
+
return null;
|
|
99
|
+
// 2. 树路径(根 → at_node;缺失节点抛错——路由层前置校验后的调用方 bug,不捕获)
|
|
100
|
+
const tree = readOutlineFile(outlineDir);
|
|
101
|
+
const path = getOutlinePathIds(tree, input.atNodeId);
|
|
102
|
+
// 3. state 基座:初始 data 深拷贝(每次计算独立,不污染实体行)
|
|
103
|
+
const state = structuredClone(entity.data);
|
|
104
|
+
const appliedDeltas = [];
|
|
105
|
+
const conflicts = [];
|
|
106
|
+
// 4. 双层排序累积:节点间按路径序(根 → at_node),节点内按 order ASC(listDeltasByNode)
|
|
107
|
+
for (const nodeId of path) {
|
|
108
|
+
for (const delta of listDeltasByNode(db, nodeId, outlineDir)) {
|
|
109
|
+
if (delta.targetId !== input.targetId)
|
|
110
|
+
continue; // 只取目标实体的 Delta
|
|
111
|
+
const skipped = [];
|
|
112
|
+
const changes = delta.changes;
|
|
113
|
+
for (let i = 0; i < changes.length; i++) {
|
|
114
|
+
applyChange(state, changes[i], i, delta.id, skipped, conflicts);
|
|
115
|
+
}
|
|
116
|
+
const applied = {
|
|
117
|
+
nodeId: delta.nodeId,
|
|
118
|
+
description: delta.description,
|
|
119
|
+
changes, // 原样 changes 数组(本模块不修改,可安全共享引用)
|
|
120
|
+
};
|
|
121
|
+
if (skipped.length > 0)
|
|
122
|
+
applied.skipped = skipped; // 仅该 delta 有跳过时出现
|
|
123
|
+
appliedDeltas.push(applied);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
targetType: input.targetType,
|
|
128
|
+
targetId: input.targetId,
|
|
129
|
+
atNodeId: input.atNodeId,
|
|
130
|
+
state,
|
|
131
|
+
appliedDeltas,
|
|
132
|
+
conflicts,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=compute-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compute-state.js","sourceRoot":"","sources":["../../src/queries/compute-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { DeltaChange, DeltaRecord, DeltaRow } from "@whispering233/ai-editor-shared";
|
|
2
|
+
import { type Db } from "../connection.js";
|
|
3
|
+
/** 追加入参(endpoints.md 第 402-419 行;无 order 字段——服务端生成) */
|
|
4
|
+
export interface InsertDeltaInput {
|
|
5
|
+
/** 触发变更的大纲节点 ID */
|
|
6
|
+
nodeId: string;
|
|
7
|
+
targetType: string;
|
|
8
|
+
targetId: string;
|
|
9
|
+
changes: DeltaChange[];
|
|
10
|
+
/** 人类可读描述 */
|
|
11
|
+
description: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 追加属性变更记录(POST /api/v1/delta,endpoints.md 第 397-434 行):
|
|
15
|
+
* - **order 服务端生成、全局单调递增**:SELECT COALESCE(MAX("order"), 0) + 1,包单库事务
|
|
16
|
+
* (better-sqlite3 同步单连接下读-写无竞态,事务保证跨语句原子与回滚语义)
|
|
17
|
+
* - **前置约定:本层不校验触发节点/目标存在性**——endpoints.md POST /delta 未定义
|
|
18
|
+
* 该错误码;指向不存在节点的记录会因可见性规则(决策 12 修订:触发节点缺失视同
|
|
19
|
+
* 不可见、目标缺失仅省略 name)永久不可见。存在性校验由 S5.3 路由层负责。
|
|
20
|
+
* - id = shared generateId("delta-")(与关系 generateId("rel-") 同构;mapping.test.ts
|
|
21
|
+
* 的 "delta-1" 形状一致;前缀 + nanoid 全局唯一)
|
|
22
|
+
* - created_at = updated_at = nowIso()(应用层写时间约定,schema.md 第 16 行)
|
|
23
|
+
* @returns 完整行(DeltaRow,changes 已解析为数组)
|
|
24
|
+
*/
|
|
25
|
+
export declare function insertDelta(db: Db, input: InsertDeltaInput): DeltaRow;
|
|
26
|
+
/**
|
|
27
|
+
* 按 id 取单条 Delta(S7.5 提案快照重校验,决策 14):
|
|
28
|
+
* 记录级查询——自身软删(deleted_at 非空)视为不存在返回 null;
|
|
29
|
+
* 触发节点/目标端点的可见性联动(决策 12 修订)不在此判定:
|
|
30
|
+
* 提案引用的对象是 delta_records 记录本身,「存在性 + 自身 updated_at」即决策 14 语义
|
|
31
|
+
* (与 getRelation 的端点联动过滤不同——关系引用同样只比对关系自身,见 proposal/relation.ts
|
|
32
|
+
* buildProposeRemoveRelation 只采集 refRelation)。
|
|
33
|
+
* @returns 完整行(DeltaRow,changes 已解析);不存在或已软删返回 null
|
|
34
|
+
*/
|
|
35
|
+
export declare function getDeltaRow(db: Db, id: string): DeltaRow | null;
|
|
36
|
+
/**
|
|
37
|
+
* 按触发节点查询 Delta(GET /api/v1/delta/node/:nodeId,endpoints.md 第 436-462 行):
|
|
38
|
+
* - SQL:node_id = ? AND deleted_at IS NULL,按 "order" 递增(computeState 同节点内应用序)
|
|
39
|
+
* - **可见性三态过滤(决策 12 修订,AND)**:见 filterVisibleDeltas(本函数行同属一个
|
|
40
|
+
* 触发节点,仍逐行判定——与 listDeltasByTarget 共用同一实现,语义幂等)
|
|
41
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的软删/标题校验读 outline.json)
|
|
42
|
+
*/
|
|
43
|
+
export declare function listDeltasByNode(db: Db, nodeId: string, outlineDir: string): DeltaRecord[];
|
|
44
|
+
/**
|
|
45
|
+
* 按目标端点查询 Delta(S6.3 工具 get_delta_history 下沉,tools.md「状态查询」):
|
|
46
|
+
* - SQL:target_id = ? AND deleted_at IS NULL,按 "order" 递增(order 全局单调 =
|
|
47
|
+
* 创建顺序,即「按时间/节点排序」)
|
|
48
|
+
* - 可见性过滤与 listDeltasByNode **完全一致**(filterVisibleDeltas 共享:delta 自身 /
|
|
49
|
+
* 触发节点 / 目标端点三态软删判定 + targetName 联表,决策 12 修订)
|
|
50
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的软删/标题校验读 outline.json)
|
|
51
|
+
*/
|
|
52
|
+
export declare function listDeltasByTarget(db: Db, targetId: string, outlineDir: string): DeltaRecord[];
|
|
53
|
+
/** 悬空 Delta 的不可见原因(决策 12 修订三态的后两态 + 脏引用兜底) */
|
|
54
|
+
export type DanglingDeltaReason = "trigger_missing" | "trigger_deleted" | "target_missing" | "target_deleted";
|
|
55
|
+
/** 悬空 Delta 记录摘要(delta 自身未软删但已不可见——「幽灵变更」) */
|
|
56
|
+
export interface DanglingDeltaInfo {
|
|
57
|
+
id: string;
|
|
58
|
+
nodeId: string;
|
|
59
|
+
targetType: string;
|
|
60
|
+
targetId: string;
|
|
61
|
+
description: string;
|
|
62
|
+
reason: DanglingDeltaReason;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 全量悬空 Delta 诊断(S6.4 工具 find_orphan_elements 下沉,tools.md「孤立元素」):
|
|
66
|
+
* delta 自身未软删,但三态可见性(决策 12 修订)后两态命中——该记录永不生效:
|
|
67
|
+
* - trigger_missing:触发节点已物理删除(purge 后脏引用)
|
|
68
|
+
* - trigger_deleted:触发节点已软删但本 delta 未级联软删(跨存储不一致的 delta 侧,
|
|
69
|
+
* 与 outline-ops 级联软删/启动一致性校验(决策 16 修订)兜底的对象同源)
|
|
70
|
+
* - target_missing:目标端点已物理删除(实体 purge / 大纲节点 purge)
|
|
71
|
+
* - target_deleted:目标端点已软删但本 delta 未级联软删
|
|
72
|
+
* 软删自身的 delta(回收站对象)**不在此列**——由回收站管理,非悬空。
|
|
73
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的存在性/软删校验读 outline.json)
|
|
74
|
+
*/
|
|
75
|
+
export declare function listDanglingDeltas(db: Db, outlineDir: string): DanglingDeltaInfo[];
|
|
76
|
+
//# sourceMappingURL=delta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/queries/delta.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAmB,MAAM,iCAAiC,CAAC;AAG3G,OAAO,EAAmB,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAoC5D,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,gBAAgB,GAAG,QAAQ,CAiCrE;AAkED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAM/D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE,CAa1F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE,CAQ9F;AAID,8CAA8C;AAC9C,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAE9G,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAiElF"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// @whispering233/ai-editor-db Delta 增查(S5.1):增量插入 + 按节点查询(联表 target_name + 可见性联动)
|
|
2
|
+
//
|
|
3
|
+
// 单一事实来源:
|
|
4
|
+
// - doc/api/endpoints.md 第 395-462 行(POST /delta 追加 + GET /delta/node/:nodeId;
|
|
5
|
+
// DeltaRecord 含 targetName 联表字段;**无 order 入参**——服务端全局单调生成;
|
|
6
|
+
// op 语义 2026-08 修订:set/update/add/remove)
|
|
7
|
+
// - doc/database/schema.md 第 89-105 行(delta_records 表:changes JSON、order 全局单调、
|
|
8
|
+
// created_at/updated_at/deleted_at ISO 应用层写入)
|
|
9
|
+
// - 决策 12 修订(可见性联动触发节点与目标实体:任一软删即不可见)、
|
|
10
|
+
// 决策 9 修订(computeState 只沿大纲树父链累积已确认 Delta——同一节点内按 order 应用)
|
|
11
|
+
//
|
|
12
|
+
// 边界:级联软删/还原/补标已有实现——entity.ts softDeleteEntity(target_id 级联)、
|
|
13
|
+
// server outline.ts cascadeSoftDelete(node_id 级联)、trash.ts restore/purge 级联、
|
|
14
|
+
// S4.2 一致性补标;本卡只做增量插入与可见性查询,不重复。
|
|
15
|
+
//
|
|
16
|
+
// 可见性过滤实现(决策 12 修订,三态 AND):
|
|
17
|
+
// - SQL 层:delta 自身 deleted_at IS NULL
|
|
18
|
+
// - JS 层(参照 relation.ts buildEndpointContext 模式):
|
|
19
|
+
// 触发节点(node_id)未软删——outline.json 检查;
|
|
20
|
+
// 目标端点未软删——实体 target 查 entities 软删集合(一次 IN 查询),
|
|
21
|
+
// 大纲节点 target 走 outline.json
|
|
22
|
+
// - name 联表同路径:实体 → entities.name、大纲节点 → outline.json title
|
|
23
|
+
import { generateId, mapRowToDelta } from "@whispering233/ai-editor-shared";
|
|
24
|
+
import { nowIso } from "../storage/atomic.js";
|
|
25
|
+
import { withTransaction } from "../connection.js";
|
|
26
|
+
import { findOutlineNode, readOutlineFile } from "../storage/outline.js";
|
|
27
|
+
/** 实体端点类型集合(entities 表 type 列;其余 target_type 按大纲节点处理,relation.ts 同款约定) */
|
|
28
|
+
const ENTITY_TARGET_TYPES = ["character", "setting", "location", "hook"];
|
|
29
|
+
/** 行 → DeltaRow(changes JSON 解析;坏行防御同 entity.ts parseDataColumn 风格) */
|
|
30
|
+
function rowToDeltaRow(row) {
|
|
31
|
+
return {
|
|
32
|
+
id: row.id,
|
|
33
|
+
node_id: row.node_id,
|
|
34
|
+
target_type: row.target_type,
|
|
35
|
+
target_id: row.target_id,
|
|
36
|
+
changes: parseChanges(row.changes),
|
|
37
|
+
description: row.description,
|
|
38
|
+
order: row.order,
|
|
39
|
+
created_at: row.created_at,
|
|
40
|
+
updated_at: row.updated_at,
|
|
41
|
+
deleted_at: row.deleted_at ?? null,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* changes 列解析防御:非法 JSON / 非数组 → [](单条坏行不打挂整表查询;
|
|
46
|
+
* 与 entity.ts parseDataColumn / chat.ts parseToolCalls 防御风格一致)
|
|
47
|
+
*/
|
|
48
|
+
function parseChanges(value) {
|
|
49
|
+
if (typeof value !== "string")
|
|
50
|
+
return [];
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(value);
|
|
53
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 追加属性变更记录(POST /api/v1/delta,endpoints.md 第 397-434 行):
|
|
61
|
+
* - **order 服务端生成、全局单调递增**:SELECT COALESCE(MAX("order"), 0) + 1,包单库事务
|
|
62
|
+
* (better-sqlite3 同步单连接下读-写无竞态,事务保证跨语句原子与回滚语义)
|
|
63
|
+
* - **前置约定:本层不校验触发节点/目标存在性**——endpoints.md POST /delta 未定义
|
|
64
|
+
* 该错误码;指向不存在节点的记录会因可见性规则(决策 12 修订:触发节点缺失视同
|
|
65
|
+
* 不可见、目标缺失仅省略 name)永久不可见。存在性校验由 S5.3 路由层负责。
|
|
66
|
+
* - id = shared generateId("delta-")(与关系 generateId("rel-") 同构;mapping.test.ts
|
|
67
|
+
* 的 "delta-1" 形状一致;前缀 + nanoid 全局唯一)
|
|
68
|
+
* - created_at = updated_at = nowIso()(应用层写时间约定,schema.md 第 16 行)
|
|
69
|
+
* @returns 完整行(DeltaRow,changes 已解析为数组)
|
|
70
|
+
*/
|
|
71
|
+
export function insertDelta(db, input) {
|
|
72
|
+
return withTransaction(db, () => {
|
|
73
|
+
const now = nowIso();
|
|
74
|
+
const order = db.prepare('SELECT COALESCE(MAX("order"), 0) + 1 AS next FROM delta_records').get().next;
|
|
75
|
+
const row = {
|
|
76
|
+
id: generateId("delta-"),
|
|
77
|
+
node_id: input.nodeId,
|
|
78
|
+
target_type: input.targetType,
|
|
79
|
+
target_id: input.targetId,
|
|
80
|
+
changes: input.changes,
|
|
81
|
+
description: input.description,
|
|
82
|
+
order,
|
|
83
|
+
created_at: now,
|
|
84
|
+
updated_at: now,
|
|
85
|
+
deleted_at: null,
|
|
86
|
+
};
|
|
87
|
+
db.prepare('INSERT INTO delta_records (id, node_id, target_type, target_id, changes, description, "order", created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)').run(row.id, row.node_id, row.target_type, row.target_id, JSON.stringify(row.changes), row.description, row.order, row.created_at, row.updated_at);
|
|
88
|
+
return row;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 行数组 → 可见性三态过滤 + targetName 联表(决策 12 修订,AND):
|
|
93
|
+
* 1. delta 自身未软删(SQL 层 WHERE deleted_at IS NULL 保证,调用方负责)
|
|
94
|
+
* 2. 触发节点未软删(outline.json:节点 deleted !== true;节点不存在视同不可见——
|
|
95
|
+
* purge 已物理清除其 Delta,脏引用兜底为空)——**逐行判定**(listDeltasByTarget
|
|
96
|
+
* 的行可来自不同触发节点,listDeltasByNode 复用同一逻辑,语义幂等)
|
|
97
|
+
* 3. 目标端点未软删:实体 target 查 entities 软删集合(一次 IN 查询);大纲 target 查树
|
|
98
|
+
* (target 不存在 → 不过滤但省略 name——与 relation.ts 端点缺失语义一致)
|
|
99
|
+
* - targetName 联表(参照 relation.ts buildEndpointContext):实体 → entities.name
|
|
100
|
+
* (一次 IN 查询);大纲节点 → outline.json title;解析失败/缺失 → 省略字段
|
|
101
|
+
*
|
|
102
|
+
* 供 listDeltasByNode / listDeltasByTarget 共用(S6.3 工具下沉:按目标查询复用同一语义)。
|
|
103
|
+
* @param tree 已读取的 outline.json 树(调用方读取一次,避免重复 I/O)
|
|
104
|
+
*/
|
|
105
|
+
function filterVisibleDeltas(db, rows, tree) {
|
|
106
|
+
// 目标实体端点:一次 IN 查询收集软删集合与名称映射(含软删实体——名称填充不受
|
|
107
|
+
// 可见性影响,过滤在另一层;relation.ts 同款取舍)。重复 target_id 用 Set 去重
|
|
108
|
+
// (同一节点多条 Delta 指向同一实体时常见),避免 IN 参数重复
|
|
109
|
+
const entityIds = [
|
|
110
|
+
...new Set(rows
|
|
111
|
+
.filter((r) => ENTITY_TARGET_TYPES.includes(r.target_type))
|
|
112
|
+
.map((r) => r.target_id)),
|
|
113
|
+
];
|
|
114
|
+
const entitySoftDeleted = new Set();
|
|
115
|
+
const entityNames = new Map();
|
|
116
|
+
if (entityIds.length > 0) {
|
|
117
|
+
const placeholders = entityIds.map(() => "?").join(",");
|
|
118
|
+
const softRows = db
|
|
119
|
+
.prepare(`SELECT id FROM entities WHERE deleted_at IS NOT NULL AND id IN (${placeholders})`)
|
|
120
|
+
.all(...entityIds);
|
|
121
|
+
for (const r of softRows)
|
|
122
|
+
entitySoftDeleted.add(r.id);
|
|
123
|
+
const nameRows = db
|
|
124
|
+
.prepare(`SELECT id, name FROM entities WHERE id IN (${placeholders})`)
|
|
125
|
+
.all(...entityIds);
|
|
126
|
+
for (const r of nameRows)
|
|
127
|
+
entityNames.set(r.id, r.name);
|
|
128
|
+
}
|
|
129
|
+
const records = [];
|
|
130
|
+
for (const raw of rows) {
|
|
131
|
+
const row = rowToDeltaRow(raw);
|
|
132
|
+
// 触发节点软删检查(决策 12 修订:触发节点软删 → 该 Delta 不可见)
|
|
133
|
+
const trigger = findOutlineNode(tree, row.node_id);
|
|
134
|
+
if (trigger === undefined || trigger.deleted === true)
|
|
135
|
+
continue;
|
|
136
|
+
// 目标端点软删检查(决策 12 修订)
|
|
137
|
+
let targetSoftDeleted;
|
|
138
|
+
let targetName;
|
|
139
|
+
if (ENTITY_TARGET_TYPES.includes(row.target_type)) {
|
|
140
|
+
targetSoftDeleted = entitySoftDeleted.has(row.target_id);
|
|
141
|
+
targetName = entityNames.get(row.target_id);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const node = findOutlineNode(tree, row.target_id);
|
|
145
|
+
targetSoftDeleted = node !== undefined && node.deleted === true;
|
|
146
|
+
if (node !== undefined)
|
|
147
|
+
targetName = node.title;
|
|
148
|
+
}
|
|
149
|
+
if (targetSoftDeleted)
|
|
150
|
+
continue;
|
|
151
|
+
const record = mapRowToDelta(row); // shared 映射(camelCase;targetName 可选字段另行填充)
|
|
152
|
+
if (targetName !== undefined)
|
|
153
|
+
record.targetName = targetName;
|
|
154
|
+
records.push(record);
|
|
155
|
+
}
|
|
156
|
+
return records;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 按 id 取单条 Delta(S7.5 提案快照重校验,决策 14):
|
|
160
|
+
* 记录级查询——自身软删(deleted_at 非空)视为不存在返回 null;
|
|
161
|
+
* 触发节点/目标端点的可见性联动(决策 12 修订)不在此判定:
|
|
162
|
+
* 提案引用的对象是 delta_records 记录本身,「存在性 + 自身 updated_at」即决策 14 语义
|
|
163
|
+
* (与 getRelation 的端点联动过滤不同——关系引用同样只比对关系自身,见 proposal/relation.ts
|
|
164
|
+
* buildProposeRemoveRelation 只采集 refRelation)。
|
|
165
|
+
* @returns 完整行(DeltaRow,changes 已解析);不存在或已软删返回 null
|
|
166
|
+
*/
|
|
167
|
+
export function getDeltaRow(db, id) {
|
|
168
|
+
const row = db.prepare("SELECT * FROM delta_records WHERE id = ? AND deleted_at IS NULL").get(id);
|
|
169
|
+
if (row === undefined)
|
|
170
|
+
return null;
|
|
171
|
+
return rowToDeltaRow(row);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 按触发节点查询 Delta(GET /api/v1/delta/node/:nodeId,endpoints.md 第 436-462 行):
|
|
175
|
+
* - SQL:node_id = ? AND deleted_at IS NULL,按 "order" 递增(computeState 同节点内应用序)
|
|
176
|
+
* - **可见性三态过滤(决策 12 修订,AND)**:见 filterVisibleDeltas(本函数行同属一个
|
|
177
|
+
* 触发节点,仍逐行判定——与 listDeltasByTarget 共用同一实现,语义幂等)
|
|
178
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的软删/标题校验读 outline.json)
|
|
179
|
+
*/
|
|
180
|
+
export function listDeltasByNode(db, nodeId, outlineDir) {
|
|
181
|
+
const rows = db
|
|
182
|
+
.prepare('SELECT * FROM delta_records WHERE node_id = ? AND deleted_at IS NULL ORDER BY "order" ASC')
|
|
183
|
+
.all(nodeId);
|
|
184
|
+
if (rows.length === 0)
|
|
185
|
+
return [];
|
|
186
|
+
const tree = readOutlineFile(outlineDir);
|
|
187
|
+
// 触发节点软删检查(决策 12 修订:触发节点软删 → 其全部 Delta 不可见;
|
|
188
|
+
// 提前短路避免无谓的实体软删集合 IN 查询)
|
|
189
|
+
const trigger = findOutlineNode(tree, nodeId);
|
|
190
|
+
if (trigger === undefined || trigger.deleted === true)
|
|
191
|
+
return [];
|
|
192
|
+
return filterVisibleDeltas(db, rows, tree);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 按目标端点查询 Delta(S6.3 工具 get_delta_history 下沉,tools.md「状态查询」):
|
|
196
|
+
* - SQL:target_id = ? AND deleted_at IS NULL,按 "order" 递增(order 全局单调 =
|
|
197
|
+
* 创建顺序,即「按时间/节点排序」)
|
|
198
|
+
* - 可见性过滤与 listDeltasByNode **完全一致**(filterVisibleDeltas 共享:delta 自身 /
|
|
199
|
+
* 触发节点 / 目标端点三态软删判定 + targetName 联表,决策 12 修订)
|
|
200
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的软删/标题校验读 outline.json)
|
|
201
|
+
*/
|
|
202
|
+
export function listDeltasByTarget(db, targetId, outlineDir) {
|
|
203
|
+
const rows = db
|
|
204
|
+
.prepare('SELECT * FROM delta_records WHERE target_id = ? AND deleted_at IS NULL ORDER BY "order" ASC')
|
|
205
|
+
.all(targetId);
|
|
206
|
+
if (rows.length === 0)
|
|
207
|
+
return [];
|
|
208
|
+
const tree = readOutlineFile(outlineDir);
|
|
209
|
+
return filterVisibleDeltas(db, rows, tree);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 全量悬空 Delta 诊断(S6.4 工具 find_orphan_elements 下沉,tools.md「孤立元素」):
|
|
213
|
+
* delta 自身未软删,但三态可见性(决策 12 修订)后两态命中——该记录永不生效:
|
|
214
|
+
* - trigger_missing:触发节点已物理删除(purge 后脏引用)
|
|
215
|
+
* - trigger_deleted:触发节点已软删但本 delta 未级联软删(跨存储不一致的 delta 侧,
|
|
216
|
+
* 与 outline-ops 级联软删/启动一致性校验(决策 16 修订)兜底的对象同源)
|
|
217
|
+
* - target_missing:目标端点已物理删除(实体 purge / 大纲节点 purge)
|
|
218
|
+
* - target_deleted:目标端点已软删但本 delta 未级联软删
|
|
219
|
+
* 软删自身的 delta(回收站对象)**不在此列**——由回收站管理,非悬空。
|
|
220
|
+
* @param outlineDir 项目根(触发节点与大纲 target 的存在性/软删校验读 outline.json)
|
|
221
|
+
*/
|
|
222
|
+
export function listDanglingDeltas(db, outlineDir) {
|
|
223
|
+
const rows = db
|
|
224
|
+
.prepare('SELECT * FROM delta_records WHERE deleted_at IS NULL ORDER BY "order" ASC')
|
|
225
|
+
.all();
|
|
226
|
+
if (rows.length === 0)
|
|
227
|
+
return [];
|
|
228
|
+
const tree = readOutlineFile(outlineDir);
|
|
229
|
+
// 实体目标端点状态:一次 IN 批量收集(避免逐行查询——relation.ts buildEndpointContext 同款先例)
|
|
230
|
+
const entityIds = [
|
|
231
|
+
...new Set(rows
|
|
232
|
+
.filter((r) => ENTITY_TARGET_TYPES.includes(r.target_type))
|
|
233
|
+
.map((r) => r.target_id)),
|
|
234
|
+
];
|
|
235
|
+
const entityStates = new Map();
|
|
236
|
+
if (entityIds.length > 0) {
|
|
237
|
+
const placeholders = entityIds.map(() => "?").join(",");
|
|
238
|
+
const found = db
|
|
239
|
+
.prepare(`SELECT id, deleted_at FROM entities WHERE id IN (${placeholders})`)
|
|
240
|
+
.all(...entityIds);
|
|
241
|
+
const foundIds = new Set(found.map((f) => f.id));
|
|
242
|
+
for (const id of entityIds) {
|
|
243
|
+
if (!foundIds.has(id)) {
|
|
244
|
+
entityStates.set(id, "missing");
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const f = found.find((x) => x.id === id);
|
|
248
|
+
entityStates.set(id, f.deleted_at !== null ? "deleted" : "ok");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const out = [];
|
|
253
|
+
for (const raw of rows) {
|
|
254
|
+
const row = rowToDeltaRow(raw);
|
|
255
|
+
// 触发节点:缺失 → 脏引用;软删 → 未级联
|
|
256
|
+
const trigger = findOutlineNode(tree, row.node_id);
|
|
257
|
+
if (trigger === undefined) {
|
|
258
|
+
out.push({ id: row.id, nodeId: row.node_id, targetType: row.target_type, targetId: row.target_id, description: row.description, reason: "trigger_missing" });
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (trigger.deleted === true) {
|
|
262
|
+
out.push({ id: row.id, nodeId: row.node_id, targetType: row.target_type, targetId: row.target_id, description: row.description, reason: "trigger_deleted" });
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
// 目标端点:实体查批量 Map / 大纲节点查树
|
|
266
|
+
let targetState;
|
|
267
|
+
if (ENTITY_TARGET_TYPES.includes(row.target_type)) {
|
|
268
|
+
targetState = entityStates.get(row.target_id) ?? "missing";
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
const node = findOutlineNode(tree, row.target_id);
|
|
272
|
+
targetState = node === undefined ? "missing" : node.deleted === true ? "deleted" : "ok";
|
|
273
|
+
}
|
|
274
|
+
if (targetState !== "ok") {
|
|
275
|
+
out.push({
|
|
276
|
+
id: row.id,
|
|
277
|
+
nodeId: row.node_id,
|
|
278
|
+
targetType: row.target_type,
|
|
279
|
+
targetId: row.target_id,
|
|
280
|
+
description: row.description,
|
|
281
|
+
reason: targetState === "missing" ? "target_missing" : "target_deleted",
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/queries/delta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { EntityRow, EntitySummary, EntityType } from "@whispering233/ai-editor-shared";
|
|
2
|
+
import { type Db } from "../connection.js";
|
|
3
|
+
/** 列表查询参数(endpoints.md 第 162-169 行;缺省值语义与路由层对齐) */
|
|
4
|
+
export interface EntityListQuery {
|
|
5
|
+
type?: EntityType;
|
|
6
|
+
/** 搜索关键词(模糊匹配 name;LIKE 通配符 %/_ 原样透传——模糊搜索语义,注释明示) */
|
|
7
|
+
q?: string;
|
|
8
|
+
/**
|
|
9
|
+
* data 字段过滤(S6.3 工具 search_entities 下沉,tools.md「实体查询」filters):
|
|
10
|
+
* status 精确匹配 data.status;tags 要求 data.tags 数组包含全部指定 tags(AND)。
|
|
11
|
+
* 列表摘要不含 data(toSummary 只提取关键字段),故本过滤走「全行查询 + JS 层判定」。
|
|
12
|
+
*/
|
|
13
|
+
filters?: {
|
|
14
|
+
tags?: string[];
|
|
15
|
+
status?: string;
|
|
16
|
+
};
|
|
17
|
+
/** 分页偏移,默认 0 */
|
|
18
|
+
offset?: number;
|
|
19
|
+
/** 每页条数,默认 50,最大 200(超限 clamp,防恶意大页) */
|
|
20
|
+
limit?: number;
|
|
21
|
+
sort?: "name" | "created_at" | "updated_at";
|
|
22
|
+
order?: "asc" | "desc";
|
|
23
|
+
}
|
|
24
|
+
/** 列表结果(endpoints.md 第 171-177 行;items 为 API 形态 EntitySummary,与 chat.ts 同款风格) */
|
|
25
|
+
export interface EntityListResult {
|
|
26
|
+
items: EntitySummary[];
|
|
27
|
+
total: number;
|
|
28
|
+
}
|
|
29
|
+
/** 行 → EntityRow(data 从 JSON 字符串解析为对象;坏行防御见 rowToEntityRow 注释) */
|
|
30
|
+
export declare function rowToEntityRow(row: Record<string, unknown>): EntityRow;
|
|
31
|
+
/**
|
|
32
|
+
* 实体列表(GET /api/v1/entity/:type,endpoints.md 第 154-193 行):
|
|
33
|
+
* type 过滤 + q 模糊搜索(name LIKE)+ 排序(name/created_at/updated_at × asc/desc,
|
|
34
|
+
* 白名单防注入)+ 分页(limit clamp 1-200)+ **默认过滤软删**(决策 12)。
|
|
35
|
+
* total 为过滤后总数(不含分页)。
|
|
36
|
+
* filters 语义(S6.3 下沉):data 字段 JS 过滤(列表摘要不含 data),此时 SQL 只做
|
|
37
|
+
* type/q/软删过滤,filters + 分页在 JS 层(MVP 数据量小,全行查询可接受);
|
|
38
|
+
* 无 filters 时保持 COUNT + LIMIT SQL 原路径(行为不变)。
|
|
39
|
+
*/
|
|
40
|
+
export declare function listEntities(db: Db, query: EntityListQuery): EntityListResult;
|
|
41
|
+
/** 按 id 取实体详情(GET /api/v1/entity/:type/:id);不存在或已软删返回 null(决策 12 过滤) */
|
|
42
|
+
export declare function getEntity(db: Db, id: string): EntityRow | null;
|
|
43
|
+
/** 实体的 Delta 计数(详情响应 deltaCount;决策 12 修订:目标实体软删的 Delta 不可见) */
|
|
44
|
+
export declare function countDeltasForEntity(db: Db, id: string): number;
|
|
45
|
+
/**
|
|
46
|
+
* 创建实体(POST /api/v1/entity/:type,endpoints.md 第 221-253 行):
|
|
47
|
+
* id = shared generateEntityId(char-/set-/loc-/hook- 前缀)、created_at/updated_at 应用层 ISO、
|
|
48
|
+
* data 缺省 {}、type 必须 ∈ ENTITY_TYPES(非法抛错——路由层 schema 校验后一般不可达,防御)。
|
|
49
|
+
* @returns 新行(EntityRow,data 已解析)
|
|
50
|
+
*/
|
|
51
|
+
export declare function createEntity(db: Db, input: {
|
|
52
|
+
type: EntityType;
|
|
53
|
+
name: string;
|
|
54
|
+
data?: Record<string, unknown>;
|
|
55
|
+
}): EntityRow;
|
|
56
|
+
/**
|
|
57
|
+
* 部分更新实体(PUT /api/v1/entity/:type/:id,endpoints.md 第 255-278 行):
|
|
58
|
+
* 仅合并传入字段——name 直接替换;data **浅合并**(未传字段保留,endpoints.md 第 267 行);
|
|
59
|
+
* updated_at 刷新(决策 14 提案快照比对)。软删实体不可更新(getEntity 过滤 → null,路由层 404)。
|
|
60
|
+
* 读后写包 withTransaction(oracle 审核建议 2):better-sqlite3 同步单连接下无竞态(安全),
|
|
61
|
+
* 包事务统一风格(与 softDeleteEntity 一致);返回行在事务内直接构造,无额外复杂度。
|
|
62
|
+
* @returns 更新后的行;实体不存在或已软删返回 null
|
|
63
|
+
*/
|
|
64
|
+
export declare function updateEntity(db: Db, id: string, patch: {
|
|
65
|
+
name?: string;
|
|
66
|
+
data?: Record<string, unknown>;
|
|
67
|
+
}): EntityRow | null;
|
|
68
|
+
/**
|
|
69
|
+
* 软删实体(DELETE /api/v1/entity/:type/:id,决策 12):
|
|
70
|
+
* **级联软删关联关系与 Delta**(单库内事务保证原子性):
|
|
71
|
+
* - relation_records:source_id = id **或** target_id = id(任一端点软删即不可见,决策 12 修订)
|
|
72
|
+
* - delta_records:target_id = id(目标实体软删即不可见,决策 12 修订)
|
|
73
|
+
* - 自身:deleted_at 置位 + updated_at 刷新(决策 12 修订:软删亦更新版本戳)
|
|
74
|
+
* 幂等:实体已软删(或不存在)→ 返回 null 且无任何副作用(级联 UPDATE 均带 deleted_at IS NULL 过滤)。
|
|
75
|
+
*
|
|
76
|
+
* @returns { relations, deltas } 实际级联行数;实体不存在/已软删返回 null
|
|
77
|
+
*/
|
|
78
|
+
export declare function softDeleteEntity(db: Db, id: string, deletedAt: string): {
|
|
79
|
+
relations: number;
|
|
80
|
+
deltas: number;
|
|
81
|
+
} | null;
|
|
82
|
+
/** 实体聚合统计结果:总数 + 类型专属分布(稀疏字段,仅出现对应类型的分布) */
|
|
83
|
+
export interface EntitySummaryStats {
|
|
84
|
+
type: EntityType;
|
|
85
|
+
/** 非软删实体总数(决策 12 修订:回收站对象不计入) */
|
|
86
|
+
total: number;
|
|
87
|
+
/** character:data.role 分布 */
|
|
88
|
+
byRole?: Record<string, number>;
|
|
89
|
+
/** character / hook:data.status 分布 */
|
|
90
|
+
byStatus?: Record<string, number>;
|
|
91
|
+
/** setting:data.category 分布 */
|
|
92
|
+
byCategory?: Record<string, number>;
|
|
93
|
+
/** location:data.type 分布 */
|
|
94
|
+
byType?: Record<string, number>;
|
|
95
|
+
/** hook:data.payoff_timing 分布 */
|
|
96
|
+
byPayoffTiming?: Record<string, number>;
|
|
97
|
+
/** character:data.abilities 频率(取前 10,防 token 爆炸,决策 15) */
|
|
98
|
+
topAbilities?: {
|
|
99
|
+
ability: string;
|
|
100
|
+
count: number;
|
|
101
|
+
}[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 实体聚合统计(S6.3 工具 get_entity_summary 下沉,tools.md「聚合分析」):
|
|
105
|
+
* 指定类型实体的总数 + 类型专属分布。仅统计非软删实体(决策 12 修订);
|
|
106
|
+
* 分布字段按类型稀疏出现:character→byRole/byStatus/topAbilities、setting→byCategory、
|
|
107
|
+
* location→byType、hook→byStatus/byPayoffTiming;缺字段(data 未填)不报错、不计入。
|
|
108
|
+
*/
|
|
109
|
+
export declare function getEntitySummaryStats(db: Db, type: EntityType): EntitySummaryStats;
|
|
110
|
+
//# sourceMappingURL=entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/queries/entity.ts"],"names":[],"mappings":"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"}
|