5etools-utils 0.4.11 → 0.5.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/lib/Api.js +7 -0
- package/lib/ObjectWalker.js +83 -0
- package/lib/TestData.js +188 -0
- package/package.json +1 -1
- package/schema/brew/homebrew.json +4 -1
- package/schema/brew/makebrew-creature.json +29 -1
- package/schema/brew-fast/homebrew.json +4 -1
- package/schema/brew-fast/makebrew-creature.json +29 -1
- package/schema/site/homebrew.json +4 -1
- package/schema/site/makebrew-creature.json +28 -1
- package/schema/site-fast/homebrew.json +4 -1
- package/schema/site-fast/makebrew-creature.json +28 -1
package/lib/Api.js
CHANGED
|
@@ -6,6 +6,8 @@ import {BrewCleaner} from "./BrewCleaner.js";
|
|
|
6
6
|
import {BrewTimestamper} from "./BrewTimestamper.js";
|
|
7
7
|
import {BrewTester} from "./BrewTester.js";
|
|
8
8
|
import {getCleanJson} from "./UtilClean.js";
|
|
9
|
+
import {DataTester, DataTesterBase, BraceCheck, EscapeCharacterCheck} from "./TestData.js";
|
|
10
|
+
import {ObjectWalker} from "./ObjectWalker.js";
|
|
9
11
|
|
|
10
12
|
export {
|
|
11
13
|
JsonTester,
|
|
@@ -16,4 +18,9 @@ export {
|
|
|
16
18
|
BrewTimestamper,
|
|
17
19
|
BrewTester,
|
|
18
20
|
getCleanJson,
|
|
21
|
+
DataTester,
|
|
22
|
+
DataTesterBase,
|
|
23
|
+
BraceCheck,
|
|
24
|
+
EscapeCharacterCheck,
|
|
25
|
+
ObjectWalker,
|
|
19
26
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class ObjectWalker {
|
|
2
|
+
static _runHandlers (
|
|
3
|
+
{
|
|
4
|
+
primitiveHandlers,
|
|
5
|
+
to,
|
|
6
|
+
obj,
|
|
7
|
+
filePath,
|
|
8
|
+
lastType,
|
|
9
|
+
lastKey,
|
|
10
|
+
},
|
|
11
|
+
) {
|
|
12
|
+
if (!primitiveHandlers[to]) return;
|
|
13
|
+
|
|
14
|
+
primitiveHandlers[to] instanceof Array
|
|
15
|
+
? primitiveHandlers[to].forEach(ph => ph(obj, {filePath, lastType, lastKey}))
|
|
16
|
+
: primitiveHandlers[to](obj, {filePath, lastType, lastKey});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static walk (
|
|
20
|
+
{
|
|
21
|
+
obj,
|
|
22
|
+
filePath,
|
|
23
|
+
primitiveHandlers,
|
|
24
|
+
lastType,
|
|
25
|
+
lastKey,
|
|
26
|
+
},
|
|
27
|
+
) {
|
|
28
|
+
const to = typeof obj;
|
|
29
|
+
|
|
30
|
+
const [opts] = arguments;
|
|
31
|
+
const nxtOpts = {...opts};
|
|
32
|
+
|
|
33
|
+
if (obj === null) {
|
|
34
|
+
this._runHandlers({
|
|
35
|
+
...nxtOpts,
|
|
36
|
+
to: "null",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
switch (to) {
|
|
43
|
+
case undefined:
|
|
44
|
+
case "boolean":
|
|
45
|
+
case "number":
|
|
46
|
+
case "string": {
|
|
47
|
+
this._runHandlers({
|
|
48
|
+
...nxtOpts,
|
|
49
|
+
to,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return obj;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case "object": {
|
|
56
|
+
if (obj instanceof Array) {
|
|
57
|
+
this._runHandlers({
|
|
58
|
+
...nxtOpts,
|
|
59
|
+
to: "array",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return obj.map(it => this.walk({obj: it, filePath, primitiveHandlers, lastType, lastKey}));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this._runHandlers({
|
|
66
|
+
...nxtOpts,
|
|
67
|
+
to: "object",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
Object.keys(obj)
|
|
71
|
+
.forEach(k => {
|
|
72
|
+
obj[k] = this.walk({obj: obj[k], filePath, primitiveHandlers, lastType, lastKey: k});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return obj;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
default: throw new Error(`Unhandled type "${to}"`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {ObjectWalker};
|
package/lib/TestData.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import {readJSON} from "./UtilFs.js";
|
|
3
|
+
import {ObjectWalker} from "./ObjectWalker.js";
|
|
4
|
+
|
|
5
|
+
/** Runs multiple handlers on each file, to avoid re-reading each file for each handler */
|
|
6
|
+
class _ParsedJsonChecker {
|
|
7
|
+
static _CLAZZES_FILE_HANDLER = [];
|
|
8
|
+
|
|
9
|
+
static _PRIMITIVE_HANDLERS = {};
|
|
10
|
+
|
|
11
|
+
static registerFileHandler (Clazz) {
|
|
12
|
+
_ParsedJsonChecker._CLAZZES_FILE_HANDLER.push(Clazz);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static addPrimitiveHandler (primitiveType, handler) {
|
|
16
|
+
(this._PRIMITIVE_HANDLERS[primitiveType] = this._PRIMITIVE_HANDLERS[primitiveType] || [])
|
|
17
|
+
.push(handler);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* -------------------------------------------- */
|
|
21
|
+
|
|
22
|
+
static run (filePath, {fnIsIgnoredFile, fnIsIgnoredDirector} = {}) {
|
|
23
|
+
this._fileRecurse({
|
|
24
|
+
filePath,
|
|
25
|
+
fnIsIgnoredFile,
|
|
26
|
+
fnIsIgnoredDirector,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static _fileRecurse ({filePath, fnIsIgnoredFile, fnIsIgnoredDirector}) {
|
|
31
|
+
if (fs.lstatSync(filePath).isDirectory()) {
|
|
32
|
+
if (fnIsIgnoredDirector && fnIsIgnoredDirector(filePath)) return;
|
|
33
|
+
|
|
34
|
+
return fs.readdirSync(filePath)
|
|
35
|
+
.forEach(nxt => this._fileRecurse({filePath: `${filePath}/${nxt}`, fnIsIgnoredFile, fnIsIgnoredDirector}));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!filePath.endsWith(".json") || (fnIsIgnoredFile && fnIsIgnoredFile(filePath))) return;
|
|
39
|
+
|
|
40
|
+
const contents = readJSON(filePath);
|
|
41
|
+
|
|
42
|
+
ObjectWalker.walk({
|
|
43
|
+
obj: contents,
|
|
44
|
+
filePath,
|
|
45
|
+
primitiveHandlers: this._PRIMITIVE_HANDLERS,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this._CLAZZES_FILE_HANDLER.forEach(dataTester => dataTester.handleFile(filePath, contents));
|
|
49
|
+
this._CLAZZES_FILE_HANDLER.forEach(dataTester => dataTester.addMessageBoundary());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class DataTesterBase {
|
|
54
|
+
static _MESSAGE = "";
|
|
55
|
+
|
|
56
|
+
static _addMessage (str) {
|
|
57
|
+
this._MESSAGE += str;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static addMessageBoundary () {
|
|
61
|
+
if (!this._MESSAGE.trim()) return;
|
|
62
|
+
if (this._MESSAGE.trim().slice(-5) === "\n---\n") return;
|
|
63
|
+
this._MESSAGE = this._MESSAGE.trimEnd();
|
|
64
|
+
this._MESSAGE += `\n---\n`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static getMessage () { return this._MESSAGE; }
|
|
68
|
+
|
|
69
|
+
/* -------------------------------------------- */
|
|
70
|
+
|
|
71
|
+
static async pRun () { /* Implement as required */ }
|
|
72
|
+
|
|
73
|
+
static async pPostRun () { /* Implement as required */ }
|
|
74
|
+
|
|
75
|
+
/* -------------------------------------------- */
|
|
76
|
+
|
|
77
|
+
static registerParsedFileCheckers (parsedJsonChecker) { /* Implement as required */ }
|
|
78
|
+
|
|
79
|
+
static registerParsedPrimitiveHandlers (parsedJsonChecker) { /* Implement as required */ }
|
|
80
|
+
|
|
81
|
+
/* -------------------------------------------- */
|
|
82
|
+
|
|
83
|
+
static handleFile (filePath, contents) { /* Implement as required */ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class BraceCheck extends DataTesterBase {
|
|
87
|
+
static registerParsedPrimitiveHandlers (parsedJsonChecker) {
|
|
88
|
+
parsedJsonChecker.addPrimitiveHandler("string", this._checkString.bind(this));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static _checkString (str, {filePath}) {
|
|
92
|
+
let total = 0;
|
|
93
|
+
for (let i = 0; i < str.length; ++i) {
|
|
94
|
+
const c = str[i];
|
|
95
|
+
switch (c) {
|
|
96
|
+
case "{":
|
|
97
|
+
++total;
|
|
98
|
+
break;
|
|
99
|
+
case "}":
|
|
100
|
+
--total;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (total !== 0) {
|
|
105
|
+
this._addMessage(`Mismatched braces in ${filePath}: "${str}"\n`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class EscapeCharacterCheck extends DataTesterBase {
|
|
111
|
+
static _CHARS = 16;
|
|
112
|
+
|
|
113
|
+
static _errors = [];
|
|
114
|
+
|
|
115
|
+
static registerParsedFileCheckers (parsedJsonChecker) {
|
|
116
|
+
parsedJsonChecker.registerFileHandler(this);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static _checkString (str) {
|
|
120
|
+
let re = /([\n\t\r])/g;
|
|
121
|
+
let m;
|
|
122
|
+
while ((m = re.exec(str))) {
|
|
123
|
+
const startIx = Math.max(m.index - this._CHARS, 0);
|
|
124
|
+
const endIx = Math.min(m.index + this._CHARS, str.length);
|
|
125
|
+
this._errors.push(`...${str.substring(startIx, endIx)}...`.replace(/[\n\t\r]/g, (...m) => m[0] === "\n" ? "***\\n***" : m[0] === "\t" ? "***\\t***" : "***\\r***"));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static handleFile (file, contents) {
|
|
130
|
+
this._errors = [];
|
|
131
|
+
ObjectWalker.walk({
|
|
132
|
+
obj: contents,
|
|
133
|
+
filePath: file,
|
|
134
|
+
primitiveHandlers: {
|
|
135
|
+
string: this._checkString.bind(this),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
if (this._errors.length) {
|
|
139
|
+
this._addMessage(`Unwanted escape characters in ${file}! See below:\n`);
|
|
140
|
+
this._addMessage(`\t${this._errors.join("\n\t")}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
class DataTester {
|
|
146
|
+
static async pRun (
|
|
147
|
+
filePath,
|
|
148
|
+
ClazzDataTesters,
|
|
149
|
+
{
|
|
150
|
+
fnIsIgnoredFile,
|
|
151
|
+
fnIsIgnoredDirector,
|
|
152
|
+
} = {},
|
|
153
|
+
) {
|
|
154
|
+
ClazzDataTesters.forEach(ClazzDataTester => {
|
|
155
|
+
ClazzDataTester.registerParsedPrimitiveHandlers(_ParsedJsonChecker);
|
|
156
|
+
ClazzDataTester.registerParsedFileCheckers(_ParsedJsonChecker);
|
|
157
|
+
});
|
|
158
|
+
_ParsedJsonChecker.run(filePath, {fnIsIgnoredFile, fnIsIgnoredDirector});
|
|
159
|
+
|
|
160
|
+
for (const ClazzDataTester of ClazzDataTesters) {
|
|
161
|
+
await ClazzDataTester.pRun();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const ClazzDataTester of ClazzDataTesters) {
|
|
165
|
+
await ClazzDataTester.pPostRun();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static getLogReport (ClazzDataTesters) {
|
|
170
|
+
let outMessage = "";
|
|
171
|
+
for (const ClazzDataTester of ClazzDataTesters) {
|
|
172
|
+
const pt = ClazzDataTester.getMessage();
|
|
173
|
+
if (pt) outMessage += `Error messages for ${ClazzDataTester.name}:\n\n${pt}\n`;
|
|
174
|
+
else console.log(`##### ${ClazzDataTester.name} passed! #####`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (outMessage) console.error(outMessage);
|
|
178
|
+
|
|
179
|
+
return outMessage;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
DataTester,
|
|
185
|
+
DataTesterBase,
|
|
186
|
+
BraceCheck,
|
|
187
|
+
EscapeCharacterCheck,
|
|
188
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.7",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"description": "Homebrew for 5etools. Should include arrays titled similarly to the main site data, e.g. `spell` or `class`",
|
|
6
6
|
"$defs": {
|
|
@@ -518,6 +518,9 @@
|
|
|
518
518
|
"makebrewCreatureTrait": {
|
|
519
519
|
"$ref": "makebrew-creature.json#/properties/makebrewCreatureTrait"
|
|
520
520
|
},
|
|
521
|
+
"makebrewCreatureAction": {
|
|
522
|
+
"$ref": "makebrew-creature.json#/properties/makebrewCreatureAction"
|
|
523
|
+
},
|
|
521
524
|
"reducedItemProperty": {
|
|
522
525
|
"$ref": "makecards.json#/properties/reducedItemProperty"
|
|
523
526
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "makebrew-creature.json",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"#makebrewCreatureTrait": {
|
|
@@ -34,6 +34,34 @@
|
|
|
34
34
|
"entries"
|
|
35
35
|
]
|
|
36
36
|
}
|
|
37
|
+
},
|
|
38
|
+
"makebrewCreatureAction": {
|
|
39
|
+
"description": "Additional action templates which can be used in the homebrew builder.",
|
|
40
|
+
"type": "array",
|
|
41
|
+
"minItems": 1,
|
|
42
|
+
"uniqueItems": true,
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"name": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"source": {
|
|
50
|
+
"type": "string"
|
|
51
|
+
},
|
|
52
|
+
"entries": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"$ref": "entry.json"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"required": [
|
|
60
|
+
"name",
|
|
61
|
+
"source",
|
|
62
|
+
"entries"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
37
65
|
}
|
|
38
66
|
},
|
|
39
67
|
"additionalProperties": false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.7",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"description": "Homebrew for 5etools. Should include arrays titled similarly to the main site data, e.g. `spell` or `class`",
|
|
6
6
|
"$defs": {
|
|
@@ -516,6 +516,9 @@
|
|
|
516
516
|
"makebrewCreatureTrait": {
|
|
517
517
|
"$ref": "makebrew-creature.json#/properties/makebrewCreatureTrait"
|
|
518
518
|
},
|
|
519
|
+
"makebrewCreatureAction": {
|
|
520
|
+
"$ref": "makebrew-creature.json#/properties/makebrewCreatureAction"
|
|
521
|
+
},
|
|
519
522
|
"reducedItemProperty": {
|
|
520
523
|
"$ref": "makecards.json#/properties/reducedItemProperty"
|
|
521
524
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "makebrew-creature.json",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"#makebrewCreatureTrait": {
|
|
@@ -34,6 +34,34 @@
|
|
|
34
34
|
"entries"
|
|
35
35
|
]
|
|
36
36
|
}
|
|
37
|
+
},
|
|
38
|
+
"makebrewCreatureAction": {
|
|
39
|
+
"description": "Additional action templates which can be used in the homebrew builder.",
|
|
40
|
+
"type": "array",
|
|
41
|
+
"minItems": 1,
|
|
42
|
+
"uniqueItems": true,
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"name": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"source": {
|
|
50
|
+
"type": "string"
|
|
51
|
+
},
|
|
52
|
+
"entries": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"$ref": "entry.json"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"required": [
|
|
60
|
+
"name",
|
|
61
|
+
"source",
|
|
62
|
+
"entries"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
37
65
|
}
|
|
38
66
|
},
|
|
39
67
|
"additionalProperties": false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.7",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"description": "Homebrew for 5etools. Should include arrays titled similarly to the main site data, e.g. `spell` or `class`",
|
|
6
6
|
"$defs": {
|
|
@@ -518,6 +518,9 @@
|
|
|
518
518
|
"makebrewCreatureTrait": {
|
|
519
519
|
"$ref": "makebrew-creature.json#/properties/makebrewCreatureTrait"
|
|
520
520
|
},
|
|
521
|
+
"makebrewCreatureAction": {
|
|
522
|
+
"$ref": "makebrew-creature.json#/properties/makebrewCreatureAction"
|
|
523
|
+
},
|
|
521
524
|
"reducedItemProperty": {
|
|
522
525
|
"$ref": "makecards.json#/properties/reducedItemProperty"
|
|
523
526
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "makebrew-creature.json",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"#makebrewCreatureTrait": {
|
|
@@ -33,6 +33,33 @@
|
|
|
33
33
|
"entries"
|
|
34
34
|
]
|
|
35
35
|
}
|
|
36
|
+
},
|
|
37
|
+
"makebrewCreatureAction": {
|
|
38
|
+
"description": "Additional action templates which can be used in the homebrew builder.",
|
|
39
|
+
"type": "array",
|
|
40
|
+
"minItems": 1,
|
|
41
|
+
"uniqueItems": true,
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"name": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"source": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
"entries": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"$ref": "entry.json"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"name",
|
|
60
|
+
"entries"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
36
63
|
}
|
|
37
64
|
},
|
|
38
65
|
"additionalProperties": false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.7",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"description": "Homebrew for 5etools. Should include arrays titled similarly to the main site data, e.g. `spell` or `class`",
|
|
6
6
|
"$defs": {
|
|
@@ -516,6 +516,9 @@
|
|
|
516
516
|
"makebrewCreatureTrait": {
|
|
517
517
|
"$ref": "makebrew-creature.json#/properties/makebrewCreatureTrait"
|
|
518
518
|
},
|
|
519
|
+
"makebrewCreatureAction": {
|
|
520
|
+
"$ref": "makebrew-creature.json#/properties/makebrewCreatureAction"
|
|
521
|
+
},
|
|
519
522
|
"reducedItemProperty": {
|
|
520
523
|
"$ref": "makecards.json#/properties/reducedItemProperty"
|
|
521
524
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "makebrew-creature.json",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.3",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"#makebrewCreatureTrait": {
|
|
@@ -33,6 +33,33 @@
|
|
|
33
33
|
"entries"
|
|
34
34
|
]
|
|
35
35
|
}
|
|
36
|
+
},
|
|
37
|
+
"makebrewCreatureAction": {
|
|
38
|
+
"description": "Additional action templates which can be used in the homebrew builder.",
|
|
39
|
+
"type": "array",
|
|
40
|
+
"minItems": 1,
|
|
41
|
+
"uniqueItems": true,
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"name": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"source": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
},
|
|
51
|
+
"entries": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"$ref": "entry.json"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"name",
|
|
60
|
+
"entries"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
36
63
|
}
|
|
37
64
|
},
|
|
38
65
|
"additionalProperties": false
|