5etools-utils 0.10.13 → 0.10.15

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.
@@ -1,25 +1,123 @@
1
- // Adapted from 5etools `clean-jsons.js`
2
- // ===
3
-
4
1
  import * as fs from "fs";
5
2
  import * as Uf from "./UtilFs.js";
6
3
  import Um from "./UtilMisc.js";
7
4
  import {getCleanJson} from "./UtilClean.js";
5
+ import MiscUtil from "./UtilMisc.js";
8
6
 
9
- class BrewCleaner {
10
- static _IS_FAIL_SLOW = !!process.env.FAIL_SLOW;
11
-
7
+ class _BrewFileCleaner {
12
8
  static _RUN_TIMESTAMP = Math.floor(Date.now() / 1000);
13
9
  static _MAX_TIMESTAMP = 9999999999;
14
10
 
15
- static _CONTENT_KEY_BLOCKLIST = new Set(["$schema", "_meta", "siteVersion"]);
11
+ static cleanFile ({file, contents}) {
12
+ // Ensure _meta is at the top of the file
13
+ const tmp = {$schema: contents.$schema, _meta: contents._meta};
14
+ delete contents.$schema;
15
+ delete contents._meta;
16
+ Object.assign(tmp, contents);
17
+ contents = tmp;
18
+
19
+ if (contents._meta.dateAdded == null) {
20
+ Um.warn(`TIMESTAMPS`, `\tFile "${file}" did not have "dateAdded"! Adding one...`);
21
+ contents._meta.dateAdded = this._RUN_TIMESTAMP;
22
+ } else if (contents._meta.dateAdded > this._MAX_TIMESTAMP) {
23
+ Um.warn(`TIMESTAMPS`, `\tFile "${file}" had a "dateAdded" in milliseconds! Converting to seconds...`);
24
+ contents._meta.dateAdded = Math.round(contents._meta.dateAdded / 1000);
25
+ }
16
26
 
17
- static _RE_INVALID_WINDOWS_CHARS = /[<>:"/\\|?*]/;
27
+ if (contents._meta.dateLastModified == null) {
28
+ Um.warn(`TIMESTAMPS`, `\tFile "${file}" did not have "dateLastModified"! Adding one...`);
29
+ contents._meta.dateLastModified = this._RUN_TIMESTAMP;
30
+ } else if (contents._meta.dateLastModified > this._MAX_TIMESTAMP) {
31
+ Um.warn(`TIMESTAMPS`, `\tFile "${file}" had a "dateLastModified" in milliseconds! Converting to seconds...`);
32
+ contents._meta.dateLastModified = Math.round(contents._meta.dateLastModified / 1000);
33
+ }
18
34
 
35
+ (contents._meta.sources || []).forEach(source => {
36
+ if (source.version != null) return;
37
+ Um.warn(`VERSION`, `\tFile "${file}" source "${source.json}" did not have "version"! Adding one...`);
38
+ source.version = "unknown";
39
+ });
40
+
41
+ return contents;
42
+ }
43
+ }
44
+
45
+ class _BrewFileTester {
19
46
  static _RE_VALID_SOURCE_CHARS = /^[-a-zA-Z0-9 :&+!]+$/;
20
47
 
21
48
  static _ALL_SOURCES_JSON_LOWER = new Set();
22
49
 
50
+ static _CONTENT_KEY_BLOCKLIST = new Set(["$schema", "_meta", "siteVersion"]);
51
+
52
+ static _testFile_sources ({ALL_ERRORS, file, contents}) {
53
+ const docSourcesJson = contents._meta.sources.map(src => src.json);
54
+ const duplicateSourcesJson = docSourcesJson.filter(src => this._ALL_SOURCES_JSON_LOWER.has(src.toLowerCase()));
55
+ if (duplicateSourcesJson.length) {
56
+ ALL_ERRORS.push(`${file} :: "json" source${duplicateSourcesJson.length === 1 ? "" : "s"} exist in other documents; sources were: ${duplicateSourcesJson.map(src => `"${src}"`).join(", ")}`);
57
+ }
58
+ docSourcesJson.forEach(src => this._ALL_SOURCES_JSON_LOWER.add(src.toLowerCase()));
59
+
60
+ const invalidSourceChars = docSourcesJson.filter(src => !this._RE_VALID_SOURCE_CHARS.test(src));
61
+ if (invalidSourceChars.length) {
62
+ ALL_ERRORS.push(`${file} :: "json" source${invalidSourceChars.length === 1 ? "" : "s"} contained invalid characters (must match ${this._RE_VALID_SOURCE_CHARS.toString()}): ${invalidSourceChars.map(src => `"${src}"`).join(", ")}`);
63
+ }
64
+
65
+ const docSourcesJsonSet = new Set(docSourcesJson);
66
+
67
+ Object.keys(contents)
68
+ .filter(k => !this._CONTENT_KEY_BLOCKLIST.has(k))
69
+ .forEach(k => {
70
+ const data = contents[k];
71
+
72
+ if (!(data instanceof Array) || !data.forEach) throw new Error(`File "${k}" data was not an array!`);
73
+
74
+ if (!data.length) throw new Error(`File "${k}" array is empty!`);
75
+
76
+ data.forEach(it => {
77
+ const source = it.source || (it.inherits ? it.inherits.source : null);
78
+ if (!source) return ALL_ERRORS.push(`${file} :: ${k} :: "${it.name || it.id}" had no source!`);
79
+ if (!docSourcesJsonSet.has(source)) return ALL_ERRORS.push(`${file} :: ${k} :: "${it.name || it.id}" source "${source}" was not in _meta`);
80
+ });
81
+ });
82
+ }
83
+
84
+ static _PROP_PUBLICATION_PAIRS = [
85
+ {
86
+ propContents: "adventure",
87
+ propData: "adventureData",
88
+ },
89
+ {
90
+ propContents: "book",
91
+ propData: "bookData",
92
+ },
93
+ ];
94
+
95
+ static _testFile_publicationTuples ({ALL_ERRORS, file, contents}) {
96
+ this._PROP_PUBLICATION_PAIRS
97
+ .forEach(({propContents, propData}) => {
98
+ const idsContents = new Set((contents[propContents] || []).map(it => it.id));
99
+ const idsData = new Set((contents[propData] || []).map(it => it.id));
100
+
101
+ if (MiscUtil.setEq(idsContents, idsData)) return;
102
+
103
+ const lstContents = [...idsContents].sort((a, b) => a.localeCompare(b, {sensitivity: "base"}));
104
+ const lstData = [...idsData].sort((a, b) => a.localeCompare(b, {sensitivity: "base"}));
105
+
106
+ ALL_ERRORS.push(`${file} :: "${propContents}"/"${propData}" :: contents ID list "${lstContents.join(", ")}" did not equal data ID list "${lstData.join(", ")}"`);
107
+ });
108
+ }
109
+
110
+ static testFile ({ALL_ERRORS, file, contents}) {
111
+ this._testFile_sources({ALL_ERRORS, file, contents});
112
+ this._testFile_publicationTuples({ALL_ERRORS, file, contents});
113
+ }
114
+ }
115
+
116
+ export class BrewCleaner {
117
+ static _IS_FAIL_SLOW = !!process.env.FAIL_SLOW;
118
+
119
+ static _RE_INVALID_WINDOWS_CHARS = /[<>:"/\\|?*]/;
120
+
23
121
  static _cleanFolder (folder) {
24
122
  const ALL_ERRORS = [];
25
123
 
@@ -37,68 +135,8 @@ class BrewCleaner {
37
135
  if (!this._IS_FAIL_SLOW) break;
38
136
  }
39
137
 
40
- // region clean
41
- // Ensure _meta is at the top of the file
42
- const tmp = {$schema: contents.$schema, _meta: contents._meta};
43
- delete contents.$schema;
44
- delete contents._meta;
45
- Object.assign(tmp, contents);
46
- contents = tmp;
47
-
48
- if (contents._meta.dateAdded == null) {
49
- Um.warn(`TIMESTAMPS`, `\tFile "${file}" did not have "dateAdded"! Adding one...`);
50
- contents._meta.dateAdded = this._RUN_TIMESTAMP;
51
- } else if (contents._meta.dateAdded > this._MAX_TIMESTAMP) {
52
- Um.warn(`TIMESTAMPS`, `\tFile "${file}" had a "dateAdded" in milliseconds! Converting to seconds...`);
53
- contents._meta.dateAdded = Math.round(contents._meta.dateAdded / 1000);
54
- }
55
-
56
- if (contents._meta.dateLastModified == null) {
57
- Um.warn(`TIMESTAMPS`, `\tFile "${file}" did not have "dateLastModified"! Adding one...`);
58
- contents._meta.dateLastModified = this._RUN_TIMESTAMP;
59
- } else if (contents._meta.dateLastModified > this._MAX_TIMESTAMP) {
60
- Um.warn(`TIMESTAMPS`, `\tFile "${file}" had a "dateLastModified" in milliseconds! Converting to seconds...`);
61
- contents._meta.dateLastModified = Math.round(contents._meta.dateLastModified / 1000);
62
- }
63
-
64
- (contents._meta.sources || []).forEach(source => {
65
- if (source.version != null) return;
66
- Um.warn(`VERSION`, `\tFile "${file}" source "${source.json}" did not have "version"! Adding one...`);
67
- source.version = "unknown";
68
- });
69
- // endregion
70
-
71
- // region test
72
- const docSourcesJson = contents._meta.sources.map(src => src.json);
73
- const duplicateSourcesJson = docSourcesJson.filter(src => this._ALL_SOURCES_JSON_LOWER.has(src.toLowerCase()));
74
- if (duplicateSourcesJson.length) {
75
- ALL_ERRORS.push(`${file} :: "json" source${duplicateSourcesJson.length === 1 ? "" : "s"} exist in other documents; sources were: ${duplicateSourcesJson.map(src => `"${src}"`).join(", ")}`);
76
- }
77
- docSourcesJson.forEach(src => this._ALL_SOURCES_JSON_LOWER.add(src.toLowerCase()));
78
-
79
- const invalidSourceChars = docSourcesJson.filter(src => !this._RE_VALID_SOURCE_CHARS.test(src));
80
- if (invalidSourceChars.length) {
81
- ALL_ERRORS.push(`${file} :: "json" source${invalidSourceChars.length === 1 ? "" : "s"} contained invalid characters (must match ${this._RE_VALID_SOURCE_CHARS.toString()}): ${invalidSourceChars.map(src => `"${src}"`).join(", ")}`);
82
- }
83
-
84
- const docSourcesJsonSet = new Set(docSourcesJson);
85
-
86
- Object.keys(contents)
87
- .filter(k => !this._CONTENT_KEY_BLOCKLIST.has(k))
88
- .forEach(k => {
89
- const data = contents[k];
90
-
91
- if (!(data instanceof Array) || !data.forEach) throw new Error(`File "${k}" data was not an array!`);
92
-
93
- if (!data.length) throw new Error(`File "${k}" array is empty!`);
94
-
95
- data.forEach(it => {
96
- const source = it.source || (it.inherits ? it.inherits.source : null);
97
- if (!source) return ALL_ERRORS.push(`${file} :: ${k} :: "${it.name || it.id}" had no source!`);
98
- if (!docSourcesJsonSet.has(source)) return ALL_ERRORS.push(`${file} :: ${k} :: "${it.name || it.id}" source "${source}" was not in _meta`);
99
- });
100
- });
101
- // endregion
138
+ contents = _BrewFileCleaner.cleanFile({file, contents});
139
+ _BrewFileTester.testFile({ALL_ERRORS, file, contents});
102
140
 
103
141
  if (!this._IS_FAIL_SLOW && ALL_ERRORS.length) break;
104
142
 
@@ -126,5 +164,3 @@ class BrewCleaner {
126
164
  Um.info(`CLEANER`, `Cleaning complete. Cleaned ${totalFiles} file${totalFiles === 1 ? "" : "s"}.`);
127
165
  }
128
166
  }
129
-
130
- export {BrewCleaner};
package/lib/UtilMisc.js CHANGED
@@ -1,4 +1,4 @@
1
- class MiscUtil {
1
+ export default class MiscUtil {
2
2
  /* -------------------------------------------- */
3
3
 
4
4
  // region Logging
@@ -36,6 +36,10 @@ class MiscUtil {
36
36
  }
37
37
 
38
38
  /* -------------------------------------------- */
39
- }
40
39
 
41
- export default MiscUtil;
40
+ static setEq (a, b) {
41
+ if (a.size !== b.size) return false;
42
+ for (const it of a) if (!b.has(it)) return false;
43
+ return true;
44
+ }
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "5etools-utils",
3
- "version": "0.10.13",
3
+ "version": "0.10.15",
4
4
  "description": "Shared utilities for the 5etools ecosystem.",
5
5
  "type": "module",
6
6
  "main": "lib/Api.js",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -181,17 +181,17 @@
181
181
  "cha": {
182
182
  "type": "integer"
183
183
  },
184
+ "resist": {
185
+ "$ref": "util.json#/$defs/damageResistArray"
186
+ },
184
187
  "conditionImmune": {
185
- "type": "array",
186
- "items": {
187
- "$ref": "entry.json#/$defs/dataCondImmune"
188
- }
188
+ "$ref": "util.json#/$defs/conditionImmunityArray"
189
189
  },
190
190
  "immune": {
191
- "type": "array",
192
- "items": {
193
- "$ref": "entry.json#/$defs/dataDamImmune"
194
- }
191
+ "$ref": "util.json#/$defs/damageImmunityArray"
192
+ },
193
+ "vulnerable": {
194
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
195
195
  },
196
196
  "actionThresholds": {
197
197
  "type": "object",
@@ -750,17 +750,17 @@
750
750
  "mt"
751
751
  ]
752
752
  },
753
+ "resist": {
754
+ "$ref": "util.json#/$defs/damageResistArray"
755
+ },
753
756
  "conditionImmune": {
754
- "type": "array",
755
- "items": {
756
- "$ref": "entry.json#/$defs/dataCondImmune"
757
- }
757
+ "$ref": "util.json#/$defs/conditionImmunityArray"
758
758
  },
759
759
  "immune": {
760
- "type": "array",
761
- "items": {
762
- "$ref": "entry.json#/$defs/dataDamImmune"
763
- }
760
+ "$ref": "util.json#/$defs/damageImmunityArray"
761
+ },
762
+ "vulnerable": {
763
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
764
764
  },
765
765
  "trait": {
766
766
  "type": "array",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -181,17 +181,17 @@
181
181
  "cha": {
182
182
  "type": "integer"
183
183
  },
184
+ "resist": {
185
+ "$ref": "util.json#/$defs/damageResistArray"
186
+ },
184
187
  "conditionImmune": {
185
- "type": "array",
186
- "items": {
187
- "$ref": "entry.json#/$defs/dataCondImmune"
188
- }
188
+ "$ref": "util.json#/$defs/conditionImmunityArray"
189
189
  },
190
190
  "immune": {
191
- "type": "array",
192
- "items": {
193
- "$ref": "entry.json#/$defs/dataDamImmune"
194
- }
191
+ "$ref": "util.json#/$defs/damageImmunityArray"
192
+ },
193
+ "vulnerable": {
194
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
195
195
  },
196
196
  "actionThresholds": {
197
197
  "type": "object",
@@ -750,17 +750,17 @@
750
750
  "mt"
751
751
  ]
752
752
  },
753
+ "resist": {
754
+ "$ref": "util.json#/$defs/damageResistArray"
755
+ },
753
756
  "conditionImmune": {
754
- "type": "array",
755
- "items": {
756
- "$ref": "entry.json#/$defs/dataCondImmune"
757
- }
757
+ "$ref": "util.json#/$defs/conditionImmunityArray"
758
758
  },
759
759
  "immune": {
760
- "type": "array",
761
- "items": {
762
- "$ref": "entry.json#/$defs/dataDamImmune"
763
- }
760
+ "$ref": "util.json#/$defs/damageImmunityArray"
761
+ },
762
+ "vulnerable": {
763
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
764
764
  },
765
765
  "trait": {
766
766
  "type": "array",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -182,17 +182,17 @@
182
182
  "cha": {
183
183
  "type": "integer"
184
184
  },
185
+ "resist": {
186
+ "$ref": "util.json#/$defs/damageResistArray"
187
+ },
185
188
  "conditionImmune": {
186
- "type": "array",
187
- "items": {
188
- "$ref": "entry.json#/$defs/dataCondImmune"
189
- }
189
+ "$ref": "util.json#/$defs/conditionImmunityArray"
190
190
  },
191
191
  "immune": {
192
- "type": "array",
193
- "items": {
194
- "$ref": "entry.json#/$defs/dataDamImmune"
195
- }
192
+ "$ref": "util.json#/$defs/damageImmunityArray"
193
+ },
194
+ "vulnerable": {
195
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
196
196
  },
197
197
  "actionThresholds": {
198
198
  "type": "object",
@@ -701,17 +701,17 @@
701
701
  "mt"
702
702
  ]
703
703
  },
704
+ "resist": {
705
+ "$ref": "util.json#/$defs/damageResistArray"
706
+ },
704
707
  "conditionImmune": {
705
- "type": "array",
706
- "items": {
707
- "$ref": "entry.json#/$defs/dataCondImmune"
708
- }
708
+ "$ref": "util.json#/$defs/conditionImmunityArray"
709
709
  },
710
710
  "immune": {
711
- "type": "array",
712
- "items": {
713
- "$ref": "entry.json#/$defs/dataDamImmune"
714
- }
711
+ "$ref": "util.json#/$defs/damageImmunityArray"
712
+ },
713
+ "vulnerable": {
714
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
715
715
  },
716
716
  "trait": {
717
717
  "type": "array",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -182,17 +182,17 @@
182
182
  "cha": {
183
183
  "type": "integer"
184
184
  },
185
+ "resist": {
186
+ "$ref": "util.json#/$defs/damageResistArray"
187
+ },
185
188
  "conditionImmune": {
186
- "type": "array",
187
- "items": {
188
- "$ref": "entry.json#/$defs/dataCondImmune"
189
- }
189
+ "$ref": "util.json#/$defs/conditionImmunityArray"
190
190
  },
191
191
  "immune": {
192
- "type": "array",
193
- "items": {
194
- "$ref": "entry.json#/$defs/dataDamImmune"
195
- }
192
+ "$ref": "util.json#/$defs/damageImmunityArray"
193
+ },
194
+ "vulnerable": {
195
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
196
196
  },
197
197
  "actionThresholds": {
198
198
  "type": "object",
@@ -701,17 +701,17 @@
701
701
  "mt"
702
702
  ]
703
703
  },
704
+ "resist": {
705
+ "$ref": "util.json#/$defs/damageResistArray"
706
+ },
704
707
  "conditionImmune": {
705
- "type": "array",
706
- "items": {
707
- "$ref": "entry.json#/$defs/dataCondImmune"
708
- }
708
+ "$ref": "util.json#/$defs/conditionImmunityArray"
709
709
  },
710
710
  "immune": {
711
- "type": "array",
712
- "items": {
713
- "$ref": "entry.json#/$defs/dataDamImmune"
714
- }
711
+ "$ref": "util.json#/$defs/damageImmunityArray"
712
+ },
713
+ "vulnerable": {
714
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
715
715
  },
716
716
  "trait": {
717
717
  "type": "array",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -182,17 +182,17 @@
182
182
  "cha": {
183
183
  "type": "integer"
184
184
  },
185
+ "resist": {
186
+ "$ref": "util.json#/$defs/damageResistArray"
187
+ },
185
188
  "conditionImmune": {
186
- "type": "array",
187
- "items": {
188
- "$ref": "entry.json#/$defs/dataCondImmune"
189
- }
189
+ "$ref": "util.json#/$defs/conditionImmunityArray"
190
190
  },
191
191
  "immune": {
192
- "type": "array",
193
- "items": {
194
- "$ref": "entry.json#/$defs/dataDamImmune"
195
- }
192
+ "$ref": "util.json#/$defs/damageImmunityArray"
193
+ },
194
+ "vulnerable": {
195
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
196
196
  },
197
197
  "actionThresholds": {
198
198
  "type": "object",
@@ -719,17 +719,17 @@
719
719
  "mt"
720
720
  ]
721
721
  },
722
+ "resist": {
723
+ "$ref": "util.json#/$defs/damageResistArray"
724
+ },
722
725
  "conditionImmune": {
723
- "type": "array",
724
- "items": {
725
- "$ref": "entry.json#/$defs/dataCondImmune"
726
- }
726
+ "$ref": "util.json#/$defs/conditionImmunityArray"
727
727
  },
728
728
  "immune": {
729
- "type": "array",
730
- "items": {
731
- "$ref": "entry.json#/$defs/dataDamImmune"
732
- }
729
+ "$ref": "util.json#/$defs/damageImmunityArray"
730
+ },
731
+ "vulnerable": {
732
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
733
733
  },
734
734
  "trait": {
735
735
  "type": "array",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "vehicles.json",
4
- "version": "1.7.5",
4
+ "version": "1.7.9",
5
5
  "type": "object",
6
6
  "$defs": {
7
7
  "vehicleTerrain": {
@@ -182,17 +182,17 @@
182
182
  "cha": {
183
183
  "type": "integer"
184
184
  },
185
+ "resist": {
186
+ "$ref": "util.json#/$defs/damageResistArray"
187
+ },
185
188
  "conditionImmune": {
186
- "type": "array",
187
- "items": {
188
- "$ref": "entry.json#/$defs/dataCondImmune"
189
- }
189
+ "$ref": "util.json#/$defs/conditionImmunityArray"
190
190
  },
191
191
  "immune": {
192
- "type": "array",
193
- "items": {
194
- "$ref": "entry.json#/$defs/dataDamImmune"
195
- }
192
+ "$ref": "util.json#/$defs/damageImmunityArray"
193
+ },
194
+ "vulnerable": {
195
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
196
196
  },
197
197
  "actionThresholds": {
198
198
  "type": "object",
@@ -719,17 +719,17 @@
719
719
  "mt"
720
720
  ]
721
721
  },
722
+ "resist": {
723
+ "$ref": "util.json#/$defs/damageResistArray"
724
+ },
722
725
  "conditionImmune": {
723
- "type": "array",
724
- "items": {
725
- "$ref": "entry.json#/$defs/dataCondImmune"
726
- }
726
+ "$ref": "util.json#/$defs/conditionImmunityArray"
727
727
  },
728
728
  "immune": {
729
- "type": "array",
730
- "items": {
731
- "$ref": "entry.json#/$defs/dataDamImmune"
732
- }
729
+ "$ref": "util.json#/$defs/damageImmunityArray"
730
+ },
731
+ "vulnerable": {
732
+ "$ref": "util.json#/$defs/damageVulnerabilityArray"
733
733
  },
734
734
  "trait": {
735
735
  "type": "array",