@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.
Files changed (73) hide show
  1. package/dist/connection.d.ts +24 -0
  2. package/dist/connection.d.ts.map +1 -0
  3. package/dist/connection.js +39 -0
  4. package/dist/connection.js.map +1 -0
  5. package/dist/index.d.ts +15 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +17 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/migrations/index.d.ts +10 -0
  10. package/dist/migrations/index.d.ts.map +1 -0
  11. package/dist/migrations/index.js +34 -0
  12. package/dist/migrations/index.js.map +1 -0
  13. package/dist/queries/atomic.d.ts +15 -0
  14. package/dist/queries/atomic.d.ts.map +1 -0
  15. package/dist/queries/atomic.js +44 -0
  16. package/dist/queries/atomic.js.map +1 -0
  17. package/dist/queries/chat.d.ts +74 -0
  18. package/dist/queries/chat.d.ts.map +1 -0
  19. package/dist/queries/chat.js +188 -0
  20. package/dist/queries/chat.js.map +1 -0
  21. package/dist/queries/compute-state.d.ts +32 -0
  22. package/dist/queries/compute-state.d.ts.map +1 -0
  23. package/dist/queries/compute-state.js +135 -0
  24. package/dist/queries/compute-state.js.map +1 -0
  25. package/dist/queries/delta.d.ts +76 -0
  26. package/dist/queries/delta.d.ts.map +1 -0
  27. package/dist/queries/delta.js +287 -0
  28. package/dist/queries/delta.js.map +1 -0
  29. package/dist/queries/entity.d.ts +110 -0
  30. package/dist/queries/entity.d.ts.map +1 -0
  31. package/dist/queries/entity.js +285 -0
  32. package/dist/queries/entity.js.map +1 -0
  33. package/dist/queries/migration.d.ts +153 -0
  34. package/dist/queries/migration.d.ts.map +1 -0
  35. package/dist/queries/migration.js +260 -0
  36. package/dist/queries/migration.js.map +1 -0
  37. package/dist/queries/outline-ops.d.ts +126 -0
  38. package/dist/queries/outline-ops.d.ts.map +1 -0
  39. package/dist/queries/outline-ops.js +346 -0
  40. package/dist/queries/outline-ops.js.map +1 -0
  41. package/dist/queries/outline.d.ts +48 -0
  42. package/dist/queries/outline.d.ts.map +1 -0
  43. package/dist/queries/outline.js +134 -0
  44. package/dist/queries/outline.js.map +1 -0
  45. package/dist/queries/project.d.ts +21 -0
  46. package/dist/queries/project.d.ts.map +1 -0
  47. package/dist/queries/project.js +43 -0
  48. package/dist/queries/project.js.map +1 -0
  49. package/dist/queries/relation.d.ts +83 -0
  50. package/dist/queries/relation.d.ts.map +1 -0
  51. package/dist/queries/relation.js +418 -0
  52. package/dist/queries/relation.js.map +1 -0
  53. package/dist/queries/trash.d.ts +65 -0
  54. package/dist/queries/trash.d.ts.map +1 -0
  55. package/dist/queries/trash.js +121 -0
  56. package/dist/queries/trash.js.map +1 -0
  57. package/dist/schema.d.ts +29 -0
  58. package/dist/schema.d.ts.map +1 -0
  59. package/dist/schema.js +102 -0
  60. package/dist/schema.js.map +1 -0
  61. package/dist/storage/atomic.d.ts +32 -0
  62. package/dist/storage/atomic.d.ts.map +1 -0
  63. package/dist/storage/atomic.js +112 -0
  64. package/dist/storage/atomic.js.map +1 -0
  65. package/dist/storage/outline.d.ts +68 -0
  66. package/dist/storage/outline.d.ts.map +1 -0
  67. package/dist/storage/outline.js +154 -0
  68. package/dist/storage/outline.js.map +1 -0
  69. package/dist/storage/project.d.ts +24 -0
  70. package/dist/storage/project.d.ts.map +1 -0
  71. package/dist/storage/project.js +55 -0
  72. package/dist/storage/project.js.map +1 -0
  73. package/package.json +36 -0
@@ -0,0 +1,83 @@
1
+ import type { Db } from "../connection.js";
2
+ import type { RelationQueryResult, RelationRow } from "@whispering233/ai-editor-shared";
3
+ /** 关系操作错误码(server 层映射 HttpError:RELATION_EXISTS → 409、其余 → 400) */
4
+ export type RelationErrorCode = "RELATION_EXISTS" | "ENDPOINT_NOT_FOUND" | "INVALID_RELATION_TYPE";
5
+ /** 关系操作错误(带 code,与 OutlineError 同风格;路由层 catch 映射) */
6
+ export declare class RelationError extends Error {
7
+ readonly code: RelationErrorCode;
8
+ constructor(code: RelationErrorCode, message: string);
9
+ }
10
+ /** 关系查询过滤条件(endpoints.md 第 311-318 行;全部可选,组合过滤) */
11
+ export interface RelationQuery {
12
+ sourceType?: string;
13
+ sourceId?: string;
14
+ targetType?: string;
15
+ targetId?: string;
16
+ relationType?: string;
17
+ }
18
+ /** 大纲端点 id(relation_records 端点类型约定,schema.md) */
19
+ export declare const OUTLINE_ENDPOINT_TYPE = "outline_node";
20
+ /**
21
+ * 关系列表 + k 跳路径(GET /api/v1/relation,endpoints.md 第 306-343 行):
22
+ * 过滤条件组合(全部可选)+ 可见性过滤(关系自身未软删 + 两端点均未软删,决策 12 修订)。
23
+ * depth=1:直接关系(relations,联表填充 sourceName/targetName)。
24
+ * depth=2/3:追加 k 跳路径(paths:nodes/edges 结构)——**BFS 图不受 source_id/target_id
25
+ * 过滤限制**(起点定位用 source_id,图遍历需要全量可见边,否则中间节点的出边被过滤截断);
26
+ * relations 数组仍按全部过滤条件返回(endpoints.md「追加路径信息」语义)。
27
+ *
28
+ * @param outlineDir 项目根(大纲端点软删/标题校验读 outline.json)
29
+ */
30
+ export declare function listRelations(db: Db, query: RelationQuery, depth: 1 | 2 | 3, outlineDir: string): RelationQueryResult;
31
+ /**
32
+ * 单条关系(含可见性过滤,决策 12 修订);不存在/不可见返回 null。
33
+ * @param outlineDir 大纲端点软删校验读 outline.json
34
+ */
35
+ export declare function getRelation(db: Db, id: string, outlineDir: string): RelationRow | null;
36
+ /**
37
+ * 创建关系(POST /api/v1/relation,endpoints.md 第 345-374 行):
38
+ * - **判重**:同 (source_id, target_id, relation_type) 且未软删已存在 → RELATION_EXISTS(409 语义)
39
+ * - relation_type 白名单(schema.md 16 个预定义类型,含 plot_edge 剧情连线——同规则,决策 10)
40
+ * - **端点存在性**:实体端点查 entities(非软删)、大纲端点读 outline.json(存在且非软删)
41
+ * - 时间戳应用层 ISO
42
+ *
43
+ * @param outlineDir 大纲端点校验读 outline.json
44
+ * @throws RelationError RELATION_EXISTS / ENDPOINT_NOT_FOUND / INVALID_RELATION_TYPE
45
+ */
46
+ export declare function createRelation(db: Db, input: {
47
+ sourceType: string;
48
+ sourceId: string;
49
+ targetType: string;
50
+ targetId: string;
51
+ relationType: string;
52
+ metadata?: Record<string, unknown>;
53
+ }, outlineDir: string): RelationRow;
54
+ /**
55
+ * 物理删除关系(DELETE /api/v1/relation/:id,endpoints.md 第 376-391 行,决策 12 修订:
56
+ * 手动删关系 = 物理删,不进回收站)。
57
+ * @returns 影响行数(0 = 不存在,路由层映射 404 RELATION_NOT_FOUND)
58
+ */
59
+ export declare function deleteRelation(db: Db, id: string): number;
60
+ /** 悬空关系的端点异常原因(端点物理缺失 / 端点软删未级联) */
61
+ export type DanglingRelationReason = "source_missing" | "target_missing" | "source_deleted" | "target_deleted";
62
+ /** 悬空关系记录摘要(关系自身未软删但端点异常——每端点一条) */
63
+ export interface DanglingRelationInfo {
64
+ id: string;
65
+ sourceType: string;
66
+ sourceId: string;
67
+ targetType: string;
68
+ targetId: string;
69
+ relationType: string;
70
+ reason: DanglingRelationReason;
71
+ }
72
+ /**
73
+ * 全量悬空关系诊断(S6.4 工具 find_orphan_elements 下沉,tools.md「孤立元素」):
74
+ * 关系自身未软删,但端点异常——**每端点一条**记录(同一关系两端点异常时出两条):
75
+ * - *_missing:端点已物理删除(实体 purge / 大纲节点 purge)——dangling_relations 侧
76
+ * - *_deleted:端点已软删但关系未级联软删(跨存储不一致的 relation 侧,决策 12 修订
77
+ * 级联软删/启动一致性校验(决策 16 修订)兜底的对象同源)——inconsistent_soft_deletes 侧
78
+ * 软删自身的关系(回收站对象)**不在此列**——由回收站管理,非悬空。
79
+ * 实体端点状态一次 IN 批量收集(避免逐行查询——buildEndpointContext 同款先例)。
80
+ * @param outlineDir 项目根(大纲端点存在性/软删校验读 outline.json)
81
+ */
82
+ export declare function listDanglingRelations(db: Db, outlineDir: string): DanglingRelationInfo[];
83
+ //# sourceMappingURL=relation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../../src/queries/relation.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,KAAK,EAAoC,mBAAmB,EAAkB,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAI1I,mEAAmE;AACnE,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,uBAAuB,CAAC;AAEnG,qDAAqD;AACrD,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBACrB,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM;CAKrD;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iDAAiD;AACjD,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AA4EpD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAChB,UAAU,EAAE,MAAM,GACjB,mBAAmB,CA4CrB;AAuGD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAWtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,EAAE,EACN,KAAK,EAAE;IACL,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,EACD,UAAU,EAAE,MAAM,GACjB,WAAW,CA8Cb;AAwBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzD;AAID,oCAAoC;AACpC,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAE/G,oCAAoC;AACpC,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,oBAAoB,EAAE,CA4DxF"}
@@ -0,0 +1,418 @@
1
+ // @whispering233/ai-editor-db 关系管理(S3.2):查询(k 跳遍历)/ 创建(判重)/ 物理删除
2
+ //
3
+ // 单一事实来源:doc/database/schema.md 第 41-80 行(relation_records 表 + 16 个预定义关系类型)、
4
+ // doc/api/endpoints.md 第 304-391 行(关系端点)、决策 2(通用关系表)、
5
+ // 决策 12 修订(关系可见性联动端点状态:任一端点软删即不可见;手动删关系 = 物理删)、
6
+ // 决策 10(plot_edge 剧情连线同规则处理)。
7
+ //
8
+ // 可见性过滤实现(决策 12 修订):
9
+ // - SQL 层:关系自身 deleted_at IS NULL + 各过滤条件
10
+ // - JS 层(统一处理两端点):实体端点查 entities 软删集合(一次 IN 查询);
11
+ // 大纲端点读 outline.json(readOutlineFile 收集 deleted 节点 id 集合)
12
+ // - name 联表填充同路径:实体查 entities.name、大纲节点用 outline.json title
13
+ //
14
+ // 级联软删:S3.1 的 softDeleteEntity 已内联 relation_records 级联 UPDATE(source_id/target_id
15
+ // 命中即标 deleted_at)——本卡不重复实现,注释确认。
16
+ import { findOutlineNode, readOutlineFile } from "../storage/outline.js";
17
+ import { getEntity } from "./entity.js";
18
+ import { RELATION_TYPES, generateId } from "@whispering233/ai-editor-shared";
19
+ import { nowIso } from "../storage/atomic.js";
20
+ /** 关系操作错误(带 code,与 OutlineError 同风格;路由层 catch 映射) */
21
+ export class RelationError extends Error {
22
+ code;
23
+ constructor(code, message) {
24
+ super(message);
25
+ this.name = "RelationError";
26
+ this.code = code;
27
+ }
28
+ }
29
+ /** 大纲端点 id(relation_records 端点类型约定,schema.md) */
30
+ export const OUTLINE_ENDPOINT_TYPE = "outline_node";
31
+ /** 实体端点类型集合(entities 表 type 列) */
32
+ const ENTITY_ENDPOINT_TYPES = ["character", "setting", "location", "hook"];
33
+ /**
34
+ * 构建可见性上下文:一次性收集两端点的软删集合与名称映射——
35
+ * 实体端点走 entities 表(IN 查询),大纲端点走 outline.json(软删标记 + title)。
36
+ */
37
+ function buildEndpointContext(db, outlineDir, endpointIds) {
38
+ const softDeleted = new Set();
39
+ const names = new Map();
40
+ const entityIds = [...endpointIds].filter((id) => id !== "root"); // root 非实体端点(无实际关系)
41
+ if (entityIds.length > 0) {
42
+ const placeholders = entityIds.map(() => "?").join(",");
43
+ // 软删实体集合
44
+ const softRows = db
45
+ .prepare(`SELECT id FROM entities WHERE deleted_at IS NOT NULL AND id IN (${placeholders})`)
46
+ .all(...entityIds);
47
+ for (const r of softRows)
48
+ softDeleted.add(r.id);
49
+ // 名称映射(含软删实体——名称填充不受可见性影响,过滤在另一层)
50
+ const nameRows = db
51
+ .prepare(`SELECT id, name FROM entities WHERE id IN (${placeholders})`)
52
+ .all(...entityIds);
53
+ for (const r of nameRows)
54
+ names.set(r.id, r.name);
55
+ }
56
+ // 大纲端点:读一次树,收集软删节点与标题
57
+ if (endpointIds.size > 0) {
58
+ const tree = readOutlineFile(outlineDir);
59
+ // 递归遍历(OutlineFileNode 联合:卷/章/场景,children 可选)
60
+ const visit = (node) => {
61
+ if (endpointIds.has(node.id)) {
62
+ names.set(node.id, node.title);
63
+ if (node.deleted === true)
64
+ softDeleted.add(node.id);
65
+ }
66
+ const kids = node.children;
67
+ if (kids !== undefined) {
68
+ for (const k of kids)
69
+ visit(k);
70
+ }
71
+ };
72
+ for (const child of tree.children)
73
+ visit(child);
74
+ }
75
+ return { softDeleted, names };
76
+ }
77
+ /** 行 → RelationRow(metadata JSON 解析;坏行防御同 entity.ts parseDataColumn 风格) */
78
+ function rowToRelationRow(row) {
79
+ return {
80
+ id: row.id,
81
+ source_type: row.source_type,
82
+ source_id: row.source_id,
83
+ target_type: row.target_type,
84
+ target_id: row.target_id,
85
+ relation_type: row.relation_type,
86
+ metadata: parseMetadata(row.metadata),
87
+ created_at: row.created_at,
88
+ updated_at: row.updated_at,
89
+ deleted_at: row.deleted_at ?? null,
90
+ };
91
+ }
92
+ /** metadata 列解析防御:非法 JSON / 非对象 → null(与 entity.ts 同风格) */
93
+ function parseMetadata(value) {
94
+ if (typeof value !== "string" || value === "")
95
+ return null;
96
+ try {
97
+ const parsed = JSON.parse(value);
98
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ /**
105
+ * 关系列表 + k 跳路径(GET /api/v1/relation,endpoints.md 第 306-343 行):
106
+ * 过滤条件组合(全部可选)+ 可见性过滤(关系自身未软删 + 两端点均未软删,决策 12 修订)。
107
+ * depth=1:直接关系(relations,联表填充 sourceName/targetName)。
108
+ * depth=2/3:追加 k 跳路径(paths:nodes/edges 结构)——**BFS 图不受 source_id/target_id
109
+ * 过滤限制**(起点定位用 source_id,图遍历需要全量可见边,否则中间节点的出边被过滤截断);
110
+ * relations 数组仍按全部过滤条件返回(endpoints.md「追加路径信息」语义)。
111
+ *
112
+ * @param outlineDir 项目根(大纲端点软删/标题校验读 outline.json)
113
+ */
114
+ export function listRelations(db, query, depth, outlineDir) {
115
+ // relations 查询:全部过滤条件(source/target 参与)
116
+ const relationRows = queryRelationRows(db, query, true);
117
+ // BFS 图查询(depth>=2):仅 type/relation_type 过滤,source_id/target_id 不参与
118
+ const graphRows = depth >= 2 ? queryRelationRows(db, query, false) : [];
119
+ const endpointIds = new Set();
120
+ for (const r of relationRows) {
121
+ endpointIds.add(r.source_id);
122
+ endpointIds.add(r.target_id);
123
+ }
124
+ const ctx = buildEndpointContext(db, outlineDir, endpointIds);
125
+ const visible = relationRows.filter((r) => !ctx.softDeleted.has(r.source_id) && !ctx.softDeleted.has(r.target_id));
126
+ // name 联表填充
127
+ const records = visible.map((r) => ({
128
+ id: r.id,
129
+ sourceType: r.source_type,
130
+ sourceId: r.source_id,
131
+ ...(ctx.names.has(r.source_id) ? { sourceName: ctx.names.get(r.source_id) } : {}),
132
+ targetType: r.target_type,
133
+ targetId: r.target_id,
134
+ ...(ctx.names.has(r.target_id) ? { targetName: ctx.names.get(r.target_id) } : {}),
135
+ relationType: r.relation_type,
136
+ ...(r.metadata !== null ? { metadata: r.metadata } : {}),
137
+ createdAt: r.created_at,
138
+ }));
139
+ if (depth === 1) {
140
+ return { relations: records };
141
+ }
142
+ // BFS 图的可见性过滤(独立上下文——图端点集合与 relations 端点集合不同)
143
+ const graphEndpointIds = new Set();
144
+ for (const r of graphRows) {
145
+ graphEndpointIds.add(r.source_id);
146
+ graphEndpointIds.add(r.target_id);
147
+ }
148
+ const graphCtx = buildEndpointContext(db, outlineDir, graphEndpointIds);
149
+ const graph = graphRows.filter((r) => !graphCtx.softDeleted.has(r.source_id) && !graphCtx.softDeleted.has(r.target_id));
150
+ return { relations: records, paths: collectPaths(graph, graphCtx.names, query, depth) };
151
+ }
152
+ /** 查询关系行:includeSourceTarget=false 时跳过 source_id/target_id 过滤(BFS 全图模式) */
153
+ function queryRelationRows(db, query, includeSourceTarget) {
154
+ const where = ["r.deleted_at IS NULL"];
155
+ const params = [];
156
+ if (query.sourceType !== undefined) {
157
+ where.push("r.source_type = ?");
158
+ params.push(query.sourceType);
159
+ }
160
+ if (includeSourceTarget && query.sourceId !== undefined) {
161
+ where.push("r.source_id = ?");
162
+ params.push(query.sourceId);
163
+ }
164
+ if (query.targetType !== undefined) {
165
+ where.push("r.target_type = ?");
166
+ params.push(query.targetType);
167
+ }
168
+ if (includeSourceTarget && query.targetId !== undefined) {
169
+ where.push("r.target_id = ?");
170
+ params.push(query.targetId);
171
+ }
172
+ if (query.relationType !== undefined) {
173
+ where.push("r.relation_type = ?");
174
+ params.push(query.relationType);
175
+ }
176
+ const rows = db
177
+ .prepare(`SELECT r.* FROM relation_records r WHERE ${where.join(" AND ")} ORDER BY r.created_at`)
178
+ .all(...params);
179
+ return rows.map(rowToRelationRow);
180
+ }
181
+ /**
182
+ * k 跳路径收集(depth=2/3,endpoints.md 第 337-341 行):
183
+ * 有向 BFS(沿 source→target 出边)——关系有方向,遍历沿建立方向;
184
+ * 起点 = query.sourceId(缺省时多起点:所有满足 sourceType 过滤的节点)。
185
+ * 防环:路径级 visited(同一条路径不重复经过节点;不同路径可共享节点)。
186
+ * depth=3 语义:3 层内可达图(「全量」= 3 层上限 + visited 防环,避免无限图爆炸)。
187
+ * 每条路径输出 { nodes: [{type,id,name}...], edges: [{from,to,relationType}...] },
188
+ * 起点 → 终点方向;name 缺失时用 id 占位(端点不在关系集合中时理论上不可达)。
189
+ */
190
+ function collectPaths(visible, names, query, depth) {
191
+ // 邻接表(出边)
192
+ const adj = new Map();
193
+ for (const r of visible) {
194
+ const list = adj.get(r.source_id) ?? [];
195
+ list.push({ toId: r.target_id, toType: r.target_type, relationType: r.relation_type });
196
+ adj.set(r.source_id, list);
197
+ }
198
+ // 起点集合:sourceId 优先;缺省时所有出现过的节点(满足过滤条件的图内节点)
199
+ const allIds = new Set();
200
+ for (const r of visible) {
201
+ allIds.add(r.source_id);
202
+ allIds.add(r.target_id);
203
+ }
204
+ const starts = query.sourceId !== undefined ? [query.sourceId] : [...allIds];
205
+ const nameOf = (id) => names.get(id) ?? id;
206
+ const typeOf = new Map();
207
+ for (const r of visible) {
208
+ typeOf.set(r.source_id, r.source_type);
209
+ typeOf.set(r.target_id, r.target_type);
210
+ }
211
+ const paths = [];
212
+ for (const start of starts) {
213
+ if (typeOf.get(start) === undefined)
214
+ continue; // 起点不在图内(过滤条件外)——跳过
215
+ const queue = [
216
+ {
217
+ nodeId: start,
218
+ nodes: [{ type: typeOf.get(start), id: start, name: nameOf(start) }],
219
+ edges: [],
220
+ },
221
+ ];
222
+ for (let level = 1; level <= depth && queue.length > 0; level++) {
223
+ const levelCount = queue.length;
224
+ for (let i = 0; i < levelCount; i++) {
225
+ const item = queue.shift();
226
+ const outEdges = adj.get(item.nodeId) ?? [];
227
+ for (const edge of outEdges) {
228
+ // 路径级防环:目标已在当前路径上则跳过
229
+ if (item.nodes.some((n) => n.id === edge.toId))
230
+ continue;
231
+ const path = {
232
+ nodeId: edge.toId,
233
+ nodes: [...item.nodes, { type: edge.toType, id: edge.toId, name: nameOf(edge.toId) }],
234
+ edges: [...item.edges, { from: item.nodeId, to: edge.toId, relationType: edge.relationType }],
235
+ };
236
+ paths.push({ nodes: path.nodes, edges: path.edges });
237
+ if (level < depth)
238
+ queue.push(path);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ return paths;
244
+ }
245
+ /**
246
+ * 单条关系(含可见性过滤,决策 12 修订);不存在/不可见返回 null。
247
+ * @param outlineDir 大纲端点软删校验读 outline.json
248
+ */
249
+ export function getRelation(db, id, outlineDir) {
250
+ const row = db.prepare("SELECT * FROM relation_records WHERE id = ? AND deleted_at IS NULL").get(id);
251
+ if (row === undefined)
252
+ return null;
253
+ const relation = rowToRelationRow(row);
254
+ const ctx = buildEndpointContext(db, outlineDir, new Set([relation.source_id, relation.target_id]));
255
+ if (ctx.softDeleted.has(relation.source_id) || ctx.softDeleted.has(relation.target_id)) {
256
+ return null;
257
+ }
258
+ return relation;
259
+ }
260
+ /**
261
+ * 创建关系(POST /api/v1/relation,endpoints.md 第 345-374 行):
262
+ * - **判重**:同 (source_id, target_id, relation_type) 且未软删已存在 → RELATION_EXISTS(409 语义)
263
+ * - relation_type 白名单(schema.md 16 个预定义类型,含 plot_edge 剧情连线——同规则,决策 10)
264
+ * - **端点存在性**:实体端点查 entities(非软删)、大纲端点读 outline.json(存在且非软删)
265
+ * - 时间戳应用层 ISO
266
+ *
267
+ * @param outlineDir 大纲端点校验读 outline.json
268
+ * @throws RelationError RELATION_EXISTS / ENDPOINT_NOT_FOUND / INVALID_RELATION_TYPE
269
+ */
270
+ export function createRelation(db, input, outlineDir) {
271
+ if (!RELATION_TYPES.includes(input.relationType)) {
272
+ throw new RelationError("INVALID_RELATION_TYPE", `非法关系类型: ${input.relationType}`);
273
+ }
274
+ // 端点存在性(决策 12 修订:软删端点不可建立新关系)
275
+ assertEndpointExists(db, outlineDir, input.sourceType, input.sourceId);
276
+ assertEndpointExists(db, outlineDir, input.targetType, input.targetId);
277
+ // 判重(同三元组未软删即视为已存在)
278
+ const dup = db
279
+ .prepare("SELECT 1 FROM relation_records WHERE source_id = ? AND target_id = ? AND relation_type = ? AND deleted_at IS NULL")
280
+ .get(input.sourceId, input.targetId, input.relationType);
281
+ if (dup !== undefined) {
282
+ throw new RelationError("RELATION_EXISTS", `关系已存在: ${input.sourceId} → ${input.targetId} (${input.relationType})`);
283
+ }
284
+ const now = nowIso();
285
+ // 关系 id:endpoints.md id 约定未列 rel- 前缀(运行时对象为 prop_/sess_/call_),
286
+ // 用 generateId("rel-") 保证唯一(前缀 + nanoid,与实体/节点同构)
287
+ const row = {
288
+ id: generateId("rel-"),
289
+ source_type: input.sourceType,
290
+ source_id: input.sourceId,
291
+ target_type: input.targetType,
292
+ target_id: input.targetId,
293
+ relation_type: input.relationType,
294
+ metadata: input.metadata ?? null,
295
+ created_at: now,
296
+ updated_at: now,
297
+ deleted_at: null,
298
+ };
299
+ db.prepare(`INSERT INTO relation_records (id, source_type, source_id, target_type, target_id, relation_type, metadata, created_at, updated_at)
300
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.source_type, row.source_id, row.target_type, row.target_id, row.relation_type, row.metadata === null ? null : JSON.stringify(row.metadata), row.created_at, row.updated_at);
301
+ return row;
302
+ }
303
+ /** 端点存在性校验(实体非软删 / 大纲节点存在非软删) */
304
+ function assertEndpointExists(db, outlineDir, type, id) {
305
+ if (id === "root") {
306
+ throw new RelationError("ENDPOINT_NOT_FOUND", `端点不存在或已软删: ${type}/${id}`);
307
+ }
308
+ if (ENTITY_ENDPOINT_TYPES.includes(type)) {
309
+ if (getEntity(db, id) === null) {
310
+ throw new RelationError("ENDPOINT_NOT_FOUND", `端点不存在或已软删: ${type}/${id}`);
311
+ }
312
+ return;
313
+ }
314
+ if (type === OUTLINE_ENDPOINT_TYPE) {
315
+ const tree = readOutlineFile(outlineDir);
316
+ const node = findOutlineNode(tree, id);
317
+ if (node === undefined || node.deleted === true) {
318
+ throw new RelationError("ENDPOINT_NOT_FOUND", `端点不存在或已软删: ${type}/${id}`);
319
+ }
320
+ return;
321
+ }
322
+ throw new RelationError("ENDPOINT_NOT_FOUND", `非法端点类型: ${type}`);
323
+ }
324
+ /**
325
+ * 物理删除关系(DELETE /api/v1/relation/:id,endpoints.md 第 376-391 行,决策 12 修订:
326
+ * 手动删关系 = 物理删,不进回收站)。
327
+ * @returns 影响行数(0 = 不存在,路由层映射 404 RELATION_NOT_FOUND)
328
+ */
329
+ export function deleteRelation(db, id) {
330
+ return db.prepare("DELETE FROM relation_records WHERE id = ?").run(id).changes;
331
+ }
332
+ /** 端点状态判定(实体查预构建批量 Map / 大纲节点查树;root 恒为正常——树根非关系端点但无害) */
333
+ function checkEndpointState(entityStates, tree, type, id) {
334
+ if (id === "root")
335
+ return "ok";
336
+ if (ENTITY_ENDPOINT_TYPES.includes(type)) {
337
+ return entityStates.get(id) ?? "missing";
338
+ }
339
+ if (type === OUTLINE_ENDPOINT_TYPE) {
340
+ const node = findOutlineNode(tree, id);
341
+ return node === undefined ? "missing" : node.deleted === true ? "deleted" : "ok";
342
+ }
343
+ return "missing"; // 未知端点类型(脏数据)
344
+ }
345
+ /**
346
+ * 全量悬空关系诊断(S6.4 工具 find_orphan_elements 下沉,tools.md「孤立元素」):
347
+ * 关系自身未软删,但端点异常——**每端点一条**记录(同一关系两端点异常时出两条):
348
+ * - *_missing:端点已物理删除(实体 purge / 大纲节点 purge)——dangling_relations 侧
349
+ * - *_deleted:端点已软删但关系未级联软删(跨存储不一致的 relation 侧,决策 12 修订
350
+ * 级联软删/启动一致性校验(决策 16 修订)兜底的对象同源)——inconsistent_soft_deletes 侧
351
+ * 软删自身的关系(回收站对象)**不在此列**——由回收站管理,非悬空。
352
+ * 实体端点状态一次 IN 批量收集(避免逐行查询——buildEndpointContext 同款先例)。
353
+ * @param outlineDir 项目根(大纲端点存在性/软删校验读 outline.json)
354
+ */
355
+ export function listDanglingRelations(db, outlineDir) {
356
+ const rows = db
357
+ .prepare("SELECT * FROM relation_records WHERE deleted_at IS NULL ORDER BY created_at")
358
+ .all();
359
+ if (rows.length === 0)
360
+ return [];
361
+ const tree = readOutlineFile(outlineDir);
362
+ // 实体端点 id 集合(一次 IN 批量收集状态)
363
+ const entityIds = new Set();
364
+ for (const raw of rows) {
365
+ const r = rowToRelationRow(raw);
366
+ if (ENTITY_ENDPOINT_TYPES.includes(r.source_type))
367
+ entityIds.add(r.source_id);
368
+ if (ENTITY_ENDPOINT_TYPES.includes(r.target_type))
369
+ entityIds.add(r.target_id);
370
+ }
371
+ const entityStates = new Map();
372
+ if (entityIds.size > 0) {
373
+ const placeholders = [...entityIds].map(() => "?").join(",");
374
+ const found = db
375
+ .prepare(`SELECT id, deleted_at FROM entities WHERE id IN (${placeholders})`)
376
+ .all(...entityIds);
377
+ const foundIds = new Set(found.map((f) => f.id));
378
+ for (const id of entityIds) {
379
+ if (!foundIds.has(id)) {
380
+ entityStates.set(id, "missing");
381
+ }
382
+ else {
383
+ const f = found.find((x) => x.id === id);
384
+ entityStates.set(id, f.deleted_at !== null ? "deleted" : "ok");
385
+ }
386
+ }
387
+ }
388
+ const out = [];
389
+ for (const raw of rows) {
390
+ const r = rowToRelationRow(raw);
391
+ const source = checkEndpointState(entityStates, tree, r.source_type, r.source_id);
392
+ if (source !== "ok") {
393
+ out.push({
394
+ id: r.id,
395
+ sourceType: r.source_type,
396
+ sourceId: r.source_id,
397
+ targetType: r.target_type,
398
+ targetId: r.target_id,
399
+ relationType: r.relation_type,
400
+ reason: source === "missing" ? "source_missing" : "source_deleted",
401
+ });
402
+ }
403
+ const target = checkEndpointState(entityStates, tree, r.target_type, r.target_id);
404
+ if (target !== "ok") {
405
+ out.push({
406
+ id: r.id,
407
+ sourceType: r.source_type,
408
+ sourceId: r.source_id,
409
+ targetType: r.target_type,
410
+ targetId: r.target_id,
411
+ relationType: r.relation_type,
412
+ reason: target === "missing" ? "target_missing" : "target_deleted",
413
+ });
414
+ }
415
+ }
416
+ return out;
417
+ }
418
+ //# sourceMappingURL=relation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation.js","sourceRoot":"","sources":["../../src/queries/relation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { EntityType } from "@whispering233/ai-editor-shared";
2
+ import { type Db } from "../connection.js";
3
+ /** 回收站实体条目(GET /api/v1/trash entities 项,endpoints.md 第 671 行) */
4
+ export interface DeletedEntityInfo {
5
+ id: string;
6
+ type: EntityType;
7
+ name: string;
8
+ deletedAt: string;
9
+ }
10
+ /**
11
+ * 回收站列表(实体侧,GET /api/v1/trash):entities 表 deleted_at IS NOT NULL,
12
+ * 按 deleted_at 倒序(回收站排序约定——ISO 字符串字典序即时间序,与 listDeletedNodes 一致)。
13
+ * 常规查询默认过滤软删(决策 12),回收站 API 是访问软删对象的唯一入口。
14
+ */
15
+ export declare function listDeletedEntities(db: Db): DeletedEntityInfo[];
16
+ /**
17
+ * 还原软删实体(POST /api/v1/trash/entity/:type/:id/restore,endpoints.md 第 676-692 行,决策 12 修订):
18
+ * - 自身:deleted_at 置 NULL + **updated_at 刷新**(决策 12 修订:与常规编辑一致,
19
+ * 保证决策 14 提案快照比对语义统一——还原后基于旧快照的提案必然 PROPOSAL_STALE)
20
+ * - 关联关系:relation_records(source_id = id **或** target_id = id)deleted_at 置 NULL——
21
+ * **全部还原**,不因另一端仍软删而跳过(决策 12 修订:数据永不丢失;端点仍软删的
22
+ * 关系由查询层可见性过滤,端点还原后自动可见)
23
+ * - 关联 Delta:delta_records(target_id = id)同规则
24
+ * 单库事务(withTransaction,原子性);幂等:实体不存在、类型不匹配或未软删 → 返回 null
25
+ * 且无任何副作用(存在性检查限定 deleted_at IS NOT NULL,级联 UPDATE 均带
26
+ * deleted_at IS NOT NULL 过滤——已还原的行不重复计数)。
27
+ *
28
+ * @param type 实体类型(路由路径参数;与 id 联合校验,类型不匹配视同不存在)
29
+ * @returns { restoredRelations, restoredDeltas } 实际还原的级联行数;实体不在回收站返回 null
30
+ */
31
+ export declare function restoreEntity(db: Db, type: EntityType, id: string): {
32
+ restoredRelations: number;
33
+ restoredDeltas: number;
34
+ } | null;
35
+ /**
36
+ * 物理清除软删实体(DELETE /api/v1/trash/entity/:type/:id,endpoints.md 第 713-724 行):
37
+ * 不可恢复——DELETE 实体本体 + 关联 relations(source_id = id 或 target_id = id)+
38
+ * deltas(target_id = id)。单库事务。
39
+ * 幂等:实体不存在或类型不匹配 → 返回 null 且无副作用。
40
+ *
41
+ * 注意:本层**不校验「必须已软删」**——「purge 仅用于回收站清理」的语义拦截由路由层负责
42
+ * (S4.3,与 outline purge 的 isSoftDeleted 拦截同构),本层只保证原子物理清除。
43
+ *
44
+ * @returns true(清除成功);实体不存在返回 null
45
+ */
46
+ export declare function purgeEntity(db: Db, type: EntityType, id: string): true | null;
47
+ /**
48
+ * 级联还原子树关联的 relation/delta(决策 12 修订:全部还原,不因另一端仍软删而跳过;
49
+ * 自 server/routes/outline.ts 下沉,参数与 SQL 语义不变):
50
+ * - relations:source 或 target 命中子树任一节点 → deleted_at 置 NULL(含 plot_edge 画布连线)
51
+ * - deltas:node_id 命中子树任一节点 → deleted_at 置 NULL(delta_records.node_id = 触发节点列)
52
+ * @returns { relations, deltas } 实际还原数(UPDATE.changes)
53
+ */
54
+ export declare function cascadeRestore(db: Db, subtreeIds: string[]): {
55
+ relations: number;
56
+ deltas: number;
57
+ };
58
+ /**
59
+ * 物理清除子树关联的 relation/delta(purge,决策 12:物理清除且不可恢复;
60
+ * 自 server/routes/outline.ts 下沉,参数与 SQL 语义不变):
61
+ * - relations:source 或 target 命中子树任一节点 → 物理删除
62
+ * - deltas:node_id 命中子树任一节点 → 物理删除
63
+ */
64
+ export declare function cascadePurge(db: Db, subtreeIds: string[]): void;
65
+ //# sourceMappingURL=trash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trash.d.ts","sourceRoot":"","sources":["../../src/queries/trash.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EAAmB,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAE5D,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAK/D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,MAAM,GACT;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiB9D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS7E;AAOD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAalG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAO/D"}