@musnows/scriverse 0.4.4 → 0.4.6

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
@@ -1702,15 +1702,15 @@ export class Store {
1702
1702
  });
1703
1703
  return this.getSetting(settingId);
1704
1704
  }
1705
- listSettings(workId) {
1705
+ listSettings(workId, includeContent = true) {
1706
1706
  this.getWork(workId);
1707
- return this.db.all("SELECT * FROM settings WHERE work_id = ? ORDER BY locked DESC, category, title", workId).map((row) => this.mapSetting(row));
1707
+ return this.db.all("SELECT * FROM settings WHERE work_id = ? ORDER BY locked DESC, category, title", workId).map((row) => this.mapSetting(row, includeContent));
1708
1708
  }
1709
- listSettingsPage(workId, pagination) {
1709
+ listSettingsPage(workId, pagination, includeContent = true) {
1710
1710
  this.getWork(workId);
1711
1711
  const page = paginationSql(pagination);
1712
1712
  const rows = this.db.all(`SELECT * FROM settings WHERE work_id = ? ORDER BY locked DESC, category, title${page.sql}`, workId, ...page.params);
1713
- return paginated(rows.map((row) => this.mapSetting(row)), pagination);
1713
+ return paginated(rows.map((row) => this.mapSetting(row, includeContent)), pagination);
1714
1714
  }
1715
1715
  getSetting(settingId) {
1716
1716
  const row = this.db.get("SELECT * FROM settings WHERE id = ?", settingId);
@@ -1741,13 +1741,14 @@ export class Store {
1741
1741
  this.audit(String(current.workId), "setting.deleted", "setting", settingId);
1742
1742
  });
1743
1743
  }
1744
- mapSetting(row) {
1744
+ mapSetting(row, includeContent = true) {
1745
+ const content = requiredString(row, "content");
1745
1746
  return {
1746
1747
  id: requiredString(row, "id"),
1747
1748
  workId: requiredString(row, "work_id"),
1748
1749
  title: requiredString(row, "title"),
1749
1750
  category: requiredString(row, "category"),
1750
- content: requiredString(row, "content"),
1751
+ ...(includeContent ? { content } : { contentPreview: content.replace(/\s+/gu, " ").trim().slice(0, 320) }),
1751
1752
  tags: json(requiredString(row, "tags_json"), []),
1752
1753
  status: requiredString(row, "status"),
1753
1754
  locked: booleanValue(row, "locked"),
@@ -1788,21 +1789,21 @@ export class Store {
1788
1789
  });
1789
1790
  return this.getRace(raceId);
1790
1791
  }
1791
- listRaces(workId) {
1792
+ listRaces(workId, includeMarkdown = true) {
1792
1793
  this.getWork(workId);
1793
- return this.db.all("SELECT * FROM races WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapRace(row));
1794
+ return this.db.all("SELECT * FROM races WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapRace(row, includeMarkdown));
1794
1795
  }
1795
- listRacesPage(workId, pagination) {
1796
+ listRacesPage(workId, pagination, includeMarkdown = true) {
1796
1797
  this.getWork(workId);
1797
1798
  const page = paginationSql(pagination);
1798
1799
  const rows = this.db.all(`SELECT * FROM races WHERE work_id = ? ORDER BY name${page.sql}`, workId, ...page.params);
1799
- return paginated(rows.map((row) => this.mapRace(row)), pagination);
1800
+ return paginated(rows.map((row) => this.mapRace(row, includeMarkdown)), pagination);
1800
1801
  }
1801
- getRace(raceId) {
1802
+ getRace(raceId, includeMarkdown = true) {
1802
1803
  const row = this.db.get("SELECT * FROM races WHERE id = ?", raceId);
1803
1804
  if (!row)
1804
1805
  throw notFound("种族");
1805
- return this.mapRace(row);
1806
+ return this.mapRace(row, includeMarkdown);
1806
1807
  }
1807
1808
  updateRace(raceId, input, source = "manual", sourceRef = null, changeNote = "", expectedVersionNo) {
1808
1809
  const current = this.getRace(raceId);
@@ -1908,9 +1909,9 @@ export class Store {
1908
1909
  const row = this.db.get("SELECT id FROM races WHERE work_id = ? AND normalized_name = ?", workId, normalizedName);
1909
1910
  return row ? requiredString(row, "id") : null;
1910
1911
  }
1911
- mapRace(row) {
1912
+ mapRace(row, includeMarkdown = true) {
1912
1913
  const raceId = requiredString(row, "id");
1913
- const lineage = this.raceLineage(raceId);
1914
+ const lineage = includeMarkdown ? this.raceLineage(raceId) : this.raceLineageNames(raceId);
1914
1915
  const settingsSections = knowledgeSectionsFromStored(row.settings_sections_json, json(requiredString(row, "settings_json"), []));
1915
1916
  const settings = settingsFromKnowledgeSections(settingsSections);
1916
1917
  const members = this.db.all("SELECT id, name FROM characters WHERE race_id = ? ORDER BY name", requiredString(row, "id")).map((member) => ({
@@ -1923,19 +1924,21 @@ export class Store {
1923
1924
  parentRaceId: optionalString(row, "parent_race_id"),
1924
1925
  name: requiredString(row, "name"),
1925
1926
  description: requiredString(row, "description"),
1926
- settings,
1927
- settingsMarkdown: settingsMarkdownFromList(settings),
1928
- settingsSections,
1927
+ ...(includeMarkdown
1928
+ ? { settings, settingsMarkdown: settingsMarkdownFromList(settings), settingsSections }
1929
+ : { settings: [], settingsCount: settingsSections.length }),
1929
1930
  lineage: lineage.map((item) => ({ id: item.id, name: item.name })),
1930
- effectiveSettings: lineage.flatMap((item, index) => item.settingsSections.map((section) => ({
1931
- title: section.title,
1932
- summary: section.summary,
1933
- sortOrder: section.sortOrder,
1934
- value: section.contentMarkdown,
1935
- sourceRaceId: item.id,
1936
- sourceRaceName: item.name,
1937
- inherited: index < lineage.length - 1
1938
- }))),
1931
+ ...(includeMarkdown
1932
+ ? { effectiveSettings: lineage.flatMap((item, index) => item.settingsSections.map((section) => ({
1933
+ title: section.title,
1934
+ summary: section.summary,
1935
+ sortOrder: section.sortOrder,
1936
+ value: section.contentMarkdown,
1937
+ sourceRaceId: item.id,
1938
+ sourceRaceName: item.name,
1939
+ inherited: index < lineage.length - 1
1940
+ }))) }
1941
+ : {}),
1939
1942
  memberIds: members.map((member) => member.characterId),
1940
1943
  members,
1941
1944
  versionNo: this.currentEntityVersionNo("race", requiredString(row, "id")),
@@ -1995,6 +1998,22 @@ export class Store {
1995
1998
  }
1996
1999
  return lineage.reverse();
1997
2000
  }
2001
+ raceLineageNames(raceId) {
2002
+ const lineage = [];
2003
+ const seen = new Set();
2004
+ let currentId = raceId;
2005
+ while (currentId) {
2006
+ if (seen.has(currentId))
2007
+ throw new AppError(500, "RACE_HIERARCHY_INVALID", "种族层级存在循环");
2008
+ seen.add(currentId);
2009
+ const row = this.db.get("SELECT id, name, parent_race_id FROM races WHERE id = ?", currentId);
2010
+ if (!row)
2011
+ throw new AppError(500, "RACE_HIERARCHY_INVALID", "种族层级引用了不存在的父种族");
2012
+ lineage.push({ id: requiredString(row, "id"), name: requiredString(row, "name") });
2013
+ currentId = optionalString(row, "parent_race_id");
2014
+ }
2015
+ return lineage.reverse();
2016
+ }
1998
2017
  replaceRaceMembers(raceId, raceName, memberIds) {
1999
2018
  const timestamp = now();
2000
2019
  this.db.run("UPDATE characters SET race_id = NULL, species = '', updated_at = ? WHERE race_id = ?", timestamp, raceId);
@@ -2029,15 +2048,15 @@ export class Store {
2029
2048
  });
2030
2049
  return this.getOrganization(organizationId);
2031
2050
  }
2032
- listOrganizations(workId) {
2051
+ listOrganizations(workId, includeMarkdown = true) {
2033
2052
  this.getWork(workId);
2034
- return this.db.all("SELECT * FROM organizations WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapOrganization(row));
2053
+ return this.db.all("SELECT * FROM organizations WHERE work_id = ? ORDER BY name", workId).map((row) => this.mapOrganization(row, includeMarkdown));
2035
2054
  }
2036
- listOrganizationsPage(workId, pagination) {
2055
+ listOrganizationsPage(workId, pagination, includeMarkdown = true) {
2037
2056
  this.getWork(workId);
2038
2057
  const page = paginationSql(pagination);
2039
2058
  const rows = this.db.all(`SELECT * FROM organizations WHERE work_id = ? ORDER BY name${page.sql}`, workId, ...page.params);
2040
- return paginated(rows.map((row) => this.mapOrganization(row)), pagination);
2059
+ return paginated(rows.map((row) => this.mapOrganization(row, includeMarkdown)), pagination);
2041
2060
  }
2042
2061
  getOrganization(organizationId) {
2043
2062
  const row = this.db.get("SELECT * FROM organizations WHERE id = ?", organizationId);
@@ -2124,7 +2143,7 @@ export class Store {
2124
2143
  });
2125
2144
  return { mergeId, target: this.getOrganization(targetOrganizationId), source };
2126
2145
  }
2127
- mapOrganization(row) {
2146
+ mapOrganization(row, includeMarkdown = true) {
2128
2147
  const settingsSections = knowledgeSectionsFromStored(row.settings_sections_json, json(requiredString(row, "settings_json"), []));
2129
2148
  const settings = settingsFromKnowledgeSections(settingsSections);
2130
2149
  const members = this.db.all(`SELECT c.id, c.name, m.role, m.note
@@ -2141,9 +2160,9 @@ export class Store {
2141
2160
  workId: requiredString(row, "work_id"),
2142
2161
  name: requiredString(row, "name"),
2143
2162
  description: requiredString(row, "description"),
2144
- settings,
2145
- settingsMarkdown: settingsMarkdownFromList(settings),
2146
- settingsSections,
2163
+ ...(includeMarkdown
2164
+ ? { settings, settingsMarkdown: settingsMarkdownFromList(settings), settingsSections }
2165
+ : { settings: [], settingsCount: settingsSections.length }),
2147
2166
  memberIds: members.map((member) => member.characterId),
2148
2167
  members,
2149
2168
  versionNo: this.currentEntityVersionNo("organization", requiredString(row, "id")),
@@ -2202,7 +2221,6 @@ export class Store {
2202
2221
  profile,
2203
2222
  currentState: character.currentState,
2204
2223
  lockedFields: [...character.lockedFields],
2205
- visibility: String(character.visibility),
2206
2224
  firstChapterId: character.firstChapterId
2207
2225
  };
2208
2226
  }
@@ -2247,8 +2265,8 @@ export class Store {
2247
2265
  this.assertOrganizationsInWork(workId, organizationIds);
2248
2266
  this.db.transaction(() => {
2249
2267
  this.db.run(`INSERT INTO characters (id, work_id, name, code, aliases_json, species, race_id, attributes_json, profile_json, current_state_json,
2250
- locked_fields_json, visibility, first_chapter_id, created_at, updated_at)
2251
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, characterId, workId, names.name, input.code?.trim() ?? "", JSON.stringify(names.aliases), species, raceId, JSON.stringify(input.attributes ?? {}), JSON.stringify(input.profile ?? {}), JSON.stringify(input.currentState ?? {}), JSON.stringify(input.lockedFields ?? []), input.visibility ?? "author", input.firstChapterId ?? null, timestamp, timestamp);
2268
+ locked_fields_json, first_chapter_id, created_at, updated_at)
2269
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, characterId, workId, names.name, input.code?.trim() ?? "", JSON.stringify(names.aliases), species, raceId, JSON.stringify(input.attributes ?? {}), JSON.stringify(input.profile ?? {}), JSON.stringify(input.currentState ?? {}), JSON.stringify(input.lockedFields ?? []), input.firstChapterId ?? null, timestamp, timestamp);
2252
2270
  this.insertCharacterNames(workId, characterId, names.entries);
2253
2271
  this.replaceCharacterOrganizations(characterId, organizationIds);
2254
2272
  this.insertCharacterVersion(characterId, 1, "create", null, "建立人物档案", timestamp);
@@ -2256,16 +2274,16 @@ export class Store {
2256
2274
  });
2257
2275
  return this.getCharacter(characterId);
2258
2276
  }
2259
- listCharacters(workId, includeProfileSections = false, includeMerged = false) {
2277
+ listCharacters(workId, includeProfileSections = false, includeMerged = false, includeRaceMarkdown = true) {
2260
2278
  this.getWork(workId);
2261
2279
  return this.db.all(`SELECT * FROM characters WHERE work_id = ?${includeMerged ? "" : " AND merged_into_character_id IS NULL"} ORDER BY name`, workId)
2262
- .map((row) => this.mapCharacter(row, includeProfileSections));
2280
+ .map((row) => this.mapCharacter(row, includeProfileSections, includeRaceMarkdown));
2263
2281
  }
2264
- listCharactersPage(workId, pagination, includeProfileSections = false, includeMerged = false) {
2282
+ listCharactersPage(workId, pagination, includeProfileSections = false, includeMerged = false, includeRaceMarkdown = true) {
2265
2283
  this.getWork(workId);
2266
2284
  const page = paginationSql(pagination);
2267
2285
  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);
2268
- return paginated(rows.map((row) => this.mapCharacter(row, includeProfileSections)), pagination);
2286
+ return paginated(rows.map((row) => this.mapCharacter(row, includeProfileSections, includeRaceMarkdown)), pagination);
2269
2287
  }
2270
2288
  mapCharacterProfileSection(row) {
2271
2289
  return {
@@ -2598,7 +2616,7 @@ export class Store {
2598
2616
  const lockedCurrent = this.getCharacter(characterId);
2599
2617
  this.assertExpectedRevision("character", characterId, expectedVersionNo, "人物", Number(lockedCurrent.versionNo));
2600
2618
  this.db.run(`UPDATE characters SET name = ?, code = ?, aliases_json = ?, species = ?, race_id = ?, attributes_json = ?, profile_json = ?, current_state_json = ?,
2601
- locked_fields_json = ?, visibility = ?, first_chapter_id = ?, updated_at = ? WHERE id = ?`, names.name, input.code === undefined ? String(current.code) : input.code.trim(), JSON.stringify(names.aliases), species, raceId, JSON.stringify(attributes), JSON.stringify(input.profile ?? current.profile), JSON.stringify(input.currentState ?? current.currentState), JSON.stringify(input.lockedFields ?? current.lockedFields), input.visibility ?? String(current.visibility), input.firstChapterId === undefined ? current.firstChapterId : input.firstChapterId, now(), characterId);
2619
+ locked_fields_json = ?, first_chapter_id = ?, updated_at = ? WHERE id = ?`, names.name, input.code === undefined ? String(current.code) : input.code.trim(), JSON.stringify(names.aliases), species, raceId, JSON.stringify(attributes), JSON.stringify(input.profile ?? current.profile), JSON.stringify(input.currentState ?? current.currentState), JSON.stringify(input.lockedFields ?? current.lockedFields), input.firstChapterId === undefined ? current.firstChapterId : input.firstChapterId, now(), characterId);
2602
2620
  this.db.run("DELETE FROM character_names WHERE character_id = ?", characterId);
2603
2621
  this.insertCharacterNames(workId, characterId, names.entries);
2604
2622
  if (organizationIds)
@@ -2685,8 +2703,8 @@ export class Store {
2685
2703
  const nextVersionNo = numberValue(this.db.get("SELECT COALESCE(MAX(version_no), 0) AS version_no FROM character_versions WHERE character_id = ?", characterId) ?? {}, "version_no") + 1;
2686
2704
  this.db.transaction(() => {
2687
2705
  this.db.run(`INSERT INTO characters (id, work_id, name, code, aliases_json, species, race_id, attributes_json, profile_json, current_state_json,
2688
- locked_fields_json, visibility, first_chapter_id, version_no, created_at, updated_at)
2689
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, characterId, workId, names.name, snapshot.code ?? "", JSON.stringify(names.aliases), species, raceId, JSON.stringify(snapshot.attributes ?? {}), JSON.stringify(snapshot.profile ?? {}), JSON.stringify(snapshot.currentState ?? {}), JSON.stringify(snapshot.lockedFields ?? []), snapshot.visibility ?? "author", snapshot.firstChapterId ?? null, nextVersionNo, timestamp, timestamp);
2706
+ locked_fields_json, first_chapter_id, version_no, created_at, updated_at)
2707
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, characterId, workId, names.name, snapshot.code ?? "", JSON.stringify(names.aliases), species, raceId, JSON.stringify(snapshot.attributes ?? {}), JSON.stringify(snapshot.profile ?? {}), JSON.stringify(snapshot.currentState ?? {}), JSON.stringify(snapshot.lockedFields ?? []), snapshot.firstChapterId ?? null, nextVersionNo, timestamp, timestamp);
2690
2708
  this.insertCharacterNames(workId, characterId, names.entries);
2691
2709
  this.replaceCharacterOrganizations(characterId, organizationIds);
2692
2710
  this.insertCharacterVersion(characterId, nextVersionNo, "restore", requiredString(version, "id"), `恢复至 v${versionNo}`, timestamp, workId);
@@ -2723,7 +2741,7 @@ export class Store {
2723
2741
  this.audit(workId, "character.deleted", "character", characterId, { versionNo });
2724
2742
  });
2725
2743
  }
2726
- mapCharacter(row, includeProfileSections = true) {
2744
+ mapCharacter(row, includeProfileSections = true, includeRaceMarkdown = true) {
2727
2745
  const indexedAliases = this.db.all("SELECT display_name FROM character_names WHERE character_id = ? AND kind = 'alias' ORDER BY sort_order", requiredString(row, "id")).map((item) => requiredString(item, "display_name"));
2728
2746
  const organizations = this.db.all(`SELECT o.id, o.name, m.role, m.note
2729
2747
  FROM character_organization_memberships m
@@ -2735,9 +2753,11 @@ export class Store {
2735
2753
  note: requiredString(item, "note")
2736
2754
  }));
2737
2755
  const raceId = optionalString(row, "race_id");
2738
- const race = raceId ? this.getRace(raceId) : undefined;
2756
+ const race = raceId ? this.getRace(raceId, includeRaceMarkdown) : undefined;
2739
2757
  const species = race ? String(race.name) : requiredString(row, "species");
2740
2758
  const profile = json(requiredString(row, "profile_json"), {});
2759
+ if (!includeProfileSections)
2760
+ delete profile.sections;
2741
2761
  const characterId = requiredString(row, "id");
2742
2762
  const profileSectionCount = Number(this.db.get("SELECT COUNT(*) AS count FROM character_profile_sections WHERE character_id = ?", characterId)?.count ?? 0);
2743
2763
  const markdownSections = includeProfileSections
@@ -2776,7 +2796,6 @@ export class Store {
2776
2796
  profileSectionCount,
2777
2797
  currentState: json(requiredString(row, "current_state_json"), {}),
2778
2798
  lockedFields: json(requiredString(row, "locked_fields_json"), []),
2779
- visibility: requiredString(row, "visibility"),
2780
2799
  firstChapterId: optionalString(row, "first_chapter_id"),
2781
2800
  mergedIntoCharacterId: optionalString(row, "merged_into_character_id"),
2782
2801
  mergedAt: optionalString(row, "merged_at"),