@musnows/scriverse 0.5.9 → 0.5.11

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
@@ -7,6 +7,7 @@ import { currentRequestActor } from "./request-context.js";
7
7
  import { classifyWorkModulePermissions, emptyWorkModulePermissions, fullWorkModulePermissions, storedWorkModulePermissions } from "./work-permissions.js";
8
8
  import { countWords, documentShortSearchTerms, id, json, normalizeDocumentSearchText, normalizeParagraphSpacing, now, splitDocumentParagraphs } from "./utils.js";
9
9
  const defaultPlatformPageSizes = {
10
+ drafts: 30,
10
11
  settings: 30,
11
12
  characters: 30,
12
13
  races: 30,
@@ -29,6 +30,7 @@ function platformPageSizes(value) {
29
30
  : defaultPlatformPageSizes[key];
30
31
  };
31
32
  return {
33
+ drafts: pageSize("drafts"),
32
34
  settings: pageSize("settings"),
33
35
  characters: pageSize("characters"),
34
36
  races: pageSize("races"),
@@ -110,6 +112,7 @@ function settingsFromKnowledgeSections(sections) {
110
112
  export const versionedEntityTypes = [
111
113
  "work",
112
114
  "volume",
115
+ "draft",
113
116
  "setting",
114
117
  "race",
115
118
  "organization",
@@ -237,6 +240,8 @@ export class Store {
237
240
  return this.getWork(entityId);
238
241
  if (type === "volume")
239
242
  return this.getVolume(entityId);
243
+ if (type === "draft")
244
+ return this.getDraft(entityId);
240
245
  if (type === "setting")
241
246
  return this.getSetting(entityId);
242
247
  if (type === "race")
@@ -287,6 +292,12 @@ export class Store {
287
292
  keywords: entity.keywords,
288
293
  sortOrder: entity.sortOrder
289
294
  };
295
+ if (type === "draft")
296
+ return {
297
+ draftType: entity.draftType,
298
+ title: entity.title,
299
+ content: entity.content
300
+ };
290
301
  if (type === "setting")
291
302
  return {
292
303
  title: entity.title,
@@ -393,6 +404,7 @@ export class Store {
393
404
  const entities = [
394
405
  ...this.db.all("SELECT id, updated_at FROM works").map((row) => ["work", requiredString(row, "id"), requiredString(row, "updated_at")]),
395
406
  ...this.db.all("SELECT id, updated_at FROM volumes").map((row) => ["volume", requiredString(row, "id"), requiredString(row, "updated_at")]),
407
+ ...this.db.all("SELECT id, updated_at FROM drafts").map((row) => ["draft", requiredString(row, "id"), requiredString(row, "updated_at")]),
396
408
  ...this.db.all("SELECT id, updated_at FROM settings").map((row) => ["setting", requiredString(row, "id"), requiredString(row, "updated_at")]),
397
409
  ...this.db.all("SELECT id, updated_at FROM races").map((row) => ["race", requiredString(row, "id"), requiredString(row, "updated_at")]),
398
410
  ...this.db.all("SELECT id, updated_at FROM organizations").map((row) => ["organization", requiredString(row, "id"), requiredString(row, "updated_at")]),
@@ -474,6 +486,8 @@ export class Store {
474
486
  restored = this.updateWork(entityId, snapshot, expectedVersionNo, "restore", sourceRef, changeNote);
475
487
  else if (type === "volume")
476
488
  restored = this.updateVolume(entityId, snapshot, expectedVersionNo, "restore", sourceRef, changeNote);
489
+ else if (type === "draft")
490
+ restored = this.updateDraft(entityId, snapshot, "restore", sourceRef, changeNote, expectedVersionNo);
477
491
  else if (type === "setting")
478
492
  restored = this.updateSetting(entityId, snapshot, "restore", sourceRef, changeNote, expectedVersionNo);
479
493
  else if (type === "race")
@@ -513,6 +527,9 @@ export class Store {
513
527
  if (type === "volume") {
514
528
  return this.db.transaction(() => this.insertVolumeWithId(workId, entityId, snapshot, "restore", sourceRef, changeNote));
515
529
  }
530
+ if (type === "draft") {
531
+ return this.insertDraftWithId(workId, entityId, snapshot, "restore", sourceRef, changeNote);
532
+ }
516
533
  if (type === "setting") {
517
534
  return this.insertSettingWithId(workId, entityId, snapshot, "restore", sourceRef, changeNote);
518
535
  }
@@ -668,7 +685,7 @@ export class Store {
668
685
  autoRunConsecutiveFailures: Math.max(0, Number(row?.auto_run_consecutive_failures ?? 0) || 0),
669
686
  bookSummaryContextPercent: Math.min(90, Math.max(1, Number(row?.book_summary_context_percent ?? 50) || 50)),
670
687
  contextCompactThreshold: Math.min(90, Math.max(50, Number(row?.context_compact_threshold ?? 85) || 85)),
671
- agentTools: json(String(row?.agent_tools_json ?? '["story_index","read_chapters","search_story_entities","grep","read_character_sections"]'), ["story_index", "read_chapters", "search_story_entities", "grep", "read_character_sections"])
688
+ agentTools: json(String(row?.agent_tools_json ?? '["story_index","read_chapters","search_story_entities","grep","read_character_sections","search_drafts"]'), ["story_index", "read_chapters", "search_story_entities", "grep", "read_character_sections", "search_drafts"])
672
689
  .map((tool) => tool === "query_story_knowledge" ? "search_story_entities" : tool)
673
690
  .filter((tool, index, tools) => tools.indexOf(tool) === index),
674
691
  updatedAt: String(row?.updated_at ?? "")
@@ -2163,6 +2180,94 @@ export class Store {
2163
2180
  FROM chapters c JOIN volumes v ON v.id = c.volume_id WHERE c.id = ? AND c.work_id = ?`, chapterId, workId);
2164
2181
  return row ? numberValue(row, "sequence") : Number.MAX_SAFE_INTEGER;
2165
2182
  }
2183
+ createDraft(workId, input, source = "create", sourceRef = null) {
2184
+ this.getWork(workId);
2185
+ return this.insertDraftWithId(workId, id("draft"), input, source, sourceRef);
2186
+ }
2187
+ insertDraftWithId(workId, draftId, input, source = "create", sourceRef = null, changeNote = "") {
2188
+ const timestamp = now();
2189
+ this.db.transaction(() => {
2190
+ this.db.run(`INSERT INTO drafts (id, work_id, draft_type, title, content, created_at, updated_at)
2191
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, draftId, workId, input.draftType, input.title, input.content, timestamp, timestamp);
2192
+ this.syncMarkdownAttachmentReferences(workId, "draft", draftId, input.content);
2193
+ this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "建立创作草稿", timestamp);
2194
+ this.audit(workId, source === "restore" ? "draft.restored" : "draft.created", "draft", draftId, {
2195
+ draftType: input.draftType,
2196
+ source,
2197
+ sourceRef
2198
+ });
2199
+ });
2200
+ return this.getDraft(draftId);
2201
+ }
2202
+ listDrafts(workId, draftType, includeContent = false) {
2203
+ this.getWork(workId);
2204
+ return this.db.all(`SELECT * FROM drafts WHERE work_id = ? AND (? IS NULL OR draft_type = ?)
2205
+ ORDER BY updated_at DESC, title`, workId, draftType ?? null, draftType ?? null).map((row) => this.mapDraft(row, includeContent));
2206
+ }
2207
+ listDraftsPage(workId, pagination, draftType, includeContent = false) {
2208
+ this.getWork(workId);
2209
+ const page = paginationSql(pagination);
2210
+ const rows = this.db.all(`SELECT * FROM drafts WHERE work_id = ? AND (? IS NULL OR draft_type = ?)
2211
+ ORDER BY updated_at DESC, title${page.sql}`, workId, draftType ?? null, draftType ?? null, ...page.params);
2212
+ return paginated(rows.map((row) => this.mapDraft(row, includeContent)), pagination);
2213
+ }
2214
+ searchDrafts(workId, query, draftType, limit = 20) {
2215
+ this.getWork(workId);
2216
+ const safeLimit = Math.min(30, Math.max(1, Math.trunc(limit)));
2217
+ const normalizedQuery = query.normalize("NFKC").trim();
2218
+ const escapedQuery = normalizedQuery.replace(/[\\%_]/gu, "\\$&");
2219
+ const pattern = `%${escapedQuery}%`;
2220
+ const rows = normalizedQuery
2221
+ ? this.db.all(`SELECT * FROM drafts
2222
+ WHERE work_id = ? AND (? IS NULL OR draft_type = ?)
2223
+ AND (title LIKE ? ESCAPE '\\' COLLATE NOCASE OR content LIKE ? ESCAPE '\\' COLLATE NOCASE)
2224
+ ORDER BY CASE WHEN title LIKE ? ESCAPE '\\' COLLATE NOCASE THEN 0 ELSE 1 END, updated_at DESC
2225
+ LIMIT ?`, workId, draftType ?? null, draftType ?? null, pattern, pattern, pattern, safeLimit)
2226
+ : this.db.all(`SELECT * FROM drafts WHERE work_id = ? AND (? IS NULL OR draft_type = ?)
2227
+ ORDER BY updated_at DESC, title LIMIT ?`, workId, draftType ?? null, draftType ?? null, safeLimit);
2228
+ return rows.map((row) => this.mapDraft(row, true));
2229
+ }
2230
+ getDraft(draftId) {
2231
+ const row = this.db.get("SELECT * FROM drafts WHERE id = ?", draftId);
2232
+ if (!row)
2233
+ throw notFound("草稿");
2234
+ return this.mapDraft(row, true);
2235
+ }
2236
+ updateDraft(draftId, input, source = "manual", sourceRef = null, changeNote = "", expectedVersionNo) {
2237
+ const current = this.getDraft(draftId);
2238
+ const content = input.content ?? String(current.content);
2239
+ this.db.transaction(() => {
2240
+ this.assertExpectedVersion("draft", draftId, expectedVersionNo, "草稿");
2241
+ this.db.run("UPDATE drafts SET draft_type = ?, title = ?, content = ?, updated_at = ? WHERE id = ?", input.draftType ?? String(current.draftType), input.title ?? String(current.title), content, now(), draftId);
2242
+ this.syncMarkdownAttachmentReferences(String(current.workId), "draft", draftId, content);
2243
+ this.recordEntityVersion("draft", draftId, source, sourceRef, changeNote || "更新创作草稿");
2244
+ this.audit(String(current.workId), "draft.updated", "draft", draftId, { fields: Object.keys(input), source, sourceRef });
2245
+ });
2246
+ return this.getDraft(draftId);
2247
+ }
2248
+ deleteDraft(draftId, expectedVersionNo) {
2249
+ const current = this.getDraft(draftId);
2250
+ this.db.transaction(() => {
2251
+ this.assertExpectedVersion("draft", draftId, expectedVersionNo, "草稿");
2252
+ this.recordEntityVersion("draft", draftId, "delete", null, "删除创作草稿");
2253
+ this.clearMarkdownAttachmentReferences("draft", draftId);
2254
+ this.db.run("DELETE FROM drafts WHERE id = ?", draftId);
2255
+ this.audit(String(current.workId), "draft.deleted", "draft", draftId);
2256
+ });
2257
+ }
2258
+ mapDraft(row, includeContent) {
2259
+ const content = requiredString(row, "content");
2260
+ return {
2261
+ id: requiredString(row, "id"),
2262
+ workId: requiredString(row, "work_id"),
2263
+ draftType: requiredString(row, "draft_type"),
2264
+ title: requiredString(row, "title"),
2265
+ ...(includeContent ? { content } : { contentPreview: content.replace(/\s+/gu, " ").trim().slice(0, 320) }),
2266
+ versionNo: this.currentEntityVersionNo("draft", requiredString(row, "id")),
2267
+ createdAt: requiredString(row, "created_at"),
2268
+ updatedAt: requiredString(row, "updated_at")
2269
+ };
2270
+ }
2166
2271
  createSetting(workId, input, source = "create", sourceRef = null) {
2167
2272
  this.getWork(workId);
2168
2273
  return this.insertSettingWithId(workId, id("setting"), input, source, sourceRef);
@@ -5445,9 +5550,10 @@ export class Store {
5445
5550
  exportWork(workId) {
5446
5551
  const tree = this.getWorkTree(workId);
5447
5552
  return {
5448
- schemaVersion: 7,
5553
+ schemaVersion: 8,
5449
5554
  exportedAt: now(),
5450
5555
  work: tree,
5556
+ drafts: this.listDrafts(workId, undefined, true),
5451
5557
  settings: this.listSettings(workId),
5452
5558
  characters: this.listCharacters(workId, true, true),
5453
5559
  races: this.listRaces(workId),