@musnows/scriverse 0.3.6 → 0.3.8

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/store.js CHANGED
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { PLATFORM_AI_WORK_ID } from "./database.js";
3
3
  import { AppError, notFound } from "./errors.js";
4
4
  import { accountReference, logger } from "./logger.js";
5
+ import { paginated, paginationSql } from "./pagination.js";
5
6
  import { currentRequestActor } from "./request-context.js";
6
7
  import { countWords, documentShortSearchTerms, id, json, normalizeDocumentSearchText, normalizeParagraphSpacing, now, splitDocumentParagraphs } from "./utils.js";
7
8
  export const versionedEntityTypes = [
@@ -206,6 +207,27 @@ export class Store {
206
207
  actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
207
208
  }));
208
209
  }
210
+ listEntityVersionsPage(type, entityId, pagination) {
211
+ const page = paginationSql(pagination);
212
+ const rows = this.db.all(`SELECT version.*, user.display_name AS actor_display_name, user.username AS actor_username
213
+ FROM entity_versions version LEFT JOIN users user ON user.id = version.created_by_user_id
214
+ WHERE version.entity_type = ? AND version.entity_id = ? ORDER BY version.version_no DESC${page.sql}`, type, entityId, ...page.params);
215
+ if (!rows.length && pagination.page === 1)
216
+ this.versionedEntity(type, entityId);
217
+ return paginated(rows.map((row) => ({
218
+ id: requiredString(row, "id"),
219
+ workId: requiredString(row, "work_id"),
220
+ entityType: requiredString(row, "entity_type"),
221
+ entityId: requiredString(row, "entity_id"),
222
+ versionNo: numberValue(row, "version_no"),
223
+ snapshot: json(requiredString(row, "snapshot_json"), {}),
224
+ source: requiredString(row, "source"),
225
+ sourceRef: optionalString(row, "source_ref"),
226
+ changeNote: requiredString(row, "change_note"),
227
+ createdAt: requiredString(row, "created_at"),
228
+ actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
229
+ })), pagination);
230
+ }
209
231
  restoreEntityVersion(type, entityId, versionNo) {
210
232
  const version = this.db.get("SELECT * FROM entity_versions WHERE entity_type = ? AND entity_id = ? AND version_no = ?", type, entityId, versionNo);
211
233
  if (!version)
@@ -301,6 +323,16 @@ export class Store {
301
323
  WHERE COALESCE(work.is_internal, 0) = 0 AND (work.owner_user_id = ? OR membership.user_id = ?)
302
324
  ORDER BY work.updated_at DESC`, actor.userId, actor.userId).map((row) => this.mapWork(row));
303
325
  }
326
+ listWorksPage(pagination) {
327
+ const actor = currentRequestActor();
328
+ const page = paginationSql(pagination);
329
+ const rows = !actor || (actor.role === "admin" && actor.authentication !== "api-key")
330
+ ? this.db.all(`SELECT * FROM works WHERE COALESCE(is_internal, 0) = 0 ORDER BY updated_at DESC${page.sql}`, ...page.params)
331
+ : this.db.all(`SELECT DISTINCT work.* FROM works work LEFT JOIN work_memberships membership ON membership.work_id = work.id
332
+ WHERE COALESCE(work.is_internal, 0) = 0 AND (work.owner_user_id = ? OR membership.user_id = ?)
333
+ ORDER BY work.updated_at DESC${page.sql}`, actor.userId, actor.userId, ...page.params);
334
+ return paginated(rows.map((row) => this.mapWork(row)), pagination);
335
+ }
304
336
  getWork(workId) {
305
337
  const row = this.db.get("SELECT * FROM works WHERE id = ?", workId);
306
338
  if (!row)
@@ -486,6 +518,27 @@ export class Store {
486
518
  }));
487
519
  return { ...work, volumes };
488
520
  }
521
+ getWorkDirectoryPage(workId, pagination) {
522
+ const work = this.getWork(workId);
523
+ const volumeRows = this.db.all("SELECT * FROM volumes WHERE work_id = ? ORDER BY sort_order, created_at", workId);
524
+ const page = paginationSql(pagination);
525
+ const chapterRows = this.db.all(`SELECT id, work_id, volume_id, title, chapter_type, sort_order, word_count, version_no,
526
+ analysis_status, excluded_from_analysis, created_at, updated_at
527
+ FROM chapters WHERE work_id = ? ORDER BY sort_order, created_at${page.sql}`, workId, ...page.params);
528
+ const chapters = chapterRows.map((row) => this.mapChapterDirectoryEntry(row));
529
+ const chaptersByVolume = new Map();
530
+ for (const chapter of chapters) {
531
+ const volumeId = String(chapter.volumeId);
532
+ const list = chaptersByVolume.get(volumeId) ?? [];
533
+ list.push(chapter);
534
+ chaptersByVolume.set(volumeId, list);
535
+ }
536
+ const volumes = volumeRows.map((row) => ({
537
+ ...this.mapVolume(row),
538
+ chapters: chaptersByVolume.get(requiredString(row, "id")) ?? []
539
+ }));
540
+ return { ...work, volumes, directoryPage: paginated(chapters, pagination) };
541
+ }
489
542
  listFileVersions(workId) {
490
543
  this.getWork(workId);
491
544
  return this.db
@@ -505,6 +558,25 @@ export class Store {
505
558
  actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
506
559
  }));
507
560
  }
561
+ listFileVersionsPage(workId, pagination) {
562
+ this.getWork(workId);
563
+ const page = paginationSql(pagination);
564
+ const rows = this.db.all(`SELECT version.id, version.work_id, version.file_name, version.file_type, version.word_count, version.paragraph_count,
565
+ version.warnings_json, version.created_at, user.display_name AS actor_display_name, user.username AS actor_username
566
+ FROM file_versions version LEFT JOIN users user ON user.id = version.created_by_user_id
567
+ WHERE version.work_id = ? ORDER BY version.created_at DESC${page.sql}`, workId, ...page.params);
568
+ return paginated(rows.map((row) => ({
569
+ id: requiredString(row, "id"),
570
+ workId: requiredString(row, "work_id"),
571
+ fileName: requiredString(row, "file_name"),
572
+ fileType: requiredString(row, "file_type"),
573
+ wordCount: numberValue(row, "word_count"),
574
+ paragraphCount: numberValue(row, "paragraph_count"),
575
+ warnings: json(requiredString(row, "warnings_json"), []),
576
+ createdAt: requiredString(row, "created_at"),
577
+ actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
578
+ })), pagination);
579
+ }
508
580
  restoreFileVersion(workId, fileVersionId) {
509
581
  this.getWork(workId);
510
582
  const version = this.db.get("SELECT * FROM file_versions WHERE id = ? AND work_id = ?", fileVersionId, workId);
@@ -671,6 +743,17 @@ export class Store {
671
743
  }
672
744
  return rows.map((row) => this.mapChapterVersionRow(row));
673
745
  }
746
+ listChapterVersionsPage(chapterId, pagination) {
747
+ const page = paginationSql(pagination);
748
+ const rows = this.db.all(`SELECT version.*, user.display_name AS actor_display_name, user.username AS actor_username
749
+ FROM chapter_versions version LEFT JOIN users user ON user.id = version.created_by_user_id
750
+ WHERE version.chapter_id = ? ORDER BY version.version_no DESC${page.sql}`, chapterId, ...page.params);
751
+ if (!rows.length) {
752
+ this.getChapter(chapterId);
753
+ return paginated([], pagination);
754
+ }
755
+ return paginated(rows.slice(pagination.offset, pagination.offset + pagination.limit + 1).map((row) => this.mapChapterVersionRow(row)), pagination);
756
+ }
674
757
  listChapterInsights(chapterId) {
675
758
  this.getChapter(chapterId);
676
759
  return this.db
@@ -689,6 +772,24 @@ export class Store {
689
772
  createdAt: requiredString(row, "created_at")
690
773
  }));
691
774
  }
775
+ listChapterInsightsPage(chapterId, pagination) {
776
+ this.getChapter(chapterId);
777
+ const page = paginationSql(pagination);
778
+ const rows = this.db.all(`SELECT * FROM chapter_insights WHERE chapter_id = ? ORDER BY chapter_version DESC, created_at DESC${page.sql}`, chapterId, ...page.params);
779
+ return paginated(rows.map((row) => ({
780
+ id: requiredString(row, "id"),
781
+ chapterId: requiredString(row, "chapter_id"),
782
+ chapterVersion: numberValue(row, "chapter_version"),
783
+ summary: requiredString(row, "summary"),
784
+ events: json(requiredString(row, "events_json"), []),
785
+ characters: json(requiredString(row, "characters_json"), []),
786
+ settings: json(requiredString(row, "settings_json"), []),
787
+ evidence: json(requiredString(row, "evidence_json"), []),
788
+ uncertainties: json(requiredString(row, "uncertainties_json"), []),
789
+ status: requiredString(row, "status"),
790
+ createdAt: requiredString(row, "created_at")
791
+ })), pagination);
792
+ }
692
793
  listCurrentChapterInsights(workId) {
693
794
  this.getWork(workId);
694
795
  return this.db.all(`SELECT insight.id, insight.chapter_id, insight.summary, chapter.title AS chapter_title,
@@ -1027,6 +1128,35 @@ export class Store {
1027
1128
  updatedAt: optionalString(row, "updated_at")
1028
1129
  }));
1029
1130
  }
1131
+ listChapterOutlinesPage(workId, pagination) {
1132
+ this.getWork(workId);
1133
+ const page = paginationSql(pagination);
1134
+ const rows = this.db.all(`SELECT c.id AS chapter_id, c.title AS chapter_title, c.volume_id, c.sort_order AS chapter_order,
1135
+ v.title AS volume_title, v.sort_order AS volume_order,
1136
+ o.goal, o.conflict, o.turning_point, o.notes, o.status, o.created_at, o.updated_at,
1137
+ (SELECT COUNT(DISTINCT fo.foreshadow_id) FROM foreshadow_occurrences fo
1138
+ JOIN foreshadows f ON f.id = fo.foreshadow_id
1139
+ WHERE fo.chapter_id = c.id AND f.status IN ('planned', 'planted')) AS unresolved_count
1140
+ FROM chapters c
1141
+ JOIN volumes v ON v.id = c.volume_id
1142
+ LEFT JOIN chapter_outlines o ON o.chapter_id = c.id
1143
+ WHERE c.work_id = ?
1144
+ ORDER BY v.sort_order, c.sort_order, c.created_at${page.sql}`, workId, ...page.params);
1145
+ return paginated(rows.map((row) => ({
1146
+ chapterId: requiredString(row, "chapter_id"),
1147
+ chapterTitle: requiredString(row, "chapter_title"),
1148
+ volumeId: requiredString(row, "volume_id"),
1149
+ volumeTitle: requiredString(row, "volume_title"),
1150
+ goal: optionalString(row, "goal") ?? "",
1151
+ conflict: optionalString(row, "conflict") ?? "",
1152
+ turningPoint: optionalString(row, "turning_point") ?? "",
1153
+ notes: optionalString(row, "notes") ?? "",
1154
+ status: optionalString(row, "status") ?? "draft",
1155
+ unresolvedForeshadowCount: numberValue(row, "unresolved_count"),
1156
+ createdAt: optionalString(row, "created_at"),
1157
+ updatedAt: optionalString(row, "updated_at")
1158
+ })), pagination);
1159
+ }
1030
1160
  upsertChapterOutline(chapterId, input, source = "manual", sourceRef = null, changeNote = "") {
1031
1161
  const chapter = this.getChapter(chapterId);
1032
1162
  const current = this.getChapterOutline(chapterId);
@@ -1131,6 +1261,18 @@ export class Store {
1131
1261
  return this.db.all(`SELECT id FROM foreshadows WHERE work_id = ? ${where}
1132
1262
  ORDER BY CASE importance WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, created_at`, workId).map((row) => this.getForeshadow(requiredString(row, "id"), currentChapterId));
1133
1263
  }
1264
+ listForeshadowsPage(workId, pagination, status = "all", currentChapterId) {
1265
+ this.getWork(workId);
1266
+ if (currentChapterId)
1267
+ this.assertChapterInWork(currentChapterId, workId);
1268
+ const where = status === "unresolved"
1269
+ ? "AND status IN ('planned', 'planted')"
1270
+ : status === "resolved" ? "AND status IN ('resolved', 'abandoned')" : "";
1271
+ const page = paginationSql(pagination);
1272
+ const rows = this.db.all(`SELECT id FROM foreshadows WHERE work_id = ? ${where}
1273
+ ORDER BY CASE importance WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, created_at${page.sql}`, workId, ...page.params);
1274
+ return paginated(rows.map((row) => this.getForeshadow(requiredString(row, "id"), currentChapterId)), pagination);
1275
+ }
1134
1276
  updateForeshadow(foreshadowId, input, source = "manual", sourceRef = null, changeNote = "") {
1135
1277
  const current = this.getForeshadow(foreshadowId);
1136
1278
  const workId = String(current.workId);
@@ -1248,6 +1390,12 @@ export class Store {
1248
1390
  this.getWork(workId);
1249
1391
  return this.db.all("SELECT * FROM settings WHERE work_id = ? ORDER BY locked DESC, category, title", workId).map((row) => this.mapSetting(row));
1250
1392
  }
1393
+ listSettingsPage(workId, pagination) {
1394
+ this.getWork(workId);
1395
+ const page = paginationSql(pagination);
1396
+ const rows = this.db.all(`SELECT * FROM settings WHERE work_id = ? ORDER BY locked DESC, category, title${page.sql}`, workId, ...page.params);
1397
+ return paginated(rows.map((row) => this.mapSetting(row)), pagination);
1398
+ }
1251
1399
  getSetting(settingId) {
1252
1400
  const row = this.db.get("SELECT * FROM settings WHERE id = ?", settingId);
1253
1401
  if (!row)
@@ -1319,6 +1467,12 @@ export class Store {
1319
1467
  this.getWork(workId);
1320
1468
  return this.db.all("SELECT * FROM races WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapRace(row));
1321
1469
  }
1470
+ listRacesPage(workId, pagination) {
1471
+ this.getWork(workId);
1472
+ const page = paginationSql(pagination);
1473
+ const rows = this.db.all(`SELECT * FROM races WHERE work_id = ? ORDER BY name${page.sql}`, workId, ...page.params);
1474
+ return paginated(rows.map((row) => this.mapRace(row)), pagination);
1475
+ }
1322
1476
  getRace(raceId) {
1323
1477
  const row = this.db.get("SELECT * FROM races WHERE id = ?", raceId);
1324
1478
  if (!row)
@@ -1493,6 +1647,12 @@ export class Store {
1493
1647
  this.getWork(workId);
1494
1648
  return this.db.all("SELECT * FROM organizations WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapOrganization(row));
1495
1649
  }
1650
+ listOrganizationsPage(workId, pagination) {
1651
+ this.getWork(workId);
1652
+ const page = paginationSql(pagination);
1653
+ const rows = this.db.all(`SELECT * FROM organizations WHERE work_id = ? ORDER BY name${page.sql}`, workId, ...page.params);
1654
+ return paginated(rows.map((row) => this.mapOrganization(row)), pagination);
1655
+ }
1496
1656
  getOrganization(organizationId) {
1497
1657
  const row = this.db.get("SELECT * FROM organizations WHERE id = ?", organizationId);
1498
1658
  if (!row)
@@ -1666,6 +1826,12 @@ export class Store {
1666
1826
  return this.db.all(`SELECT * FROM characters WHERE work_id = ?${includeMerged ? "" : " AND merged_into_character_id IS NULL"} ORDER BY name`, workId)
1667
1827
  .map((row) => this.mapCharacter(row, includeProfileSections));
1668
1828
  }
1829
+ listCharactersPage(workId, pagination, includeProfileSections = false, includeMerged = false) {
1830
+ this.getWork(workId);
1831
+ const page = paginationSql(pagination);
1832
+ const rows = this.db.all(`SELECT * FROM characters WHERE work_id = ?${includeMerged ? "" : " AND merged_into_character_id IS NULL"} ORDER BY name${page.sql}`, workId, ...page.params);
1833
+ return paginated(rows.map((row) => this.mapCharacter(row, includeProfileSections)), pagination);
1834
+ }
1669
1835
  mapCharacterProfileSection(row) {
1670
1836
  return {
1671
1837
  id: requiredString(row, "id"),
@@ -1687,6 +1853,12 @@ export class Store {
1687
1853
  this.getCharacter(characterId);
1688
1854
  return this.db.all("SELECT * FROM character_profile_sections WHERE character_id = ? ORDER BY sort_order, created_at", characterId).map((row) => this.mapCharacterProfileSection(row));
1689
1855
  }
1856
+ listCharacterProfileSectionsPage(characterId, pagination) {
1857
+ this.getCharacter(characterId);
1858
+ const page = paginationSql(pagination);
1859
+ const rows = this.db.all(`SELECT * FROM character_profile_sections WHERE character_id = ? ORDER BY sort_order, created_at${page.sql}`, characterId, ...page.params);
1860
+ return paginated(rows.map((row) => this.mapCharacterProfileSection(row)), pagination);
1861
+ }
1690
1862
  listCharacterProfileSectionCatalog(characterId) {
1691
1863
  this.getCharacter(characterId);
1692
1864
  return this.db.all(`SELECT id, character_id, section_type, title, summary, sort_order, version_no
@@ -1813,6 +1985,27 @@ export class Store {
1813
1985
  actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
1814
1986
  }));
1815
1987
  }
1988
+ listCharacterProfileSectionVersionsPage(sectionId, pagination) {
1989
+ const page = paginationSql(pagination);
1990
+ const rows = this.db.all(`SELECT version.*, user.display_name AS actor_display_name, user.username AS actor_username
1991
+ FROM character_profile_section_versions version LEFT JOIN users user ON user.id = version.created_by_user_id
1992
+ WHERE version.section_id = ? ORDER BY version.version_no DESC${page.sql}`, sectionId, ...page.params);
1993
+ if (!rows.length && pagination.page === 1)
1994
+ this.getCharacterProfileSection(sectionId);
1995
+ return paginated(rows.map((row) => ({
1996
+ id: requiredString(row, "id"),
1997
+ workId: requiredString(row, "work_id"),
1998
+ characterId: requiredString(row, "character_id"),
1999
+ sectionId: requiredString(row, "section_id"),
2000
+ versionNo: numberValue(row, "version_no"),
2001
+ snapshot: json(requiredString(row, "snapshot_json"), {}),
2002
+ source: requiredString(row, "source"),
2003
+ sourceRef: optionalString(row, "source_ref"),
2004
+ changeNote: requiredString(row, "change_note"),
2005
+ createdAt: requiredString(row, "created_at"),
2006
+ actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
2007
+ })), pagination);
2008
+ }
1816
2009
  restoreCharacterProfileSection(sectionId, versionNo) {
1817
2010
  const version = this.db.get("SELECT * FROM character_profile_section_versions WHERE section_id = ? AND version_no = ?", sectionId, versionNo);
1818
2011
  if (!version)
@@ -1900,6 +2093,12 @@ export class Store {
1900
2093
  this.getWork(workId);
1901
2094
  return this.db.all("SELECT * FROM attachments WHERE work_id = ? ORDER BY created_at DESC", workId).map((row) => this.mapAttachment(row));
1902
2095
  }
2096
+ listAttachmentsPage(workId, pagination) {
2097
+ this.getWork(workId);
2098
+ const page = paginationSql(pagination);
2099
+ const rows = this.db.all(`SELECT * FROM attachments WHERE work_id = ? ORDER BY created_at DESC${page.sql}`, workId, ...page.params);
2100
+ return paginated(rows.map((row) => this.mapAttachment(row)), pagination);
2101
+ }
1903
2102
  getAttachment(attachmentId) {
1904
2103
  const row = this.db.get("SELECT * FROM attachments WHERE id = ?", attachmentId);
1905
2104
  if (!row)
@@ -1985,6 +2184,26 @@ export class Store {
1985
2184
  actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
1986
2185
  }));
1987
2186
  }
2187
+ listCharacterVersionsPage(characterId, pagination) {
2188
+ const page = paginationSql(pagination);
2189
+ const rows = this.db.all(`SELECT version.*, user.display_name AS actor_display_name, user.username AS actor_username
2190
+ FROM character_versions version LEFT JOIN users user ON user.id = version.created_by_user_id
2191
+ WHERE version.character_id = ? ORDER BY version.version_no DESC${page.sql}`, characterId, ...page.params);
2192
+ if (!rows.length && pagination.page === 1)
2193
+ this.getCharacter(characterId);
2194
+ return paginated(rows.map((row) => ({
2195
+ id: requiredString(row, "id"),
2196
+ workId: optionalString(row, "work_id"),
2197
+ characterId: requiredString(row, "character_id"),
2198
+ versionNo: numberValue(row, "version_no"),
2199
+ snapshot: json(requiredString(row, "snapshot_json"), {}),
2200
+ source: requiredString(row, "source"),
2201
+ sourceRef: optionalString(row, "source_ref"),
2202
+ changeNote: requiredString(row, "change_note"),
2203
+ createdAt: requiredString(row, "created_at"),
2204
+ actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? "历史数据"
2205
+ })), pagination);
2206
+ }
1988
2207
  restoreCharacter(characterId, versionNo) {
1989
2208
  const version = this.db.get("SELECT * FROM character_versions WHERE character_id = ? AND version_no = ?", characterId, versionNo);
1990
2209
  if (!version)
@@ -2291,6 +2510,12 @@ export class Store {
2291
2510
  this.getWork(workId);
2292
2511
  return this.db.all("SELECT * FROM timeline_tracks WHERE work_id = ? ORDER BY sort_order, created_at", workId).map((row) => this.mapTimelineTrack(row));
2293
2512
  }
2513
+ listTimelineTracksPage(workId, pagination) {
2514
+ this.getWork(workId);
2515
+ const page = paginationSql(pagination);
2516
+ const rows = this.db.all(`SELECT * FROM timeline_tracks WHERE work_id = ? ORDER BY sort_order, created_at${page.sql}`, workId, ...page.params);
2517
+ return paginated(rows.map((row) => this.mapTimelineTrack(row)), pagination);
2518
+ }
2294
2519
  getTimelineTrack(trackId) {
2295
2520
  const row = this.db.get("SELECT * FROM timeline_tracks WHERE id = ?", trackId);
2296
2521
  if (!row)
@@ -2345,6 +2570,12 @@ export class Store {
2345
2570
  .all("SELECT * FROM timeline_events WHERE work_id = ? ORDER BY time_sort IS NULL, time_sort, created_at", workId)
2346
2571
  .map((row) => this.mapTimelineEvent(row));
2347
2572
  }
2573
+ listTimelineEventsPage(workId, pagination) {
2574
+ this.getWork(workId);
2575
+ const page = paginationSql(pagination);
2576
+ const rows = this.db.all(`SELECT * FROM timeline_events WHERE work_id = ? ORDER BY time_sort IS NULL, time_sort, created_at${page.sql}`, workId, ...page.params);
2577
+ return paginated(rows.map((row) => this.mapTimelineEvent(row)), pagination);
2578
+ }
2348
2579
  getTimelineEvent(eventId) {
2349
2580
  const row = this.db.get("SELECT * FROM timeline_events WHERE id = ?", eventId);
2350
2581
  if (!row)
@@ -2507,6 +2738,12 @@ export class Store {
2507
2738
  .all("SELECT * FROM relationships WHERE work_id = ? AND confidence >= ? ORDER BY confidence DESC, created_at", workId, minimumConfidence)
2508
2739
  .map((row) => this.mapRelationship(row));
2509
2740
  }
2741
+ listRelationshipsPage(workId, pagination, minimumConfidence = 0) {
2742
+ this.getWork(workId);
2743
+ const page = paginationSql(pagination);
2744
+ const rows = this.db.all(`SELECT * FROM relationships WHERE work_id = ? AND confidence >= ? ORDER BY confidence DESC, created_at${page.sql}`, workId, minimumConfidence, ...page.params);
2745
+ return paginated(rows.map((row) => this.mapRelationship(row)), pagination);
2746
+ }
2510
2747
  getRelationship(relationshipId) {
2511
2748
  const row = this.db.get("SELECT * FROM relationships WHERE id = ?", relationshipId);
2512
2749
  if (!row)
@@ -2602,6 +2839,14 @@ export class Store {
2602
2839
  : this.db.all("SELECT * FROM review_items WHERE work_id = ? ORDER BY created_at DESC", workId);
2603
2840
  return rows.map((row) => this.mapReviewItem(row));
2604
2841
  }
2842
+ listReviewItemsPage(workId, pagination, status) {
2843
+ this.getWork(workId);
2844
+ const page = paginationSql(pagination);
2845
+ const rows = status
2846
+ ? this.db.all(`SELECT * FROM review_items WHERE work_id = ? AND status = ? ORDER BY created_at DESC${page.sql}`, workId, status, ...page.params)
2847
+ : this.db.all(`SELECT * FROM review_items WHERE work_id = ? ORDER BY created_at DESC${page.sql}`, workId, ...page.params);
2848
+ return paginated(rows.map((row) => this.mapReviewItem(row)), pagination);
2849
+ }
2605
2850
  getReviewItem(reviewId) {
2606
2851
  const row = this.db.get("SELECT * FROM review_items WHERE id = ?", reviewId);
2607
2852
  if (!row)
@@ -2659,6 +2904,14 @@ export class Store {
2659
2904
  throw notFound("AI 建议");
2660
2905
  return this.db.all("SELECT * FROM continuation_guard_runs WHERE suggestion_id = ? ORDER BY created_at DESC", suggestionId).map((row) => this.mapContinuationGuard(row));
2661
2906
  }
2907
+ listContinuationGuardsPage(suggestionId, pagination) {
2908
+ const suggestion = this.db.get("SELECT id FROM ai_suggestions WHERE id = ?", suggestionId);
2909
+ if (!suggestion)
2910
+ throw notFound("AI 建议");
2911
+ const page = paginationSql(pagination);
2912
+ const rows = this.db.all(`SELECT * FROM continuation_guard_runs WHERE suggestion_id = ? ORDER BY created_at DESC${page.sql}`, suggestionId, ...page.params);
2913
+ return paginated(rows.map((row) => this.mapContinuationGuard(row)), pagination);
2914
+ }
2662
2915
  getLatestContinuationGuard(suggestionId) {
2663
2916
  const row = this.db.get("SELECT * FROM continuation_guard_runs WHERE suggestion_id = ? ORDER BY created_at DESC LIMIT 1", suggestionId);
2664
2917
  return row ? this.mapContinuationGuard(row) : null;
@@ -2680,6 +2933,17 @@ export class Store {
2680
2933
  ORDER BY conversation.updated_at DESC, conversation.created_at DESC
2681
2934
  LIMIT 100`, workId).map((row) => this.mapAiConversation(row));
2682
2935
  }
2936
+ listAiConversationsPage(workId, pagination) {
2937
+ this.getWork(workId);
2938
+ const page = paginationSql(pagination);
2939
+ const rows = this.db.all(`SELECT conversation.*,
2940
+ (SELECT COUNT(*) FROM ai_conversation_messages message WHERE message.conversation_id = conversation.id) AS message_count,
2941
+ COALESCE((SELECT content FROM ai_conversation_messages message WHERE message.conversation_id = conversation.id ORDER BY message.created_at DESC, message.rowid DESC LIMIT 1), '') AS preview
2942
+ FROM ai_conversations conversation
2943
+ WHERE conversation.work_id = ?
2944
+ ORDER BY conversation.updated_at DESC, conversation.created_at DESC${page.sql}`, workId, ...page.params);
2945
+ return paginated(rows.map((row) => this.mapAiConversation(row)), pagination);
2946
+ }
2683
2947
  getAiConversation(conversationId) {
2684
2948
  const row = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
2685
2949
  if (!row)
@@ -2687,6 +2951,22 @@ export class Store {
2687
2951
  const messages = this.db.all("SELECT * FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at, rowid", conversationId).map((message) => this.mapAiConversationMessage(message));
2688
2952
  return { ...this.mapAiConversation(row), messageCount: messages.length, messages };
2689
2953
  }
2954
+ getAiConversationPage(conversationId, pagination) {
2955
+ const row = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
2956
+ if (!row)
2957
+ throw notFound("AI 对话");
2958
+ const countRow = this.db.get("SELECT COUNT(*) AS count FROM ai_conversation_messages WHERE conversation_id = ?", conversationId);
2959
+ const page = paginationSql(pagination);
2960
+ const rows = this.db.all(`SELECT * FROM ai_conversation_messages WHERE conversation_id = ? ORDER BY created_at DESC, rowid DESC${page.sql}`, conversationId, ...page.params);
2961
+ const messagesPage = paginated(rows.map((message) => this.mapAiConversationMessage(message)), pagination);
2962
+ messagesPage.items.reverse();
2963
+ return {
2964
+ ...this.mapAiConversation(row),
2965
+ messageCount: Number(countRow?.count ?? 0),
2966
+ messages: messagesPage.items,
2967
+ messagesPage
2968
+ };
2969
+ }
2690
2970
  getAiConversationContext(conversationId, workId, excludeMessageId) {
2691
2971
  const conversation = this.db.get("SELECT * FROM ai_conversations WHERE id = ?", conversationId);
2692
2972
  if (!conversation)
@@ -2840,6 +3120,12 @@ export class Store {
2840
3120
  this.getWork(workId);
2841
3121
  return this.db.all("SELECT * FROM analysis_tasks WHERE work_id = ? ORDER BY created_at DESC", workId).map((row) => this.mapTask(row));
2842
3122
  }
3123
+ listTasksPage(workId, pagination) {
3124
+ this.getWork(workId);
3125
+ const page = paginationSql(pagination);
3126
+ const rows = this.db.all(`SELECT * FROM analysis_tasks WHERE work_id = ? ORDER BY created_at DESC${page.sql}`, workId, ...page.params);
3127
+ return paginated(rows.map((row) => this.mapTask(row)), pagination);
3128
+ }
2843
3129
  getTask(taskId) {
2844
3130
  const row = this.db.get("SELECT * FROM analysis_tasks WHERE id = ?", taskId);
2845
3131
  if (!row)
@@ -3107,5 +3393,22 @@ export class Store {
3107
3393
  createdAt: requiredString(row, "created_at")
3108
3394
  }));
3109
3395
  }
3396
+ listAuditLogsPage(workId, pagination) {
3397
+ this.getWork(workId);
3398
+ const page = paginationSql(pagination);
3399
+ const rows = this.db.all(`SELECT log.*, user.display_name AS actor_display_name, user.username AS actor_username
3400
+ FROM audit_logs log LEFT JOIN users user ON user.id = log.user_id
3401
+ WHERE log.work_id = ? ORDER BY log.created_at DESC${page.sql}`, workId, ...page.params);
3402
+ return paginated(rows.map((row) => ({
3403
+ id: requiredString(row, "id"),
3404
+ action: requiredString(row, "action"),
3405
+ entityType: requiredString(row, "entity_type"),
3406
+ entityId: optionalString(row, "entity_id"),
3407
+ actor: optionalString(row, "actor_display_name") ?? optionalString(row, "actor_username") ?? requiredString(row, "actor"),
3408
+ userId: optionalString(row, "user_id"),
3409
+ detail: json(requiredString(row, "detail_json"), {}),
3410
+ createdAt: requiredString(row, "created_at")
3411
+ })), pagination);
3412
+ }
3110
3413
  }
3111
3414
  //# sourceMappingURL=store.js.map