5etools-utils 0.16.36 → 0.16.38
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/BrewTester/BrewTesterEdition.js +36 -1
- package/package.json +1 -1
- package/schema/brew/decks.json +19 -7
- package/schema/brew-fast/decks.json +19 -7
- package/schema/site/decks.json +19 -7
- package/schema/site-fast/decks.json +19 -7
- package/schema/ua/decks.json +19 -7
- package/schema/ua-fast/decks.json +19 -7
|
@@ -13,6 +13,7 @@ export class BrewTesterEdition extends BrewTesterBase {
|
|
|
13
13
|
_LOG_TAG = "EDITION";
|
|
14
14
|
|
|
15
15
|
static _EDITION_CLASSIC = "classic";
|
|
16
|
+
static _EDITION_ONE = "one";
|
|
16
17
|
|
|
17
18
|
// TODO(Future) this is non-exhaustive; we do not check e.g. `item.attachedSpells`
|
|
18
19
|
// Generalize an "entity -> used UIDs" system to use both here and in other tests
|
|
@@ -74,6 +75,32 @@ export class BrewTesterEdition extends BrewTesterBase {
|
|
|
74
75
|
|
|
75
76
|
/* -------------------------------------------- */
|
|
76
77
|
|
|
78
|
+
static _getSpellClassSourceErrors ({filePath, json, sourceToEdition}) {
|
|
79
|
+
const edition = json._meta?.edition || this._EDITION_CLASSIC;
|
|
80
|
+
|
|
81
|
+
return (json.spell || [])
|
|
82
|
+
.flatMap(spell => {
|
|
83
|
+
return [
|
|
84
|
+
...(spell.classes?.fromClassList || [])
|
|
85
|
+
.map(classMeta => ({prop: "fromClassList", classMeta})),
|
|
86
|
+
...(spell.classes?.fromClassListVariant || [])
|
|
87
|
+
.map(classMeta => ({prop: "fromClassListVariant", classMeta})),
|
|
88
|
+
...(spell.classes?.fromSubclass || [])
|
|
89
|
+
.map(({class: classMeta}) => ({prop: "fromSubclass", classMeta})),
|
|
90
|
+
]
|
|
91
|
+
.map(({prop, classMeta}) => {
|
|
92
|
+
const editionClass = sourceToEdition[classMeta.source.toLowerCase()];
|
|
93
|
+
if (!editionClass) return null;
|
|
94
|
+
if (editionClass === edition) return null;
|
|
95
|
+
|
|
96
|
+
return `${filePath} spell "${spell.name}" (${spell.source}) from edition "${edition}" had ${prop} "${classMeta.name}" (${classMeta.source}) from edition "${editionClass}"!`;
|
|
97
|
+
})
|
|
98
|
+
.filter(Boolean);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* -------------------------------------------- */
|
|
103
|
+
|
|
77
104
|
async _pRun () {
|
|
78
105
|
Um.info(this._LOG_TAG, `Checking sources used...`);
|
|
79
106
|
|
|
@@ -82,6 +109,12 @@ export class BrewTesterEdition extends BrewTesterBase {
|
|
|
82
109
|
schemaSources.$defs.sourcesModern.enum
|
|
83
110
|
.map(src => [src.toLowerCase(), src]),
|
|
84
111
|
);
|
|
112
|
+
const sourceToEdition = Object.fromEntries([
|
|
113
|
+
...schemaSources.$defs.sourcesClassic.enum
|
|
114
|
+
.map(src => [src.toLowerCase(), this.constructor._EDITION_CLASSIC]),
|
|
115
|
+
...schemaSources.$defs.sourcesModern.enum
|
|
116
|
+
.map(src => [src.toLowerCase(), this.constructor._EDITION_ONE]),
|
|
117
|
+
]);
|
|
85
118
|
|
|
86
119
|
const results = [];
|
|
87
120
|
Uf.runOnDirs((dir) => {
|
|
@@ -94,6 +127,8 @@ export class BrewTesterEdition extends BrewTesterBase {
|
|
|
94
127
|
|
|
95
128
|
const json = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
96
129
|
|
|
130
|
+
results.push(...this.constructor._getSpellClassSourceErrors({filePath, json, sourceToEdition}));
|
|
131
|
+
|
|
97
132
|
if (json._meta?.edition !== this.constructor._EDITION_CLASSIC) return;
|
|
98
133
|
|
|
99
134
|
const sourcesUsedModern = this.constructor._getSourcesUsed({filePath, json})
|
|
@@ -107,7 +142,7 @@ export class BrewTesterEdition extends BrewTesterBase {
|
|
|
107
142
|
|
|
108
143
|
if (results.length) {
|
|
109
144
|
results.forEach(r => Um.error(this._LOG_TAG, r));
|
|
110
|
-
throw new Error(`${results.length}
|
|
145
|
+
throw new Error(`${results.length} source edition error${results.length === 1 ? "" : "s"}! See above for more info.`);
|
|
111
146
|
}
|
|
112
147
|
|
|
113
148
|
Um.info(this._LOG_TAG, `Complete.`);
|
package/package.json
CHANGED
package/schema/brew/decks.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|
package/schema/site/decks.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|
package/schema/ua/decks.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "decks.json",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"$defs": {
|
|
7
7
|
"_deckSpreadOutcomesCardMap": {
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
"minProperties": 1,
|
|
35
35
|
"additionalProperties": false
|
|
36
36
|
},
|
|
37
|
+
"_deckSpreadSeeAlsoHeader": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. \"Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft\".",
|
|
42
|
+
"markdownDescription": "UIDs of adventure/book headers to be linked as a footnote.\n\nE.g. "Fortunes of Ravenloft|CoS|2|Fortunes of Ravenloft"."
|
|
43
|
+
},
|
|
44
|
+
"minItems": 1,
|
|
45
|
+
"uniqueItems": true
|
|
46
|
+
},
|
|
37
47
|
"_deckSpreadPosition": {
|
|
38
48
|
"type": "object",
|
|
39
49
|
"properties": {
|
|
@@ -48,13 +58,10 @@
|
|
|
48
58
|
"minItems": 1
|
|
49
59
|
},
|
|
50
60
|
"suits": {
|
|
51
|
-
"$comment": "The suits this position may draw from;
|
|
61
|
+
"$comment": "The suits this position may draw from; \"None\" matches cards which have no suit. Omit to draw from the whole deck.",
|
|
52
62
|
"type": "array",
|
|
53
63
|
"items": {
|
|
54
|
-
"type":
|
|
55
|
-
"string",
|
|
56
|
-
"null"
|
|
57
|
-
]
|
|
64
|
+
"type": "string"
|
|
58
65
|
},
|
|
59
66
|
"minItems": 1,
|
|
60
67
|
"uniqueItems": true
|
|
@@ -65,7 +72,6 @@
|
|
|
65
72
|
}
|
|
66
73
|
},
|
|
67
74
|
"required": [
|
|
68
|
-
"name",
|
|
69
75
|
"entries"
|
|
70
76
|
],
|
|
71
77
|
"additionalProperties": false
|
|
@@ -88,6 +94,12 @@
|
|
|
88
94
|
"$ref": "entry.json"
|
|
89
95
|
}
|
|
90
96
|
},
|
|
97
|
+
"seeAlsoAdventureHeader": {
|
|
98
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
99
|
+
},
|
|
100
|
+
"seeAlsoBookHeader": {
|
|
101
|
+
"$ref": "#/$defs/_deckSpreadSeeAlsoHeader"
|
|
102
|
+
},
|
|
91
103
|
"positions": {
|
|
92
104
|
"type": "array",
|
|
93
105
|
"items": {
|