coh-content-db 2.0.0-rc.2 → 2.0.0-rc.21

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 (120) hide show
  1. package/.editorconfig +10 -11
  2. package/.github/workflows/build.yml +4 -2
  3. package/.github/workflows/pull-request.yml +1 -1
  4. package/.github/workflows/release.yml +1 -1
  5. package/CHANGELOG.md +47 -0
  6. package/README.md +77 -32
  7. package/dist/coh-content-db.d.ts +755 -290
  8. package/dist/coh-content-db.js +1028 -358
  9. package/dist/coh-content-db.js.map +1 -1
  10. package/dist/coh-content-db.mjs +998 -349
  11. package/dist/coh-content-db.mjs.map +1 -1
  12. package/eslint.config.mjs +1 -0
  13. package/jest.config.mjs +1 -0
  14. package/package.json +14 -14
  15. package/src/main/api/alignment.ts +18 -2
  16. package/src/main/api/badge-data.ts +29 -51
  17. package/src/main/api/badge-requirement-data.ts +64 -0
  18. package/src/main/api/badge-requirement-type.ts +32 -0
  19. package/src/main/api/badge-type.ts +15 -15
  20. package/src/main/api/bundle-data.ts +47 -0
  21. package/src/main/api/bundle-header-data.ts +44 -0
  22. package/src/main/api/contact-data.ts +49 -0
  23. package/src/main/api/enhancement-category.ts +26 -26
  24. package/src/main/api/level-range-data.ts +4 -0
  25. package/src/main/api/location-data.ts +28 -0
  26. package/src/main/api/markdown-string.ts +4 -0
  27. package/src/main/api/mission-data.ts +57 -0
  28. package/src/main/api/mission-flashback-data.ts +31 -0
  29. package/src/main/api/mission-type.ts +2 -0
  30. package/src/main/api/morality.ts +49 -0
  31. package/src/main/api/set-title-data.ts +4 -0
  32. package/src/main/api/sex.ts +8 -1
  33. package/src/main/api/variant-context.ts +11 -0
  34. package/src/main/api/variant-data.ts +22 -0
  35. package/src/main/api/zone-data.ts +44 -0
  36. package/src/main/api/zone-type.ts +59 -0
  37. package/src/main/db/abstract-index.ts +37 -0
  38. package/src/main/db/alignment-list.ts +54 -0
  39. package/src/main/db/badge-index.ts +83 -0
  40. package/src/main/db/badge-requirement.ts +81 -0
  41. package/src/main/db/badge-search-options.ts +52 -0
  42. package/src/main/db/badge.ts +97 -74
  43. package/src/main/db/bundle-header.ts +52 -0
  44. package/src/main/db/coh-content-database.ts +123 -14
  45. package/src/main/db/contact.ts +63 -0
  46. package/src/main/db/level-range.ts +15 -0
  47. package/src/main/db/location.ts +30 -0
  48. package/src/main/db/mission.ts +108 -0
  49. package/src/main/db/morality-list.ts +99 -0
  50. package/src/main/db/paged.ts +11 -0
  51. package/src/main/db/set-title-ids.ts +10 -0
  52. package/src/main/db/variants.ts +84 -0
  53. package/src/main/db/zone.ts +57 -0
  54. package/src/main/index.ts +33 -18
  55. package/src/main/util/coalesce-to-array.ts +13 -0
  56. package/src/main/util/links.ts +104 -0
  57. package/src/main/util/to-date.ts +9 -0
  58. package/src/test/api/alignment.test.ts +38 -4
  59. package/src/test/api/badge-data.fixture.ts +2 -15
  60. package/src/test/api/badge-data.test.ts +5 -4
  61. package/src/test/api/badge-requirement-data.fixture.ts +7 -0
  62. package/src/test/api/badge-requirement-type.test.ts +31 -0
  63. package/src/test/api/badge-type.test.ts +5 -5
  64. package/src/test/api/bundle-data.fixture.ts +7 -0
  65. package/src/test/api/bundle-header-data.fixture.ts +8 -0
  66. package/src/test/api/contact-data.fixture.ts +7 -0
  67. package/src/test/api/enhancement-category.test.ts +5 -5
  68. package/src/test/api/mission-data.fixture.ts +12 -0
  69. package/src/test/api/morality.test.ts +31 -0
  70. package/src/test/api/sex.test.ts +33 -1
  71. package/src/test/api/zone-data.fixture.ts +9 -0
  72. package/src/test/db/abstract-index.test.ts +55 -0
  73. package/src/test/db/alignment-list.test.ts +200 -0
  74. package/src/test/db/badge-index.test.ts +653 -0
  75. package/src/test/db/badge-requirement.test.ts +145 -0
  76. package/src/test/db/badge.test.ts +416 -14
  77. package/src/test/db/bundle-header.test.ts +89 -0
  78. package/src/test/db/coh-content-database.test.ts +265 -24
  79. package/src/test/db/contact.test.ts +98 -0
  80. package/src/test/db/level-range.test.ts +47 -0
  81. package/src/test/db/location.test.ts +51 -0
  82. package/src/test/db/mission.test.ts +173 -0
  83. package/src/test/db/morality-list.test.ts +457 -0
  84. package/src/test/db/set-title-ids.test.ts +19 -0
  85. package/src/test/db/variants.test.ts +188 -0
  86. package/src/test/db/zone.test.ts +81 -0
  87. package/src/test/integration.test.ts +16 -0
  88. package/src/test/util/coalese-to-array.test.ts +17 -0
  89. package/src/test/util/links.test.ts +149 -0
  90. package/src/test/util/to-date.test.ts +15 -0
  91. package/src/main/api/alternate-data.ts +0 -22
  92. package/src/main/api/badge-partial-data.ts +0 -65
  93. package/src/main/api/badge-partial-type.ts +0 -8
  94. package/src/main/api/change.ts +0 -14
  95. package/src/main/api/game-map-data.ts +0 -26
  96. package/src/main/api/plaque-type.ts +0 -6
  97. package/src/main/api/server-group-data.ts +0 -65
  98. package/src/main/api/vidiot-map-data.ts +0 -18
  99. package/src/main/api/vidiot-map-point-of-interest-data.ts +0 -30
  100. package/src/main/changelog.ts +0 -20
  101. package/src/main/db/alternates.ts +0 -81
  102. package/src/main/db/badge-partial.ts +0 -35
  103. package/src/main/db/game-map.ts +0 -33
  104. package/src/main/db/server-group.ts +0 -112
  105. package/src/main/db/vidiot-map-point-of-interest.ts +0 -40
  106. package/src/main/db/vidiot-map.ts +0 -25
  107. package/src/main/util.ts +0 -17
  108. package/src/test/api/badge-partial-data.fixture.ts +0 -17
  109. package/src/test/api/badge-partial-type.test.ts +0 -31
  110. package/src/test/api/game-map-data.fixture.ts +0 -10
  111. package/src/test/api/plaque-type.test.ts +0 -31
  112. package/src/test/api/server-group-data.fixture.ts +0 -23
  113. package/src/test/api/server-group-data.test.ts +0 -15
  114. package/src/test/api/vidiot-map-point-of-interest.fixture.ts +0 -10
  115. package/src/test/api/vidiot-map.fixture.ts +0 -9
  116. package/src/test/changelog.test.ts +0 -36
  117. package/src/test/db/alternates.test.ts +0 -223
  118. package/src/test/db/server-group.test.ts +0 -124
  119. package/src/test/index.test.ts +0 -10
  120. package/src/test/util.test.ts +0 -39
@@ -0,0 +1,63 @@
1
+ import { Link } from '../api/link'
2
+ import { Key } from './key'
3
+ import { MarkdownString } from '../api/markdown-string'
4
+ import { ContactData } from '../api/contact-data'
5
+ import { Location } from './location'
6
+ import { MoralityList } from './morality-list'
7
+ import { coalesceToArray } from '../util/coalesce-to-array'
8
+ import { LevelRange } from './level-range'
9
+
10
+ export class Contact {
11
+ /**
12
+ * Unique key used to reference this contact.
13
+ *
14
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
15
+ */
16
+ readonly key: string
17
+
18
+ /**
19
+ * The name of this contact.
20
+ */
21
+ readonly name: string
22
+
23
+ /**
24
+ * The contact's title.
25
+ */
26
+ readonly title?: string
27
+
28
+ /**
29
+ * The character moralities that this contact will interact with.
30
+ */
31
+ readonly morality: MoralityList
32
+
33
+ /**
34
+ * The location of this contact.
35
+ */
36
+ readonly location?: Location
37
+
38
+ /**
39
+ * The level range this contact will offer missions for.
40
+ */
41
+ readonly levelRange?: LevelRange
42
+
43
+ /**
44
+ * Freeform notes or tips about the contact.
45
+ */
46
+ readonly notes?: MarkdownString
47
+
48
+ /**
49
+ * List of external links. Wiki, forums, etc.
50
+ */
51
+ readonly links: Link[]
52
+
53
+ constructor(data: ContactData) {
54
+ this.key = new Key(data.key).value
55
+ this.name = data.name
56
+ this.title = data.title
57
+ this.morality = new MoralityList(coalesceToArray(data.morality))
58
+ this.location = data.location
59
+ this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
60
+ this.notes = data.notes
61
+ this.links = data.links ?? []
62
+ }
63
+ }
@@ -0,0 +1,15 @@
1
+ import { LevelRangeData } from '../api/level-range-data'
2
+
3
+ export class LevelRange {
4
+ readonly min: number
5
+ readonly max?: number
6
+
7
+ constructor(value: LevelRangeData) {
8
+ if (Array.isArray(value)) {
9
+ this.min = value[0]
10
+ this.max = value[1] === undefined ? undefined : value[1]
11
+ } else {
12
+ this.min = value
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,30 @@
1
+ import { Coords, LocationData, LocationIcon } from '../api/location-data'
2
+
3
+ export class Location {
4
+ /**
5
+ * Key of the {@link Zone} that the location references.
6
+ */
7
+ readonly zoneKey?: string
8
+
9
+ /**
10
+ * In-game `/loc` coordinates of the location.
11
+ */
12
+ readonly coords?: Coords
13
+
14
+ /**
15
+ * The type of icon to use if the location appears on a map. (Typically the Vidiot map icon).
16
+ */
17
+ readonly icon?: LocationIcon
18
+
19
+ /**
20
+ * The text that should appear in the location icon. (Typically a number or symbol from the Vidiot map).
21
+ */
22
+ readonly iconText?: string
23
+
24
+ constructor(data: LocationData) {
25
+ this.zoneKey = data.zoneKey
26
+ this.coords = data.coords
27
+ this.icon = data.icon
28
+ this.iconText = data.iconText
29
+ }
30
+ }
@@ -0,0 +1,108 @@
1
+ import { MissionType } from '../api/mission-type'
2
+ import { MarkdownString } from '../api/markdown-string'
3
+ import { Link } from '../api/link'
4
+ import { MissionData } from '../api/mission-data'
5
+ import { Key } from './key'
6
+ import { MoralityList } from './morality-list'
7
+ import { coalesceToArray } from '../util/coalesce-to-array'
8
+ import { LevelRange } from './level-range'
9
+
10
+ export class Mission {
11
+ /**
12
+ * Unique key used to reference this mission.
13
+ *
14
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
15
+ */
16
+ readonly key: string
17
+
18
+ /**
19
+ * The name of the mission as it appears from the contact.
20
+ *
21
+ * The name may be different when viewed in Ouroboros as a Flashback.
22
+ */
23
+ readonly name: string
24
+
25
+ /**
26
+ * The type of mission... Story arc, task force, trial, etc.
27
+ */
28
+ readonly type: MissionType
29
+
30
+ /**
31
+ * The character moralities that may accept the mission.
32
+ */
33
+ readonly morality: MoralityList
34
+
35
+ /**
36
+ * The keys of any contacts that provide this mission.
37
+ */
38
+ readonly contactKeys?: string[]
39
+
40
+ /**
41
+ * The level range this mission is available for.
42
+ */
43
+ readonly levelRange?: LevelRange
44
+
45
+ /**
46
+ * Freeform notes or tips about the mission.
47
+ */
48
+ readonly notes?: MarkdownString
49
+
50
+ /**
51
+ * List of external links. Wiki, forums, etc.
52
+ */
53
+ readonly links: Link[]
54
+
55
+ /**
56
+ * If the mission is available in Ouroboros as a Flashback.
57
+ */
58
+ readonly flashback?: {
59
+
60
+ /**
61
+ * The id of the mission as seen in the Flashback menu, i.e. '14.01'.
62
+ */
63
+ readonly id: string
64
+
65
+ /**
66
+ * The level range this mission appears under as a Flashback.
67
+ */
68
+ readonly levelRange?: LevelRange
69
+
70
+ /**
71
+ * The name as it appears in the Flashback list.
72
+ */
73
+ readonly name: string
74
+
75
+ /**
76
+ * The character moralities that the mission will appear for in the Flashback list.
77
+ */
78
+ readonly morality: MoralityList
79
+
80
+ /**
81
+ * Freeform notes or tips about the Flashback version of the mission.
82
+ */
83
+ readonly notes?: MarkdownString
84
+ }
85
+
86
+ constructor(data: MissionData) {
87
+ this.key = new Key(data.key).value
88
+ this.name = data.name
89
+ this.type = data.type
90
+ this.morality = new MoralityList(coalesceToArray(data.morality))
91
+ this.contactKeys = coalesceToArray(data.contactKeys)
92
+ this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
93
+ this.notes = data.notes
94
+ this.links = data.links ?? []
95
+ this.flashback = createFlashback(data)
96
+ }
97
+ }
98
+
99
+ function createFlashback(data: MissionData): Mission['flashback'] {
100
+ if (!data.flashback) return undefined
101
+ return {
102
+ id: data.flashback.id,
103
+ levelRange: data.flashback.levelRange ? new LevelRange(data.flashback.levelRange) : undefined,
104
+ name: data.flashback.name ?? data.name,
105
+ morality: new MoralityList(coalesceToArray(data.flashback.morality ?? data.morality)),
106
+ notes: data.flashback.notes,
107
+ }
108
+ }
@@ -0,0 +1,99 @@
1
+ import { MORALITY, Morality, MoralityExtended } from '../api/morality'
2
+
3
+ export class MoralityList {
4
+ readonly #items: Set<Morality>
5
+
6
+ readonly hero: boolean
7
+ readonly vigilante: boolean
8
+ readonly villain: boolean
9
+ readonly rogue: boolean
10
+ readonly resistance: boolean
11
+ readonly loyalist: boolean
12
+
13
+ readonly primal: boolean
14
+ readonly praetorian: boolean
15
+ readonly heroic: boolean
16
+ readonly villainous: boolean
17
+ readonly paragonCityAccess: boolean
18
+ readonly rogueIslesAccess: boolean
19
+
20
+ readonly all: boolean
21
+
22
+ constructor(items?: MoralityExtended[]) {
23
+ const set = new Set(items ?? [...MORALITY])
24
+ this.hero = set.has('hero') || set.has('primal') || set.has('heroic') || set.has('paragon-city-access') || set.has('all')
25
+ this.vigilante = set.has('vigilante') || set.has('primal') || set.has('heroic') || set.has('paragon-city-access') || set.has('rogue-isles-access') || set.has('all')
26
+ this.villain = set.has('villain') || set.has('primal') || set.has('villainous') || set.has('rogue-isles-access') || set.has('all')
27
+ this.rogue = set.has('rogue') || set.has('primal') || set.has('villainous') || set.has('paragon-city-access') || set.has('rogue-isles-access') || set.has('all')
28
+ this.resistance = set.has('resistance') || set.has('praetorian') || set.has('all')
29
+ this.loyalist = set.has('loyalist') || set.has('praetorian') || set.has('all')
30
+
31
+ this.primal = this.hero && this.vigilante && this.villain && this.rogue
32
+ this.praetorian = this.loyalist && this.resistance
33
+ this.heroic = this.hero && this.vigilante
34
+ this.villainous = this.villain && this.rogue
35
+ this.paragonCityAccess = this.heroic && this.rogue
36
+ this.rogueIslesAccess = this.villainous && this.vigilante
37
+
38
+ this.all = this.primal && this.praetorian
39
+
40
+ this.#items = new Set()
41
+ if (this.hero) this.#items.add('hero')
42
+ if (this.vigilante) this.#items.add('vigilante')
43
+ if (this.villain) this.#items.add('villain')
44
+ if (this.rogue) this.#items.add('rogue')
45
+ if (this.resistance) this.#items.add('resistance')
46
+ if (this.loyalist) this.#items.add('loyalist')
47
+ }
48
+
49
+ get items(): Morality[] {
50
+ return [...this.#items]
51
+ }
52
+
53
+ has(morality?: MoralityExtended): boolean {
54
+ switch (morality) {
55
+ case 'hero': {
56
+ return this.hero
57
+ }
58
+ case 'vigilante': {
59
+ return this.vigilante
60
+ }
61
+ case 'villain': {
62
+ return this.villain
63
+ }
64
+ case 'rogue': {
65
+ return this.rogue
66
+ }
67
+ case 'resistance': {
68
+ return this.resistance
69
+ }
70
+ case 'loyalist': {
71
+ return this.loyalist
72
+ }
73
+ case 'primal' : {
74
+ return this.primal
75
+ }
76
+ case 'praetorian': {
77
+ return this.praetorian
78
+ }
79
+ case 'heroic': {
80
+ return this.hero
81
+ }
82
+ case 'paragon-city-access': {
83
+ return this.paragonCityAccess
84
+ }
85
+ case 'rogue-isles-access': {
86
+ return this.rogueIslesAccess
87
+ }
88
+ case 'villainous': {
89
+ return this.villainous
90
+ }
91
+ case 'all': {
92
+ return this.all
93
+ }
94
+ default: {
95
+ return false
96
+ }
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,11 @@
1
+ export interface Paged<T> {
2
+ items: T[]
3
+
4
+ matchedItemCount: number
5
+ totalItemCount: number
6
+
7
+ pageSize?: number
8
+ pageIndex: number
9
+ pageNumber: number
10
+ totalPageCount: number
11
+ }
@@ -0,0 +1,10 @@
1
+ import { SetTitleData } from '../api/set-title-data'
2
+
3
+ export class SetTitleIds {
4
+ readonly primal: number
5
+ readonly praetorian?: number
6
+
7
+ constructor(value: SetTitleData) {
8
+ [this.primal, this.praetorian] = value
9
+ }
10
+ }
@@ -0,0 +1,84 @@
1
+ import { VariantData } from '../api/variant-data'
2
+ import { compareSex } from '../api/sex'
3
+ import { compareAlignment } from '../api/alignment'
4
+ import { VariantContext } from '../api/variant-context'
5
+ import { MoralityMap } from '../api/morality'
6
+
7
+ export class Variants<T> {
8
+ readonly #sortedValues: VariantData<T>[] = []
9
+
10
+ /**
11
+ * Create a variant set from either a list of categorized values, or a single value when there are no variants.
12
+ * @param value List of variants, or a single value.
13
+ */
14
+ constructor(value: VariantData<T>[] | T) {
15
+ if (Array.isArray(value)) {
16
+ this.#sortedValues = value.toSorted()
17
+ this.#sortedValues.sort((a, b) => this.#compareVariants(a, b))
18
+ } else {
19
+ this.#sortedValues = [{ value }]
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Get a variant by context
25
+ * @param context The context
26
+ */
27
+ getVariant(context?: VariantContext): VariantData<T> | undefined {
28
+ const alignment = context?.morality ? MoralityMap[context.morality] : undefined
29
+ const sex = context?.sex
30
+
31
+ for (let index = this.#sortedValues.length; index--;) {
32
+ const entry = this.#sortedValues[index]
33
+ if ((entry.alignment === undefined || entry.alignment === alignment)
34
+ && (entry.sex === undefined || entry.sex === sex)
35
+ ) return entry
36
+ }
37
+
38
+ return this.default
39
+ }
40
+
41
+ /**
42
+ * Get a value by variant context
43
+ * @param context The context
44
+ */
45
+ getValue(context?: VariantContext): T | undefined {
46
+ return this.getVariant(context)?.value
47
+ }
48
+
49
+ /**
50
+ * Get the default value for this list of variants, the value with the highest priority and lowest specificity.
51
+ */
52
+ get default(): VariantData<T> | undefined {
53
+ return this.#sortedValues[0]
54
+ }
55
+
56
+ /**
57
+ * Get the list of variants sorted in canonical order (alignment then sex, low to high specificity).
58
+ */
59
+ get canonical(): VariantData<T>[] {
60
+ return this.#sortedValues
61
+ }
62
+
63
+ /**
64
+ * Create a joined string from the variant values in canonical order.
65
+ * @param separator Separator to use. Default is ' / '
66
+ */
67
+ toString(separator: string): string {
68
+ return this.canonical.map(x => x.value).join(separator)
69
+ }
70
+
71
+ #compareVariants(a: VariantData<T>, b: VariantData<T>): number {
72
+ const aSpecificity = (a.alignment ? 2 : 0) + (a.sex ? 1 : 0)
73
+ const bSpecificity = (b.alignment ? 2 : 0) + (b.sex ? 1 : 0)
74
+ if (aSpecificity !== bSpecificity) return aSpecificity - bSpecificity // Order first by least-specific
75
+
76
+ const alignmentComparison = compareAlignment(a.alignment, b.alignment) // Next by alignment
77
+ if (alignmentComparison !== 0) return alignmentComparison
78
+
79
+ const sexComparison = compareSex(a.sex, b.sex) // Last by sex
80
+ if (sexComparison !== 0) return sexComparison
81
+
82
+ return String(a.value).localeCompare(String(b.value))
83
+ }
84
+ }
@@ -0,0 +1,57 @@
1
+ import { Link } from '../api/link'
2
+ import { ZoneData } from '../api/zone-data'
3
+ import { Key } from './key'
4
+ import { MoralityList } from './morality-list'
5
+ import { coalesceToArray } from '../util/coalesce-to-array'
6
+ import { ZoneType } from '../api/zone-type'
7
+ import { MarkdownString } from '../api/markdown-string'
8
+ import { LevelRange } from './level-range'
9
+
10
+ export class Zone {
11
+ /**
12
+ * Unique key used to reference this zone.
13
+ *
14
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
15
+ */
16
+ readonly key: string
17
+
18
+ /**
19
+ * The name of the zone as it appears in-game.
20
+ */
21
+ readonly name: string
22
+
23
+ /**
24
+ * The type of zone.
25
+ */
26
+ readonly type: ZoneType
27
+
28
+ /**
29
+ * The character moralities that this zone is accessible by.
30
+ */
31
+ readonly morality: MoralityList
32
+
33
+ /**
34
+ * The level range this zone is recommended for.
35
+ */
36
+ readonly levelRange?: LevelRange
37
+
38
+ /**
39
+ * Freeform notes or tips about the zone.
40
+ */
41
+ readonly notes?: MarkdownString
42
+
43
+ /**
44
+ * List of external links. Wiki, forums, etc.
45
+ */
46
+ readonly links: Link[]
47
+
48
+ constructor(data: ZoneData) {
49
+ this.key = new Key(data.key).value
50
+ this.name = data.name
51
+ this.type = data.type
52
+ this.morality = new MoralityList(coalesceToArray(data.morality))
53
+ this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
54
+ this.notes = data.notes
55
+ this.links = data.links ?? []
56
+ }
57
+ }
package/src/main/index.ts CHANGED
@@ -1,33 +1,48 @@
1
1
  // API
2
2
  export * from './api/alignment'
3
- export * from './api/alternate-data'
4
3
  export * from './api/archetype-data'
5
4
  export * from './api/badge-data'
6
- export * from './api/badge-partial-data'
7
- export * from './api/badge-partial-type'
5
+ export * from './api/badge-requirement-data'
6
+ export * from './api/badge-requirement-type'
8
7
  export * from './api/badge-type'
9
- export * from './api/change'
8
+ export * from './api/bundle-data'
9
+ export * from './api/bundle-header-data'
10
+ export * from './api/contact-data'
10
11
  export * from './api/enhancement-category'
11
- export * from './api/game-map-data'
12
12
  export * from './api/link'
13
- export * from './api/plaque-type'
14
- export * from './api/server-group-data'
13
+ export * from './api/location-data'
14
+ export * from './api/level-range-data'
15
+ export * from './api/markdown-string'
16
+ export * from './api/mission-data'
17
+ export * from './api/mission-flashback-data'
18
+ export * from './api/mission-type'
19
+ export * from './api/morality'
20
+ export * from './api/set-title-data'
15
21
  export * from './api/sex'
16
- export * from './api/vidiot-map-data'
17
- export * from './api/vidiot-map-point-of-interest-data'
22
+ export * from './api/variant-context'
23
+ export * from './api/variant-data'
24
+ export * from './api/zone-data'
25
+ export * from './api/zone-type'
18
26
 
19
27
  // DB
28
+ export * from './db/alignment-list'
20
29
  export * from './db/archetype'
21
30
  export * from './db/badge'
22
- export * from './db/badge-partial'
31
+ export * from './db/badge-index'
32
+ export * from './db/badge-requirement'
33
+ export * from './db/badge-search-options'
34
+ export * from './db/bundle-header'
23
35
  export * from './db/coh-content-database'
24
- export * from './db/game-map'
36
+ export * from './db/contact'
25
37
  export * from './db/key'
26
- export * from './db/server-group'
27
- export * from './db/vidiot-map'
28
- export * from './db/vidiot-map-point-of-interest'
38
+ export * from './db/location'
39
+ export * from './db/level-range'
40
+ export * from './db/mission'
41
+ export * from './db/morality-list'
42
+ export * from './db/paged'
43
+ export * from './db/set-title-ids'
44
+ export * from './db/variants'
45
+ export * from './db/zone'
29
46
 
30
- // ROOT
31
- export { CHANGELOG } from './changelog'
32
-
33
- export * from './util'
47
+ // UTILS
48
+ export * from './util/links'
@@ -0,0 +1,13 @@
1
+ /**
2
+ * For fields that accept either an array of values or a single value, coalesces the value to an array.
3
+ *
4
+ * Arrays are returned as-is.
5
+ * Single values are returned as a single-value array.
6
+ * Undefined values are returned as undefined.
7
+ *
8
+ * @param value The value to coalesce.
9
+ */
10
+ export function coalesceToArray<T>(value?: T | T[]): T[] | undefined {
11
+ if (!value) return undefined
12
+ return Array.isArray(value) ? value as T[] : [value]
13
+ }
@@ -0,0 +1,104 @@
1
+ import { BadgeData } from '../api/badge-data'
2
+ import { Badge } from '../db/badge'
3
+ import { ZoneData } from '../api/zone-data'
4
+ import { Zone } from '../db/zone'
5
+ import { Contact } from '../db/contact'
6
+ import { ContactData } from '../api/contact-data'
7
+ import { Mission } from '../db/mission'
8
+ import { MissionData } from '../api/mission-data'
9
+
10
+ /**
11
+ * Returns the URI of the given badge that can be used in {@link MarkdownString} fields.
12
+ *
13
+ * URI format: `badge://<key>`
14
+ *
15
+ * @param target The badge or badge key to target.
16
+ */
17
+ export function badgeUri(target: string | Badge | BadgeData): string {
18
+ const key = typeof target === 'string' ? target : target.key
19
+ return `badge://${key}`
20
+ }
21
+
22
+ /**
23
+ * Returns a {@link MarkdownString} link to the given badge.
24
+ *
25
+ * Link format: `[<key>](badge://<key>)`
26
+ *
27
+ * @param target The {@link Badge} or badge key to target.
28
+ */
29
+ export function badgeLink(target: string | Badge | BadgeData): string {
30
+ const key = typeof target === 'string' ? target : target.key
31
+ return `[${key}](${badgeUri(target)})`
32
+ }
33
+
34
+ /**
35
+ * Returns the URI of the given contact that can be used in {@link MarkdownString} fields.
36
+ *
37
+ * URI format: `contact://<key>`
38
+ *
39
+ * @param target The {@link Contact} or contact key to target.
40
+ */
41
+ export function contactUri(target: string | Contact | ContactData): string {
42
+ const key = typeof target === 'string' ? target : target.key
43
+ return `contact://${key}`
44
+ }
45
+
46
+ /**
47
+ * Returns a {@link MarkdownString} link to the given contact.
48
+ *
49
+ * Link format: `[<key>](contact://<key>)`
50
+ *
51
+ * @param target The {@link Contact} or contact key to target.
52
+ */
53
+ export function contactLink(target: string | Contact | ContactData): string {
54
+ const key = typeof target === 'string' ? target : target.key
55
+ return `[${key}](${contactUri(target)})`
56
+ }
57
+
58
+ /**
59
+ * Returns the URI of the given mission that can be used in {@link MarkdownString} fields.
60
+ *
61
+ * URI format: `mission://<key>`
62
+ *
63
+ * @param target The {@link Mission} or mission key to target.
64
+ */
65
+ export function missionUri(target: string | Mission | MissionData): string {
66
+ const key = typeof target === 'string' ? target : target.key
67
+ return `mission://${key}`
68
+ }
69
+
70
+ /**
71
+ * Returns a {@link MarkdownString} link to the given mission.
72
+ *
73
+ * Link format: `[<key>](mission://<key>)`
74
+ *
75
+ * @param target The {@link Mission} or mission key to target.
76
+ */
77
+ export function missionLink(target: string | Mission | MissionData): string {
78
+ const key = typeof target === 'string' ? target : target.key
79
+ return `[${key}](${missionUri(target)})`
80
+ }
81
+
82
+ /**
83
+ * Returns the URI of the given zone that can be used in {@link MarkdownString} fields.
84
+ *
85
+ * URI format: `zone://<key>`
86
+ *
87
+ * @param target The {@link Zone} or zone key to target.
88
+ */
89
+ export function zoneUri(target: string | Zone | ZoneData): string {
90
+ const key = typeof target === 'string' ? target : target.key
91
+ return `zone://${key}`
92
+ }
93
+
94
+ /**
95
+ * Returns a {@link MarkdownString} link to the given zone.
96
+ *
97
+ * Link format: `[<key>](zone://<key>)`
98
+ *
99
+ * @param target The {@link Zone} or zone key to target.
100
+ */
101
+ export function zoneLink(target: string | Zone | ZoneData): string {
102
+ const key = typeof target === 'string' ? target : target.key
103
+ return `[${key}](${zoneUri(target)})`
104
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Converts an iso string to a Date object, throwing an error if the iso string is invalid.
3
+ * @param iso ISO-8601 Date string
4
+ */
5
+ export function toDate(iso: string): Date {
6
+ const date = new Date(iso)
7
+ if (!date || Number.isNaN(date.getTime())) throw new Error(`Invalid date format: [${iso}]`)
8
+ return date
9
+ }