@optolith/database-schema 0.112.0 → 0.113.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/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [0.113.1](https://github.com/Optolith/database-schema/compare/v0.113.0...v0.113.1) (2026-10-26)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **cache:** missing sources for derived select options ([68347f2](https://github.com/Optolith/database-schema/commit/68347f21f6adfc62b4c755b4bc1ad5b8db0a543e))
10
+
11
+ ## [0.113.0](https://github.com/Optolith/database-schema/compare/v0.112.0...v0.113.0) (2026-10-26)
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * pin enhancement focus rules in settings
16
+
17
+ ### Features
18
+
19
+ * **cache:** include parent source refs in new applications and uses ([26a0c1a](https://github.com/Optolith/database-schema/commit/26a0c1a959dd7c5ced909cee03a13be67174cc5b))
20
+ * pin enhancement focus rules in settings ([711a7d5](https://github.com/Optolith/database-schema/commit/711a7d5ac1f008e0d684cb4a8e94c7c9209eae8a))
21
+
5
22
  ## [0.112.0](https://github.com/Optolith/database-schema/compare/v0.111.1...v0.112.0) (2026-10-24)
6
23
 
7
24
  ### ⚠ BREAKING CHANGES
package/gen/types.d.ts CHANGED
@@ -686,6 +686,7 @@ export type TypeAliasMap = {
686
686
  EnergyType: EnergyType
687
687
  EnhancementPrerequisite: EnhancementPrerequisite
688
688
  EnhancementPrerequisites: EnhancementPrerequisites
689
+ EnhancementsSettings: EnhancementsSettings
689
690
  EquipmentPackageItem: EquipmentPackageItem
690
691
  EquipmentPrerequisites: EquipmentPrerequisites
691
692
  Errata: Errata
@@ -29260,6 +29261,11 @@ export interface Settings {
29260
29261
  */
29261
29262
  styles: StyleSpecialAbilitySettings
29262
29263
 
29264
+ /**
29265
+ * Spell and Liturgical Chant Enhancements are part of focus rules. This type defines which focus rule must be enabled in order to use an enhancement. Essentially, this is a default prerequisite they must meet, but manually inserting them with every enhancement is a lot of work and error-prone. Therefore, in order to use an enhancement, prerequisites for the respective focus rule must be generated for every entry.
29266
+ */
29267
+ enhancements: EnhancementsSettings
29268
+
29263
29269
  /**
29264
29270
  * The publication that extends the description of books with additional information.
29265
29271
  */
@@ -29341,6 +29347,21 @@ export interface StyleSpecialAbilitySettings {
29341
29347
  liturgical: FocusRule_ID
29342
29348
  }
29343
29349
 
29350
+ /**
29351
+ * Spell and Liturgical Chant Enhancements are part of focus rules. This type defines which focus rule must be enabled in order to use an enhancement. Essentially, this is a default prerequisite they must meet, but manually inserting them with every enhancement is a lot of work and error-prone. Therefore, in order to use an enhancement, prerequisites for the respective focus rule must be generated for every entry.
29352
+ */
29353
+ export interface EnhancementsSettings {
29354
+ /**
29355
+ * The focus rule for spell enhancements.
29356
+ */
29357
+ spell: FocusRule_ID
29358
+
29359
+ /**
29360
+ * The focus rule for liturgical chant enhancements.
29361
+ */
29362
+ liturgicalChant: FocusRule_ID
29363
+ }
29364
+
29344
29365
  export interface SexPractice {
29345
29366
  /**
29346
29367
  * References to where in which publications this entry has been defined. Entries may also be added or removed in later printings.
@@ -102,11 +102,12 @@ const getApValueForSkillish = (config, id, ic) => {
102
102
  return assertExhaustive(config);
103
103
  }
104
104
  };
105
- const convertSkillApplicationOrUse = (entryId, id, applicationOrUse) => ({
105
+ const convertSkillApplicationOrUse = (entryId, id, applicationOrUse, src) => ({
106
106
  id: `${fromUniformCase(entryId)}+${applicationOrUse.id.toFixed()}`,
107
107
  content: {
108
108
  parent: entryId,
109
109
  skills: [id],
110
+ src,
110
111
  translations: applicationOrUse.translations,
111
112
  },
112
113
  });
@@ -131,7 +132,7 @@ const getSpellworkFilter = (category) => {
131
132
  }
132
133
  });
133
134
  };
134
- const getDerivedSkillishSelectOptions = (database, entryId, entity, category, options, filter) => {
135
+ const getDerivedSkillishSelectOptions = (database, entryId, entity, category, options, filter, parentSrc) => {
135
136
  const { prerequisites } = category;
136
137
  const { bindingCost, ap_value } = options;
137
138
  const instances = database.getAllInstanceContainersOfEntity(entity);
@@ -150,13 +151,12 @@ const getDerivedSkillishSelectOptions = (database, entryId, entity, category, op
150
151
  name: t10n.name,
151
152
  })),
152
153
  },
153
- newApplications: category.skill_applications?.map(app => convertSkillApplicationOrUse(entryId, id, app)) ??
154
- [],
155
- uses: category.skill_uses?.map(use => convertSkillApplicationOrUse(entryId, id, use)) ?? [],
154
+ newApplications: category.skill_applications?.map(app => convertSkillApplicationOrUse(entryId, id, app, parentSrc)) ?? [],
155
+ uses: category.skill_uses?.map(use => convertSkillApplicationOrUse(entryId, id, use, parentSrc)) ?? [],
156
156
  };
157
157
  });
158
158
  };
159
- const getDerivedSelectOptions = (selectOptionCategory, entryId, database) => {
159
+ const getDerivedSelectOptions = (selectOptionCategory, entryId, database, parentSrc) => {
160
160
  switch (selectOptionCategory.kind) {
161
161
  case "Blessings":
162
162
  return database
@@ -685,15 +685,15 @@ const getDerivedSelectOptions = (selectOptionCategory, entryId, database) => {
685
685
  const matchesIdRequirement = category.Skills.specific === undefined ||
686
686
  matchesSpecificSkillishIdList(category.Skills.specific, required => required === id);
687
687
  return matchesGroupRequirement && matchesIdRequirement;
688
- });
688
+ }, parentSrc);
689
689
  case "Spells":
690
- return getDerivedSkillishSelectOptions(database, entryId, "Spell", category.Spells, selectOptionCategory.Skills, getSpellworkFilter(category.Spells));
690
+ return getDerivedSkillishSelectOptions(database, entryId, "Spell", category.Spells, selectOptionCategory.Skills, getSpellworkFilter(category.Spells), parentSrc);
691
691
  case "Rituals":
692
- return getDerivedSkillishSelectOptions(database, entryId, "Ritual", category.Rituals, selectOptionCategory.Skills, getSpellworkFilter(category.Rituals));
692
+ return getDerivedSkillishSelectOptions(database, entryId, "Ritual", category.Rituals, selectOptionCategory.Skills, getSpellworkFilter(category.Rituals), parentSrc);
693
693
  case "LiturgicalChants":
694
- return getDerivedSkillishSelectOptions(database, entryId, "LiturgicalChant", category.LiturgicalChants, selectOptionCategory.Skills, getDefaultSkillishFilter(category.LiturgicalChants));
694
+ return getDerivedSkillishSelectOptions(database, entryId, "LiturgicalChant", category.LiturgicalChants, selectOptionCategory.Skills, getDefaultSkillishFilter(category.LiturgicalChants), parentSrc);
695
695
  case "Ceremonies":
696
- return getDerivedSkillishSelectOptions(database, entryId, "Ceremony", category.Ceremonies, selectOptionCategory.Skills, getDefaultSkillishFilter(category.Ceremonies));
696
+ return getDerivedSkillishSelectOptions(database, entryId, "Ceremony", category.Ceremonies, selectOptionCategory.Skills, getDefaultSkillishFilter(category.Ceremonies), parentSrc);
697
697
  default:
698
698
  return assertExhaustive(category);
699
699
  }
@@ -702,9 +702,9 @@ const getDerivedSelectOptions = (selectOptionCategory, entryId, database) => {
702
702
  return selectOptionCategory.CombatTechniques.categories.flatMap(category => {
703
703
  switch (category.kind) {
704
704
  case "CloseCombatTechniques":
705
- return getDerivedSkillishSelectOptions(database, entryId, "CloseCombatTechnique", category.CloseCombatTechniques, selectOptionCategory.CombatTechniques, getDefaultSkillishFilter(category.CloseCombatTechniques));
705
+ return getDerivedSkillishSelectOptions(database, entryId, "CloseCombatTechnique", category.CloseCombatTechniques, selectOptionCategory.CombatTechniques, getDefaultSkillishFilter(category.CloseCombatTechniques), parentSrc);
706
706
  case "RangedCombatTechniques":
707
- return getDerivedSkillishSelectOptions(database, entryId, "RangedCombatTechnique", category.RangedCombatTechniques, selectOptionCategory.CombatTechniques, getDefaultSkillishFilter(category.RangedCombatTechniques));
707
+ return getDerivedSkillishSelectOptions(database, entryId, "RangedCombatTechnique", category.RangedCombatTechniques, selectOptionCategory.CombatTechniques, getDefaultSkillishFilter(category.RangedCombatTechniques), parentSrc);
708
708
  default:
709
709
  return assertExhaustive(category);
710
710
  }
@@ -787,14 +787,14 @@ const getExplicitSelectOptions = (id, database) => database
787
787
  newApplications: [], // database.getAllChildInstanceContainersForParent("NewSkillApplication", Case("GeneralSelectOption", id)), // prevent duplicates as these can already be queried from the database
788
788
  uses: [], // database.getAllChildInstanceContainersForParent("SkillUse", Case("GeneralSelectOption", id)), // prevent duplicates as these can already be queried from the database
789
789
  }));
790
- const getSelectOptions = (selectOptions, id, database) => [
790
+ const getSelectOptions = (selectOptions, id, database, parentSrc) => [
791
791
  ...(selectOptions.derived === undefined
792
792
  ? []
793
- : getDerivedSelectOptions(selectOptions.derived, id, database)),
793
+ : getDerivedSelectOptions(selectOptions.derived, id, database, parentSrc)),
794
794
  ...getExplicitSelectOptions(id, database),
795
795
  ];
796
796
  const getSelectOptionsForResults = (database, entity, results) => results.reduce((acc, { id, content }) => {
797
- const options = getSelectOptions(content.select_options ?? {}, Case(entity, id), database);
797
+ const options = getSelectOptions(content.select_options ?? {}, Case(entity, id), database, content.src);
798
798
  if (options.length > 0) {
799
799
  acc[id] = options;
800
800
  }
@@ -1,16 +1,18 @@
1
- import type { ActivatableIdentifier, NewSkillApplication, NewSkillApplicationOrUseParentIdentifier, Skill_ID, SkillUse } from "../../gen/types.js";
1
+ import type { ActivatableIdentifier, NewSkillApplication, NewSkillApplicationOrUseParentIdentifier, PublicationRefs, Skill_ID, SkillUse } from "../../gen/types.js";
2
2
  import type { ActivatableSelectOptionsCache } from "./activatableSelectOptions.ts";
3
3
  import type { CacheBuilder } from "./internal.ts";
4
4
  export type ResolvedNewSkillApplication = {
5
5
  id: string;
6
6
  content: Omit<NewSkillApplication, "parent"> & {
7
7
  parent: NewSkillApplicationOrUseParentIdentifier | ActivatableIdentifier;
8
+ src: PublicationRefs | undefined;
8
9
  };
9
10
  };
10
11
  export type ResolvedSkillUse = {
11
12
  id: string;
12
13
  content: Omit<SkillUse, "parent"> & {
13
14
  parent: NewSkillApplicationOrUseParentIdentifier | ActivatableIdentifier;
15
+ src: PublicationRefs | undefined;
14
16
  };
15
17
  };
16
18
  export type NewApplicationsAndUsesCache = {
@@ -1,15 +1,23 @@
1
- const groupByAffectedSkill = (entries, prev = {}) => entries.reduce((acc, entry) => entry.content.skills.reduce((acc1, skillId) => {
1
+ const groupByAffectedSkill = (database, entries, prev = {}) => entries
2
+ .map(entry => ({
3
+ ...entry,
4
+ content: {
5
+ ...entry.content,
6
+ src: database.getInstanceOfEntityById(entry.content.parent)?.src,
7
+ },
8
+ }))
9
+ .reduce((acc, entry) => entry.content.skills.reduce((acc1, skillId) => {
2
10
  acc1[skillId] = [...(acc1[skillId] ?? []), entry];
3
11
  return acc1;
4
12
  }, acc), prev);
5
- const addGeneratedEntries = (generatedSelectOptions, prev) => Object.values(generatedSelectOptions).reduce((acc, byInstance) => Object.values(byInstance).reduce((acc1, resolvedSelectOptions) => resolvedSelectOptions.reduce((acc2, selectOption) => ({
6
- newApplications: groupByAffectedSkill(selectOption.newApplications, acc2.newApplications),
7
- uses: groupByAffectedSkill(selectOption.uses, acc2.uses),
13
+ const addGeneratedEntries = (database, generatedSelectOptions, prev) => Object.values(generatedSelectOptions).reduce((acc, byInstance) => Object.values(byInstance).reduce((acc1, resolvedSelectOptions) => resolvedSelectOptions.reduce((acc2, selectOption) => ({
14
+ newApplications: groupByAffectedSkill(database, selectOption.newApplications, acc2.newApplications),
15
+ uses: groupByAffectedSkill(database, selectOption.uses, acc2.uses),
8
16
  }), acc1), acc), prev);
9
17
  export const buildNewApplicationsAndUsesCache = (database, _idMap, generatedSelectOptions) => {
10
18
  const cache = {
11
- newApplications: groupByAffectedSkill(database.getAllInstanceContainersOfEntity("NewSkillApplication")),
12
- uses: groupByAffectedSkill(database.getAllInstanceContainersOfEntity("SkillUse")),
19
+ newApplications: groupByAffectedSkill(database, database.getAllInstanceContainersOfEntity("NewSkillApplication")),
20
+ uses: groupByAffectedSkill(database, database.getAllInstanceContainersOfEntity("SkillUse")),
13
21
  };
14
- return addGeneratedEntries(generatedSelectOptions, cache);
22
+ return addGeneratedEntries(database, generatedSelectOptions, cache);
15
23
  };
@@ -186,6 +186,10 @@ export declare const Settings: DB.SingletonEntity<"Settings", {
186
186
  magic: DB.MemberDecl<DB.ReferenceIdentifier, true>;
187
187
  liturgical: DB.MemberDecl<DB.ReferenceIdentifier, true>;
188
188
  }>, []>>, true>;
189
+ enhancements: DB.MemberDecl<DB.IncludeIdentifier<[], DB.TypeAlias<"EnhancementsSettings", DB.Object<{
190
+ spell: DB.MemberDecl<DB.ReferenceIdentifier, true>;
191
+ liturgicalChant: DB.MemberDecl<DB.ReferenceIdentifier, true>;
192
+ }>, []>>, true>;
189
193
  extendedBookDescriptionPublication: DB.MemberDecl<DB.ReferenceIdentifier, true>;
190
194
  maximumSkillRatingBonusEntry: DB.MemberDecl<DB.IncludeIdentifier<[], DB.Enum<"ActivatableIdentifier", {
191
195
  Adoruzepterzauber: DB.EnumCase<DB.ReferenceIdentifier>;
@@ -27,6 +27,10 @@ export const Settings = DB.SingletonEntity(import.meta.url, {
27
27
  comment: StyleSpecialAbilitySettings.comment,
28
28
  type: DB.IncludeIdentifier(StyleSpecialAbilitySettings),
29
29
  }),
30
+ enhancements: DB.Required({
31
+ comment: EnhancementsSettings.comment,
32
+ type: DB.IncludeIdentifier(EnhancementsSettings),
33
+ }),
30
34
  extendedBookDescriptionPublication: DB.Required({
31
35
  comment: "The publication that extends the description of books with additional information.",
32
36
  type: PublicationIdentifier(),
@@ -103,3 +107,17 @@ const StyleSpecialAbilitySettings = DB.TypeAlias(import.meta.url, {
103
107
  }),
104
108
  }),
105
109
  });
110
+ const EnhancementsSettings = DB.TypeAlias(import.meta.url, {
111
+ name: "EnhancementsSettings",
112
+ comment: "Spell and Liturgical Chant Enhancements are part of focus rules. This type defines which focus rule must be enabled in order to use an enhancement. Essentially, this is a default prerequisite they must meet, but manually inserting them with every enhancement is a lot of work and error-prone. Therefore, in order to use an enhancement, prerequisites for the respective focus rule must be generated for every entry.",
113
+ type: () => DB.Object({
114
+ spell: DB.Required({
115
+ comment: "The focus rule for spell enhancements.",
116
+ type: FocusRuleIdentifier(),
117
+ }),
118
+ liturgicalChant: DB.Required({
119
+ comment: "The focus rule for liturgical chant enhancements.",
120
+ type: FocusRuleIdentifier(),
121
+ }),
122
+ }),
123
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optolith/database-schema",
3
- "version": "0.112.0",
3
+ "version": "0.113.1",
4
4
  "description": "Definitions and utilities for the flat-file database of Optolith, a character creation tool for the Pen and Paper RPG “The Dark Eye 5”, and its external integrations into other software.",
5
5
  "keywords": [
6
6
  "tde",