@routevn/creator-model 1.2.9 → 1.3.0
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/package.json +1 -1
- package/src/model.js +1719 -192
package/src/model.js
CHANGED
|
@@ -35,11 +35,64 @@ const COLLECTION_KEYS = [
|
|
|
35
35
|
"layouts",
|
|
36
36
|
"controls",
|
|
37
37
|
];
|
|
38
|
-
const ROOT_KEYS = ["project", "story", ...COLLECTION_KEYS];
|
|
38
|
+
const ROOT_KEYS = ["project", "story", "tags", ...COLLECTION_KEYS];
|
|
39
|
+
const LINE_UPDATE_ACTIONS_PRESERVE_PATHS = ["dialogue.content"];
|
|
40
|
+
const LINE_UPDATE_ACTIONS_PRESERVE_PATHS_SET = new Set(
|
|
41
|
+
LINE_UPDATE_ACTIONS_PRESERVE_PATHS,
|
|
42
|
+
);
|
|
39
43
|
const createEmptyCollectionState = () => ({
|
|
40
44
|
items: {},
|
|
41
45
|
tree: [],
|
|
42
46
|
});
|
|
47
|
+
const TAG_SCOPE_BASE_KEYS = [
|
|
48
|
+
"images",
|
|
49
|
+
"sounds",
|
|
50
|
+
"videos",
|
|
51
|
+
"characters",
|
|
52
|
+
"transforms",
|
|
53
|
+
];
|
|
54
|
+
const CHARACTER_SPRITE_TAG_SCOPE_PREFIX = "characterSprites:";
|
|
55
|
+
const createEmptyTagsState = () => ({
|
|
56
|
+
images: createEmptyCollectionState(),
|
|
57
|
+
sounds: createEmptyCollectionState(),
|
|
58
|
+
videos: createEmptyCollectionState(),
|
|
59
|
+
characters: createEmptyCollectionState(),
|
|
60
|
+
transforms: createEmptyCollectionState(),
|
|
61
|
+
});
|
|
62
|
+
const isCharacterSpriteTagScopeKey = (value) =>
|
|
63
|
+
isNonEmptyString(value) &&
|
|
64
|
+
value.startsWith(CHARACTER_SPRITE_TAG_SCOPE_PREFIX) &&
|
|
65
|
+
value.length > CHARACTER_SPRITE_TAG_SCOPE_PREFIX.length;
|
|
66
|
+
const getCharacterSpriteTagScopeCharacterId = (scopeKey) =>
|
|
67
|
+
isCharacterSpriteTagScopeKey(scopeKey)
|
|
68
|
+
? scopeKey.slice(CHARACTER_SPRITE_TAG_SCOPE_PREFIX.length)
|
|
69
|
+
: undefined;
|
|
70
|
+
const isBaseTagScopeKey = (scopeKey) => TAG_SCOPE_BASE_KEYS.includes(scopeKey);
|
|
71
|
+
const normalizeTagsState = (tags) => {
|
|
72
|
+
if (tags === undefined) {
|
|
73
|
+
return createEmptyTagsState();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!isPlainObject(tags)) {
|
|
77
|
+
return tags;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const missingBaseScopeKeys = TAG_SCOPE_BASE_KEYS.filter(
|
|
81
|
+
(scopeKey) => tags[scopeKey] === undefined,
|
|
82
|
+
);
|
|
83
|
+
if (missingBaseScopeKeys.length === 0) {
|
|
84
|
+
return tags;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const nextTags = {
|
|
88
|
+
...tags,
|
|
89
|
+
};
|
|
90
|
+
for (const scopeKey of missingBaseScopeKeys) {
|
|
91
|
+
nextTags[scopeKey] = createEmptyCollectionState();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return nextTags;
|
|
95
|
+
};
|
|
43
96
|
|
|
44
97
|
const normalizeStateCollections = (state) => {
|
|
45
98
|
if (!isPlainObject(state)) {
|
|
@@ -51,8 +104,10 @@ const normalizeStateCollections = (state) => {
|
|
|
51
104
|
"particles",
|
|
52
105
|
"controls",
|
|
53
106
|
].filter((key) => state[key] === undefined);
|
|
107
|
+
const normalizedTags = normalizeTagsState(state.tags);
|
|
108
|
+
const hasNormalizedTags = normalizedTags !== state.tags;
|
|
54
109
|
|
|
55
|
-
if (missingCollectionKeys.length === 0) {
|
|
110
|
+
if (missingCollectionKeys.length === 0 && !hasNormalizedTags) {
|
|
56
111
|
return state;
|
|
57
112
|
}
|
|
58
113
|
|
|
@@ -64,6 +119,10 @@ const normalizeStateCollections = (state) => {
|
|
|
64
119
|
nextState[key] = createEmptyCollectionState();
|
|
65
120
|
});
|
|
66
121
|
|
|
122
|
+
if (hasNormalizedTags) {
|
|
123
|
+
nextState.tags = normalizedTags;
|
|
124
|
+
}
|
|
125
|
+
|
|
67
126
|
return nextState;
|
|
68
127
|
};
|
|
69
128
|
const isString = (value) => typeof value === "string";
|
|
@@ -160,7 +219,7 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
160
219
|
"container-ref-confirm-dialog-ok",
|
|
161
220
|
"container-ref-confirm-dialog-cancel",
|
|
162
221
|
];
|
|
163
|
-
export const SCHEMA_VERSION =
|
|
222
|
+
export const SCHEMA_VERSION = 3;
|
|
164
223
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
165
224
|
"folder",
|
|
166
225
|
"container",
|
|
@@ -264,6 +323,7 @@ const isVariableReferenceTarget = (state, variableId) => {
|
|
|
264
323
|
};
|
|
265
324
|
|
|
266
325
|
const LAYOUT_ITEM_TARGET_SET = new Set(["item.savedAt"]);
|
|
326
|
+
const LAYOUT_DIALOGUE_TARGET_SET = new Set(["dialogue.characterId"]);
|
|
267
327
|
const RUNTIME_TARGET_DOT_PATTERN = /^runtime\.([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
268
328
|
const VARIABLE_TARGET_DOT_PATTERN = /^variables\.([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
269
329
|
const VARIABLE_TARGET_BRACKET_PATTERN = /^variables\[(.+)\]$/;
|
|
@@ -280,6 +340,13 @@ const parseLayoutConditionTarget = (target) => {
|
|
|
280
340
|
};
|
|
281
341
|
}
|
|
282
342
|
|
|
343
|
+
if (LAYOUT_DIALOGUE_TARGET_SET.has(target)) {
|
|
344
|
+
return {
|
|
345
|
+
kind: "dialogue",
|
|
346
|
+
target,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
283
350
|
const runtimeMatch = target.match(RUNTIME_TARGET_DOT_PATTERN);
|
|
284
351
|
if (runtimeMatch && isRuntimeFieldId(runtimeMatch[1])) {
|
|
285
352
|
return {
|
|
@@ -336,7 +403,7 @@ const isLayoutConditionTarget = (state, target) => {
|
|
|
336
403
|
return false;
|
|
337
404
|
}
|
|
338
405
|
|
|
339
|
-
if (parsedTarget.kind
|
|
406
|
+
if (parsedTarget.kind !== "variable") {
|
|
340
407
|
return true;
|
|
341
408
|
}
|
|
342
409
|
|
|
@@ -777,6 +844,7 @@ const validateImageItems = ({ items, path, errorFactory }) => {
|
|
|
777
844
|
"type",
|
|
778
845
|
"name",
|
|
779
846
|
"description",
|
|
847
|
+
"tagIds",
|
|
780
848
|
"thumbnailFileId",
|
|
781
849
|
"fileId",
|
|
782
850
|
"width",
|
|
@@ -819,6 +887,18 @@ const validateImageItems = ({ items, path, errorFactory }) => {
|
|
|
819
887
|
}
|
|
820
888
|
|
|
821
889
|
if (item.type === "image") {
|
|
890
|
+
{
|
|
891
|
+
const result = validateOptionalUniqueIdArray({
|
|
892
|
+
value: item.tagIds,
|
|
893
|
+
path: `${itemPath}.tagIds`,
|
|
894
|
+
errorFactory,
|
|
895
|
+
allowEmpty: false,
|
|
896
|
+
});
|
|
897
|
+
if (result?.valid === false) {
|
|
898
|
+
return result;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
822
902
|
if (
|
|
823
903
|
item.thumbnailFileId !== undefined &&
|
|
824
904
|
!isNonEmptyString(item.thumbnailFileId)
|
|
@@ -1066,6 +1146,7 @@ const validateSoundItems = ({ items, path, errorFactory }) => {
|
|
|
1066
1146
|
"type",
|
|
1067
1147
|
"name",
|
|
1068
1148
|
"description",
|
|
1149
|
+
"tagIds",
|
|
1069
1150
|
"fileId",
|
|
1070
1151
|
"waveformDataFileId",
|
|
1071
1152
|
"duration",
|
|
@@ -1107,6 +1188,18 @@ const validateSoundItems = ({ items, path, errorFactory }) => {
|
|
|
1107
1188
|
}
|
|
1108
1189
|
|
|
1109
1190
|
if (item.type === "sound") {
|
|
1191
|
+
{
|
|
1192
|
+
const result = validateOptionalUniqueIdArray({
|
|
1193
|
+
value: item.tagIds,
|
|
1194
|
+
path: `${itemPath}.tagIds`,
|
|
1195
|
+
errorFactory,
|
|
1196
|
+
allowEmpty: false,
|
|
1197
|
+
});
|
|
1198
|
+
if (result?.valid === false) {
|
|
1199
|
+
return result;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1110
1203
|
if (!isNonEmptyString(item.fileId)) {
|
|
1111
1204
|
return invalidFromErrorFactory(
|
|
1112
1205
|
errorFactory,
|
|
@@ -1157,6 +1250,7 @@ const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
|
1157
1250
|
"type",
|
|
1158
1251
|
"name",
|
|
1159
1252
|
"description",
|
|
1253
|
+
"tagIds",
|
|
1160
1254
|
"fileId",
|
|
1161
1255
|
"thumbnailFileId",
|
|
1162
1256
|
"duration",
|
|
@@ -1200,6 +1294,18 @@ const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
|
1200
1294
|
}
|
|
1201
1295
|
|
|
1202
1296
|
if (item.type === "video") {
|
|
1297
|
+
{
|
|
1298
|
+
const result = validateOptionalUniqueIdArray({
|
|
1299
|
+
value: item.tagIds,
|
|
1300
|
+
path: `${itemPath}.tagIds`,
|
|
1301
|
+
errorFactory,
|
|
1302
|
+
allowEmpty: false,
|
|
1303
|
+
});
|
|
1304
|
+
if (result?.valid === false) {
|
|
1305
|
+
return result;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1203
1309
|
if (!isNonEmptyString(item.fileId)) {
|
|
1204
1310
|
return invalidFromErrorFactory(
|
|
1205
1311
|
errorFactory,
|
|
@@ -2081,6 +2187,7 @@ const validateTransformItems = ({ items, path, errorFactory }) => {
|
|
|
2081
2187
|
"type",
|
|
2082
2188
|
"name",
|
|
2083
2189
|
"description",
|
|
2190
|
+
"tagIds",
|
|
2084
2191
|
"x",
|
|
2085
2192
|
"y",
|
|
2086
2193
|
"scaleX",
|
|
@@ -2126,6 +2233,18 @@ const validateTransformItems = ({ items, path, errorFactory }) => {
|
|
|
2126
2233
|
}
|
|
2127
2234
|
|
|
2128
2235
|
if (item.type === "transform") {
|
|
2236
|
+
{
|
|
2237
|
+
const result = validateOptionalUniqueIdArray({
|
|
2238
|
+
value: item.tagIds,
|
|
2239
|
+
path: `${itemPath}.tagIds`,
|
|
2240
|
+
errorFactory,
|
|
2241
|
+
allowEmpty: false,
|
|
2242
|
+
});
|
|
2243
|
+
if (result?.valid === false) {
|
|
2244
|
+
return result;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2129
2248
|
for (const key of [
|
|
2130
2249
|
"x",
|
|
2131
2250
|
"y",
|
|
@@ -2590,7 +2709,12 @@ const validateTextStyleItems = ({ items, path, errorFactory }) => {
|
|
|
2590
2709
|
}
|
|
2591
2710
|
};
|
|
2592
2711
|
|
|
2593
|
-
const validateCharacterSpriteItems = ({
|
|
2712
|
+
const validateCharacterSpriteItems = ({
|
|
2713
|
+
items,
|
|
2714
|
+
path,
|
|
2715
|
+
errorFactory,
|
|
2716
|
+
allowTagIds = true,
|
|
2717
|
+
}) => {
|
|
2594
2718
|
for (const [itemId, item] of Object.entries(items)) {
|
|
2595
2719
|
const itemPath = `${path}.${itemId}`;
|
|
2596
2720
|
|
|
@@ -2612,6 +2736,7 @@ const validateCharacterSpriteItems = ({ items, path, errorFactory }) => {
|
|
|
2612
2736
|
"type",
|
|
2613
2737
|
"name",
|
|
2614
2738
|
"description",
|
|
2739
|
+
...(allowTagIds ? ["tagIds"] : []),
|
|
2615
2740
|
"thumbnailFileId",
|
|
2616
2741
|
"fileId",
|
|
2617
2742
|
"width",
|
|
@@ -2654,6 +2779,20 @@ const validateCharacterSpriteItems = ({ items, path, errorFactory }) => {
|
|
|
2654
2779
|
}
|
|
2655
2780
|
|
|
2656
2781
|
if (item.type === "image") {
|
|
2782
|
+
if (allowTagIds) {
|
|
2783
|
+
{
|
|
2784
|
+
const result = validateOptionalUniqueIdArray({
|
|
2785
|
+
value: item.tagIds,
|
|
2786
|
+
path: `${itemPath}.tagIds`,
|
|
2787
|
+
errorFactory,
|
|
2788
|
+
allowEmpty: false,
|
|
2789
|
+
});
|
|
2790
|
+
if (result?.valid === false) {
|
|
2791
|
+
return result;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2657
2796
|
if (
|
|
2658
2797
|
item.thumbnailFileId !== undefined &&
|
|
2659
2798
|
!isNonEmptyString(item.thumbnailFileId)
|
|
@@ -3449,6 +3588,7 @@ const validateCharacterItems = ({ items, path, errorFactory }) => {
|
|
|
3449
3588
|
"type",
|
|
3450
3589
|
"name",
|
|
3451
3590
|
"description",
|
|
3591
|
+
"tagIds",
|
|
3452
3592
|
"shortcut",
|
|
3453
3593
|
"fileId",
|
|
3454
3594
|
"sprites",
|
|
@@ -3490,6 +3630,18 @@ const validateCharacterItems = ({ items, path, errorFactory }) => {
|
|
|
3490
3630
|
}
|
|
3491
3631
|
|
|
3492
3632
|
if (item.type === "character") {
|
|
3633
|
+
{
|
|
3634
|
+
const result = validateOptionalUniqueIdArray({
|
|
3635
|
+
value: item.tagIds,
|
|
3636
|
+
path: `${itemPath}.tagIds`,
|
|
3637
|
+
errorFactory,
|
|
3638
|
+
allowEmpty: false,
|
|
3639
|
+
});
|
|
3640
|
+
if (result?.valid === false) {
|
|
3641
|
+
return result;
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3493
3645
|
if (item.shortcut !== undefined && !isString(item.shortcut)) {
|
|
3494
3646
|
return invalidFromErrorFactory(
|
|
3495
3647
|
errorFactory,
|
|
@@ -3521,80 +3673,23 @@ const validateCharacterItems = ({ items, path, errorFactory }) => {
|
|
|
3521
3673
|
}
|
|
3522
3674
|
};
|
|
3523
3675
|
|
|
3524
|
-
const
|
|
3525
|
-
|
|
3526
|
-
return VALID_RESULT;
|
|
3527
|
-
}
|
|
3528
|
-
|
|
3529
|
-
if (!isPlainObject(value)) {
|
|
3530
|
-
return invalidFromErrorFactory(
|
|
3531
|
-
errorFactory,
|
|
3532
|
-
`${path} must be an object when provided`,
|
|
3533
|
-
);
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
for (const [key, interaction] of Object.entries(value)) {
|
|
3537
|
-
if (!isNonEmptyString(key)) {
|
|
3538
|
-
return invalidFromErrorFactory(
|
|
3539
|
-
errorFactory,
|
|
3540
|
-
`${path} keys must be non-empty strings`,
|
|
3541
|
-
);
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
|
-
if (!isPlainObject(interaction)) {
|
|
3545
|
-
return invalidFromErrorFactory(
|
|
3546
|
-
errorFactory,
|
|
3547
|
-
`${path}.${key} must be an object`,
|
|
3548
|
-
);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
|
|
3552
|
-
return VALID_RESULT;
|
|
3553
|
-
};
|
|
3554
|
-
|
|
3555
|
-
const validatePreviewObject = ({ value, path, errorFactory }) => {
|
|
3556
|
-
if (value === undefined) {
|
|
3557
|
-
return VALID_RESULT;
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
if (!isPlainObject(value)) {
|
|
3561
|
-
return invalidFromErrorFactory(
|
|
3562
|
-
errorFactory,
|
|
3563
|
-
`${path} must be an object when provided`,
|
|
3564
|
-
);
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
return VALID_RESULT;
|
|
3568
|
-
};
|
|
3676
|
+
const validateTagItems = ({ items, path, errorFactory }) => {
|
|
3677
|
+
const seenNames = new Set();
|
|
3569
3678
|
|
|
3570
|
-
const validateLayoutItems = ({ items, path, errorFactory }) => {
|
|
3571
3679
|
for (const [itemId, item] of Object.entries(items)) {
|
|
3572
3680
|
const itemPath = `${path}.${itemId}`;
|
|
3573
3681
|
|
|
3574
|
-
if (item?.type !== "
|
|
3682
|
+
if (item?.type !== "tag") {
|
|
3575
3683
|
return invalidFromErrorFactory(
|
|
3576
3684
|
errorFactory,
|
|
3577
|
-
`${itemPath}.type must be '
|
|
3685
|
+
`${itemPath}.type must be 'tag'`,
|
|
3578
3686
|
);
|
|
3579
3687
|
}
|
|
3580
3688
|
|
|
3581
3689
|
{
|
|
3582
3690
|
const result = validateAllowedKeys({
|
|
3583
3691
|
value: item,
|
|
3584
|
-
allowedKeys:
|
|
3585
|
-
item.type === "folder"
|
|
3586
|
-
? ["id", "type", "name", "description"]
|
|
3587
|
-
: [
|
|
3588
|
-
"id",
|
|
3589
|
-
"type",
|
|
3590
|
-
"name",
|
|
3591
|
-
"description",
|
|
3592
|
-
"layoutType",
|
|
3593
|
-
"isFragment",
|
|
3594
|
-
"thumbnailFileId",
|
|
3595
|
-
"preview",
|
|
3596
|
-
"elements",
|
|
3597
|
-
],
|
|
3692
|
+
allowedKeys: ["id", "type", "name", "color"],
|
|
3598
3693
|
path: itemPath,
|
|
3599
3694
|
errorFactory,
|
|
3600
3695
|
});
|
|
@@ -3624,81 +3719,601 @@ const validateLayoutItems = ({ items, path, errorFactory }) => {
|
|
|
3624
3719
|
);
|
|
3625
3720
|
}
|
|
3626
3721
|
|
|
3627
|
-
if (item.
|
|
3722
|
+
if (item.color !== undefined && !isHexColor(item.color)) {
|
|
3628
3723
|
return invalidFromErrorFactory(
|
|
3629
3724
|
errorFactory,
|
|
3630
|
-
`${itemPath}.
|
|
3725
|
+
`${itemPath}.color must be a hex color when provided`,
|
|
3631
3726
|
);
|
|
3632
3727
|
}
|
|
3633
3728
|
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
!isNonEmptyString(item.thumbnailFileId)
|
|
3637
|
-
) {
|
|
3729
|
+
const normalizedName = item.name.trim().toLowerCase();
|
|
3730
|
+
if (seenNames.has(normalizedName)) {
|
|
3638
3731
|
return invalidFromErrorFactory(
|
|
3639
3732
|
errorFactory,
|
|
3640
|
-
`${itemPath}.
|
|
3733
|
+
`${itemPath}.name must be unique within its tag scope`,
|
|
3641
3734
|
);
|
|
3642
3735
|
}
|
|
3643
3736
|
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
}
|
|
3737
|
+
seenNames.add(normalizedName);
|
|
3738
|
+
}
|
|
3739
|
+
};
|
|
3740
|
+
|
|
3741
|
+
const validateFlatTagTree = ({ nodes, path, errorFactory }) => {
|
|
3742
|
+
const visitNodes = (entries, entryPath) => {
|
|
3743
|
+
if (!Array.isArray(entries)) {
|
|
3744
|
+
return VALID_RESULT;
|
|
3653
3745
|
}
|
|
3654
3746
|
|
|
3655
|
-
|
|
3656
|
-
if (
|
|
3747
|
+
for (const [index, node] of entries.entries()) {
|
|
3748
|
+
if (Object.hasOwn(node, "children")) {
|
|
3657
3749
|
return invalidFromErrorFactory(
|
|
3658
3750
|
errorFactory,
|
|
3659
|
-
`${
|
|
3751
|
+
`${entryPath}[${index}].children is not allowed`,
|
|
3660
3752
|
);
|
|
3661
3753
|
}
|
|
3754
|
+
}
|
|
3662
3755
|
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
typeof item.isFragment !== "boolean"
|
|
3666
|
-
) {
|
|
3667
|
-
return invalidFromErrorFactory(
|
|
3668
|
-
errorFactory,
|
|
3669
|
-
`${itemPath}.isFragment must be a boolean when provided`,
|
|
3670
|
-
);
|
|
3671
|
-
}
|
|
3756
|
+
return VALID_RESULT;
|
|
3757
|
+
};
|
|
3672
3758
|
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3759
|
+
return visitNodes(nodes, path);
|
|
3760
|
+
};
|
|
3761
|
+
|
|
3762
|
+
const validateTagCreateData = ({
|
|
3763
|
+
data,
|
|
3764
|
+
path = "payload.data",
|
|
3765
|
+
errorFactory = createPayloadValidationError,
|
|
3766
|
+
}) => {
|
|
3767
|
+
{
|
|
3768
|
+
const result = validateAllowedKeys({
|
|
3769
|
+
value: data,
|
|
3770
|
+
allowedKeys: ["type", "name", "color"],
|
|
3771
|
+
path,
|
|
3772
|
+
errorFactory,
|
|
3773
|
+
});
|
|
3774
|
+
if (result?.valid === false) {
|
|
3775
|
+
return result;
|
|
3685
3776
|
}
|
|
3686
3777
|
}
|
|
3778
|
+
|
|
3779
|
+
if (data?.type !== "tag") {
|
|
3780
|
+
return invalidFromErrorFactory(
|
|
3781
|
+
errorFactory,
|
|
3782
|
+
`${path}.type must be 'tag'`,
|
|
3783
|
+
);
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
if (!isNonEmptyString(data.name)) {
|
|
3787
|
+
return invalidFromErrorFactory(
|
|
3788
|
+
errorFactory,
|
|
3789
|
+
`${path}.name must be a non-empty string`,
|
|
3790
|
+
);
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
if (data.color !== undefined && !isHexColor(data.color)) {
|
|
3794
|
+
return invalidFromErrorFactory(
|
|
3795
|
+
errorFactory,
|
|
3796
|
+
`${path}.color must be a hex color when provided`,
|
|
3797
|
+
);
|
|
3798
|
+
}
|
|
3687
3799
|
};
|
|
3688
3800
|
|
|
3689
|
-
const
|
|
3690
|
-
|
|
3691
|
-
|
|
3801
|
+
const validateTagUpdateData = ({
|
|
3802
|
+
data,
|
|
3803
|
+
path = "payload.data",
|
|
3804
|
+
errorFactory = createPayloadValidationError,
|
|
3805
|
+
}) => {
|
|
3806
|
+
{
|
|
3807
|
+
const result = validateAllowedKeys({
|
|
3808
|
+
value: data,
|
|
3809
|
+
allowedKeys: ["name", "color"],
|
|
3810
|
+
path,
|
|
3811
|
+
errorFactory,
|
|
3812
|
+
});
|
|
3813
|
+
if (result?.valid === false) {
|
|
3814
|
+
return result;
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3692
3817
|
|
|
3693
|
-
|
|
3818
|
+
if (Object.keys(data).length === 0) {
|
|
3819
|
+
return invalidFromErrorFactory(
|
|
3820
|
+
errorFactory,
|
|
3821
|
+
`${path} must include at least one field`,
|
|
3822
|
+
);
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
if (data.name !== undefined && !isNonEmptyString(data.name)) {
|
|
3826
|
+
return invalidFromErrorFactory(
|
|
3827
|
+
errorFactory,
|
|
3828
|
+
`${path}.name must be a non-empty string when provided`,
|
|
3829
|
+
);
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3832
|
+
if (
|
|
3833
|
+
data.color !== undefined &&
|
|
3834
|
+
data.color !== null &&
|
|
3835
|
+
!isHexColor(data.color)
|
|
3836
|
+
) {
|
|
3837
|
+
return invalidFromErrorFactory(
|
|
3838
|
+
errorFactory,
|
|
3839
|
+
`${path}.color must be a hex color or null when provided`,
|
|
3840
|
+
);
|
|
3841
|
+
}
|
|
3842
|
+
};
|
|
3843
|
+
|
|
3844
|
+
const validateTagsRoot = ({ state, tags, path, errorFactory }) => {
|
|
3845
|
+
if (!isPlainObject(tags)) {
|
|
3846
|
+
return invalidFromErrorFactory(errorFactory, `${path} must be an object`);
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
for (const scopeKey of Object.keys(tags)) {
|
|
3850
|
+
if (!isBaseTagScopeKey(scopeKey) && !isCharacterSpriteTagScopeKey(scopeKey)) {
|
|
3694
3851
|
return invalidFromErrorFactory(
|
|
3695
3852
|
errorFactory,
|
|
3696
|
-
`${
|
|
3853
|
+
`${path}.${scopeKey} is not allowed`,
|
|
3697
3854
|
);
|
|
3698
3855
|
}
|
|
3856
|
+
}
|
|
3699
3857
|
|
|
3700
|
-
|
|
3701
|
-
|
|
3858
|
+
for (const scopeKey of TAG_SCOPE_BASE_KEYS) {
|
|
3859
|
+
if (!Object.hasOwn(tags, scopeKey)) {
|
|
3860
|
+
return invalidFromErrorFactory(
|
|
3861
|
+
errorFactory,
|
|
3862
|
+
`${path}.${scopeKey} is required`,
|
|
3863
|
+
);
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
|
|
3867
|
+
for (const [scopeKey, collection] of Object.entries(tags)) {
|
|
3868
|
+
{
|
|
3869
|
+
const result = validateNestedCollection({
|
|
3870
|
+
collection,
|
|
3871
|
+
path: `${path}.${scopeKey}`,
|
|
3872
|
+
itemValidator: validateTagItems,
|
|
3873
|
+
treeValidator: validateFlatTagTree,
|
|
3874
|
+
errorFactory,
|
|
3875
|
+
});
|
|
3876
|
+
if (result?.valid === false) {
|
|
3877
|
+
return result;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
if (!isCharacterSpriteTagScopeKey(scopeKey)) {
|
|
3882
|
+
continue;
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
const characterId = getCharacterSpriteTagScopeCharacterId(scopeKey);
|
|
3886
|
+
const character = state.characters?.items?.[characterId];
|
|
3887
|
+
if (!isPlainObject(character) || character.type !== "character") {
|
|
3888
|
+
return invalidFromErrorFactory(
|
|
3889
|
+
errorFactory,
|
|
3890
|
+
`${path}.${scopeKey} must reference an existing character`,
|
|
3891
|
+
);
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
};
|
|
3895
|
+
|
|
3896
|
+
const getTagScopeCollection = ({ state, scopeKey }) => state.tags?.[scopeKey];
|
|
3897
|
+
|
|
3898
|
+
const validateTagScopeKey = ({ scopeKey, path, errorFactory }) => {
|
|
3899
|
+
if (!isNonEmptyString(scopeKey)) {
|
|
3900
|
+
return invalidFromErrorFactory(
|
|
3901
|
+
errorFactory,
|
|
3902
|
+
`${path} must be a non-empty string`,
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
if (isBaseTagScopeKey(scopeKey) || isCharacterSpriteTagScopeKey(scopeKey)) {
|
|
3907
|
+
return VALID_RESULT;
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
return invalidFromErrorFactory(
|
|
3911
|
+
errorFactory,
|
|
3912
|
+
`${path} must be a supported tag scope key`,
|
|
3913
|
+
);
|
|
3914
|
+
};
|
|
3915
|
+
|
|
3916
|
+
const validateTagScopeAgainstState = ({
|
|
3917
|
+
state,
|
|
3918
|
+
scopeKey,
|
|
3919
|
+
path,
|
|
3920
|
+
details = {},
|
|
3921
|
+
errorFactory = createPreconditionValidationError,
|
|
3922
|
+
}) => {
|
|
3923
|
+
const scopeKeyResult = validateTagScopeKey({
|
|
3924
|
+
scopeKey,
|
|
3925
|
+
path,
|
|
3926
|
+
errorFactory,
|
|
3927
|
+
});
|
|
3928
|
+
if (!scopeKeyResult.valid) {
|
|
3929
|
+
return scopeKeyResult;
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
if (!isCharacterSpriteTagScopeKey(scopeKey)) {
|
|
3933
|
+
return VALID_RESULT;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
const characterId = getCharacterSpriteTagScopeCharacterId(scopeKey);
|
|
3937
|
+
const character = state.characters?.items?.[characterId];
|
|
3938
|
+
if (!isPlainObject(character) || character.type !== "character") {
|
|
3939
|
+
return invalidFromErrorFactory(
|
|
3940
|
+
errorFactory,
|
|
3941
|
+
`${path} must reference an existing character sprite tag scope`,
|
|
3942
|
+
{
|
|
3943
|
+
...details,
|
|
3944
|
+
characterId,
|
|
3945
|
+
scopeKey,
|
|
3946
|
+
},
|
|
3947
|
+
);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
return VALID_RESULT;
|
|
3951
|
+
};
|
|
3952
|
+
|
|
3953
|
+
const validateTagIdsAgainstScope = ({
|
|
3954
|
+
state,
|
|
3955
|
+
tagIds,
|
|
3956
|
+
scopeKey,
|
|
3957
|
+
path,
|
|
3958
|
+
details = {},
|
|
3959
|
+
errorFactory = createPreconditionValidationError,
|
|
3960
|
+
}) => {
|
|
3961
|
+
if (tagIds === undefined) {
|
|
3962
|
+
return VALID_RESULT;
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
const scopeResult = validateTagScopeAgainstState({
|
|
3966
|
+
state,
|
|
3967
|
+
scopeKey,
|
|
3968
|
+
path,
|
|
3969
|
+
details,
|
|
3970
|
+
errorFactory,
|
|
3971
|
+
});
|
|
3972
|
+
if (!scopeResult.valid) {
|
|
3973
|
+
return scopeResult;
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
const collection = getTagScopeCollection({ state, scopeKey });
|
|
3977
|
+
for (const [index, tagId] of tagIds.entries()) {
|
|
3978
|
+
const tag = collection?.items?.[tagId];
|
|
3979
|
+
if (!isPlainObject(tag) || tag.type !== "tag") {
|
|
3980
|
+
return invalidFromErrorFactory(
|
|
3981
|
+
errorFactory,
|
|
3982
|
+
`${path}[${index}] must reference an existing tag in scope '${scopeKey}'`,
|
|
3983
|
+
{
|
|
3984
|
+
...details,
|
|
3985
|
+
scopeKey,
|
|
3986
|
+
tagId,
|
|
3987
|
+
},
|
|
3988
|
+
);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
return VALID_RESULT;
|
|
3993
|
+
};
|
|
3994
|
+
|
|
3995
|
+
const validateUniqueTagNameInScope = ({
|
|
3996
|
+
collection,
|
|
3997
|
+
name,
|
|
3998
|
+
path,
|
|
3999
|
+
excludeTagId,
|
|
4000
|
+
errorFactory = createPreconditionValidationError,
|
|
4001
|
+
}) => {
|
|
4002
|
+
const normalizedName = name.trim().toLowerCase();
|
|
4003
|
+
|
|
4004
|
+
for (const [tagId, tag] of Object.entries(collection?.items || {})) {
|
|
4005
|
+
if (tagId === excludeTagId || tag?.type !== "tag") {
|
|
4006
|
+
continue;
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
if (tag.name?.trim?.().toLowerCase?.() === normalizedName) {
|
|
4010
|
+
return invalidFromErrorFactory(
|
|
4011
|
+
errorFactory,
|
|
4012
|
+
`${path} must be unique within its tag scope`,
|
|
4013
|
+
);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
return VALID_RESULT;
|
|
4018
|
+
};
|
|
4019
|
+
|
|
4020
|
+
const ensureTagScopeCollection = ({ state, scopeKey }) => {
|
|
4021
|
+
state.tags ??= createEmptyTagsState();
|
|
4022
|
+
state.tags[scopeKey] ??= createEmptyCollectionState();
|
|
4023
|
+
return state.tags[scopeKey];
|
|
4024
|
+
};
|
|
4025
|
+
|
|
4026
|
+
const assignOptionalTagIds = ({ target, tagIds }) => {
|
|
4027
|
+
if (Array.isArray(tagIds) && tagIds.length > 0) {
|
|
4028
|
+
target.tagIds = structuredClone(tagIds);
|
|
4029
|
+
}
|
|
4030
|
+
};
|
|
4031
|
+
|
|
4032
|
+
const applyTagIdsUpdate = ({ currentItem, data }) => {
|
|
4033
|
+
const nextData = structuredClone(data);
|
|
4034
|
+
if (nextData.tagIds === undefined) {
|
|
4035
|
+
delete nextData.tagIds;
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
const nextItem = {
|
|
4039
|
+
...structuredClone(currentItem),
|
|
4040
|
+
...nextData,
|
|
4041
|
+
};
|
|
4042
|
+
|
|
4043
|
+
if (data.tagIds !== undefined) {
|
|
4044
|
+
if (Array.isArray(data.tagIds) && data.tagIds.length > 0) {
|
|
4045
|
+
nextItem.tagIds = structuredClone(data.tagIds);
|
|
4046
|
+
} else {
|
|
4047
|
+
delete nextItem.tagIds;
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
return nextItem;
|
|
4052
|
+
};
|
|
4053
|
+
|
|
4054
|
+
const stripDeletedTagIdsFromItem = ({ item, deletedTagIds }) => {
|
|
4055
|
+
if (!Array.isArray(item?.tagIds) || item.tagIds.length === 0) {
|
|
4056
|
+
return;
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
const remainingTagIds = item.tagIds.filter((tagId) => !deletedTagIds.has(tagId));
|
|
4060
|
+
if (remainingTagIds.length === item.tagIds.length) {
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
|
|
4064
|
+
if (remainingTagIds.length === 0) {
|
|
4065
|
+
delete item.tagIds;
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
|
|
4069
|
+
item.tagIds = remainingTagIds;
|
|
4070
|
+
};
|
|
4071
|
+
|
|
4072
|
+
const stripDeletedTagIdsFromScopeItems = ({ state, scopeKey, deletedTagIds }) => {
|
|
4073
|
+
if (deletedTagIds.size === 0) {
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
if (scopeKey === "images") {
|
|
4078
|
+
for (const item of Object.values(state.images.items)) {
|
|
4079
|
+
if (item?.type === "image") {
|
|
4080
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
if (scopeKey === "sounds") {
|
|
4087
|
+
for (const item of Object.values(state.sounds.items)) {
|
|
4088
|
+
if (item?.type === "sound") {
|
|
4089
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
return;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
if (scopeKey === "videos") {
|
|
4096
|
+
for (const item of Object.values(state.videos.items)) {
|
|
4097
|
+
if (item?.type === "video") {
|
|
4098
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
if (scopeKey === "characters") {
|
|
4105
|
+
for (const item of Object.values(state.characters.items)) {
|
|
4106
|
+
if (item?.type === "character") {
|
|
4107
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
if (scopeKey === "transforms") {
|
|
4114
|
+
for (const item of Object.values(state.transforms.items)) {
|
|
4115
|
+
if (item?.type === "transform") {
|
|
4116
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
if (!isCharacterSpriteTagScopeKey(scopeKey)) {
|
|
4123
|
+
return;
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
const characterId = getCharacterSpriteTagScopeCharacterId(scopeKey);
|
|
4127
|
+
const collection = getCharacterSpriteCollection({
|
|
4128
|
+
state,
|
|
4129
|
+
characterId,
|
|
4130
|
+
});
|
|
4131
|
+
|
|
4132
|
+
for (const item of Object.values(collection?.items || {})) {
|
|
4133
|
+
if (item?.type === "image") {
|
|
4134
|
+
stripDeletedTagIdsFromItem({ item, deletedTagIds });
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
4138
|
+
|
|
4139
|
+
const validateKeyboardMap = ({ value, path, errorFactory }) => {
|
|
4140
|
+
if (value === undefined) {
|
|
4141
|
+
return VALID_RESULT;
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
if (!isPlainObject(value)) {
|
|
4145
|
+
return invalidFromErrorFactory(
|
|
4146
|
+
errorFactory,
|
|
4147
|
+
`${path} must be an object when provided`,
|
|
4148
|
+
);
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4151
|
+
for (const [key, interaction] of Object.entries(value)) {
|
|
4152
|
+
if (!isNonEmptyString(key)) {
|
|
4153
|
+
return invalidFromErrorFactory(
|
|
4154
|
+
errorFactory,
|
|
4155
|
+
`${path} keys must be non-empty strings`,
|
|
4156
|
+
);
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
if (!isPlainObject(interaction)) {
|
|
4160
|
+
return invalidFromErrorFactory(
|
|
4161
|
+
errorFactory,
|
|
4162
|
+
`${path}.${key} must be an object`,
|
|
4163
|
+
);
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
return VALID_RESULT;
|
|
4168
|
+
};
|
|
4169
|
+
|
|
4170
|
+
const validatePreviewObject = ({ value, path, errorFactory }) => {
|
|
4171
|
+
if (value === undefined) {
|
|
4172
|
+
return VALID_RESULT;
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4175
|
+
if (!isPlainObject(value)) {
|
|
4176
|
+
return invalidFromErrorFactory(
|
|
4177
|
+
errorFactory,
|
|
4178
|
+
`${path} must be an object when provided`,
|
|
4179
|
+
);
|
|
4180
|
+
}
|
|
4181
|
+
|
|
4182
|
+
return VALID_RESULT;
|
|
4183
|
+
};
|
|
4184
|
+
|
|
4185
|
+
const validateLayoutItems = ({ items, path, errorFactory }) => {
|
|
4186
|
+
for (const [itemId, item] of Object.entries(items)) {
|
|
4187
|
+
const itemPath = `${path}.${itemId}`;
|
|
4188
|
+
|
|
4189
|
+
if (item?.type !== "folder" && item?.type !== "layout") {
|
|
4190
|
+
return invalidFromErrorFactory(
|
|
4191
|
+
errorFactory,
|
|
4192
|
+
`${itemPath}.type must be 'folder' or 'layout'`,
|
|
4193
|
+
);
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
{
|
|
4197
|
+
const result = validateAllowedKeys({
|
|
4198
|
+
value: item,
|
|
4199
|
+
allowedKeys:
|
|
4200
|
+
item.type === "folder"
|
|
4201
|
+
? ["id", "type", "name", "description"]
|
|
4202
|
+
: [
|
|
4203
|
+
"id",
|
|
4204
|
+
"type",
|
|
4205
|
+
"name",
|
|
4206
|
+
"description",
|
|
4207
|
+
"layoutType",
|
|
4208
|
+
"isFragment",
|
|
4209
|
+
"thumbnailFileId",
|
|
4210
|
+
"preview",
|
|
4211
|
+
"elements",
|
|
4212
|
+
],
|
|
4213
|
+
path: itemPath,
|
|
4214
|
+
errorFactory,
|
|
4215
|
+
});
|
|
4216
|
+
if (result?.valid === false) {
|
|
4217
|
+
return result;
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
if (!isNonEmptyString(item.id)) {
|
|
4222
|
+
return invalidFromErrorFactory(
|
|
4223
|
+
errorFactory,
|
|
4224
|
+
`${itemPath}.id must be a non-empty string`,
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
if (item.id !== itemId) {
|
|
4229
|
+
return invalidFromErrorFactory(
|
|
4230
|
+
errorFactory,
|
|
4231
|
+
`${itemPath}.id must match item key '${itemId}'`,
|
|
4232
|
+
);
|
|
4233
|
+
}
|
|
4234
|
+
|
|
4235
|
+
if (!isNonEmptyString(item.name)) {
|
|
4236
|
+
return invalidFromErrorFactory(
|
|
4237
|
+
errorFactory,
|
|
4238
|
+
`${itemPath}.name must be a non-empty string`,
|
|
4239
|
+
);
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4242
|
+
if (item.description !== undefined && !isString(item.description)) {
|
|
4243
|
+
return invalidFromErrorFactory(
|
|
4244
|
+
errorFactory,
|
|
4245
|
+
`${itemPath}.description must be a string when provided`,
|
|
4246
|
+
);
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
if (
|
|
4250
|
+
item.thumbnailFileId !== undefined &&
|
|
4251
|
+
!isNonEmptyString(item.thumbnailFileId)
|
|
4252
|
+
) {
|
|
4253
|
+
return invalidFromErrorFactory(
|
|
4254
|
+
errorFactory,
|
|
4255
|
+
`${itemPath}.thumbnailFileId must be a non-empty string when provided`,
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
{
|
|
4260
|
+
const result = validatePreviewObject({
|
|
4261
|
+
value: item.preview,
|
|
4262
|
+
path: `${itemPath}.preview`,
|
|
4263
|
+
errorFactory,
|
|
4264
|
+
});
|
|
4265
|
+
if (result?.valid === false) {
|
|
4266
|
+
return result;
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
if (item.type === "layout") {
|
|
4271
|
+
if (!LAYOUT_TYPE_KEYS.includes(item.layoutType)) {
|
|
4272
|
+
return invalidFromErrorFactory(
|
|
4273
|
+
errorFactory,
|
|
4274
|
+
`${itemPath}.layoutType must be 'general', 'save-load', 'confirmDialog', 'dialogue-adv', 'dialogue-nvl', 'choice', or 'history'`,
|
|
4275
|
+
);
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
if (
|
|
4279
|
+
item.isFragment !== undefined &&
|
|
4280
|
+
typeof item.isFragment !== "boolean"
|
|
4281
|
+
) {
|
|
4282
|
+
return invalidFromErrorFactory(
|
|
4283
|
+
errorFactory,
|
|
4284
|
+
`${itemPath}.isFragment must be a boolean when provided`,
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
{
|
|
4289
|
+
const result = validateNestedCollection({
|
|
4290
|
+
collection: item.elements,
|
|
4291
|
+
path: `${itemPath}.elements`,
|
|
4292
|
+
itemValidator: validateLayoutElementItems,
|
|
4293
|
+
treeValidator: validateLayoutElementTreeOwnership,
|
|
4294
|
+
treeNodeLabel: "layout element",
|
|
4295
|
+
});
|
|
4296
|
+
if (result?.valid === false) {
|
|
4297
|
+
return result;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
};
|
|
4303
|
+
|
|
4304
|
+
const validateControlItems = ({ items, path, errorFactory }) => {
|
|
4305
|
+
for (const [itemId, item] of Object.entries(items)) {
|
|
4306
|
+
const itemPath = `${path}.${itemId}`;
|
|
4307
|
+
|
|
4308
|
+
if (item?.type !== "folder" && item?.type !== "control") {
|
|
4309
|
+
return invalidFromErrorFactory(
|
|
4310
|
+
errorFactory,
|
|
4311
|
+
`${itemPath}.type must be 'folder' or 'control'`,
|
|
4312
|
+
);
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
{
|
|
4316
|
+
const result = validateAllowedKeys({
|
|
3702
4317
|
value: item,
|
|
3703
4318
|
allowedKeys:
|
|
3704
4319
|
item.type === "folder"
|
|
@@ -4850,6 +5465,22 @@ export const assertInvariants = ({ state }) => {
|
|
|
4850
5465
|
return result;
|
|
4851
5466
|
}
|
|
4852
5467
|
}
|
|
5468
|
+
|
|
5469
|
+
{
|
|
5470
|
+
const result = validateTagIdsAgainstScope({
|
|
5471
|
+
state,
|
|
5472
|
+
tagIds: image.tagIds,
|
|
5473
|
+
scopeKey: "images",
|
|
5474
|
+
path: "image.tagIds",
|
|
5475
|
+
details: {
|
|
5476
|
+
imageId,
|
|
5477
|
+
},
|
|
5478
|
+
errorFactory: createInvariantValidationError,
|
|
5479
|
+
});
|
|
5480
|
+
if (!result.valid) {
|
|
5481
|
+
return result;
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
4853
5484
|
}
|
|
4854
5485
|
|
|
4855
5486
|
for (const [spritesheetId, spritesheet] of Object.entries(
|
|
@@ -4922,6 +5553,22 @@ export const assertInvariants = ({ state }) => {
|
|
|
4922
5553
|
return result;
|
|
4923
5554
|
}
|
|
4924
5555
|
}
|
|
5556
|
+
|
|
5557
|
+
{
|
|
5558
|
+
const result = validateTagIdsAgainstScope({
|
|
5559
|
+
state,
|
|
5560
|
+
tagIds: sound.tagIds,
|
|
5561
|
+
scopeKey: "sounds",
|
|
5562
|
+
path: "sound.tagIds",
|
|
5563
|
+
details: {
|
|
5564
|
+
soundId,
|
|
5565
|
+
},
|
|
5566
|
+
errorFactory: createInvariantValidationError,
|
|
5567
|
+
});
|
|
5568
|
+
if (!result.valid) {
|
|
5569
|
+
return result;
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
4925
5572
|
}
|
|
4926
5573
|
|
|
4927
5574
|
for (const [videoId, video] of Object.entries(state.videos.items)) {
|
|
@@ -4954,6 +5601,22 @@ export const assertInvariants = ({ state }) => {
|
|
|
4954
5601
|
return result;
|
|
4955
5602
|
}
|
|
4956
5603
|
}
|
|
5604
|
+
|
|
5605
|
+
{
|
|
5606
|
+
const result = validateTagIdsAgainstScope({
|
|
5607
|
+
state,
|
|
5608
|
+
tagIds: video.tagIds,
|
|
5609
|
+
scopeKey: "videos",
|
|
5610
|
+
path: "video.tagIds",
|
|
5611
|
+
details: {
|
|
5612
|
+
videoId,
|
|
5613
|
+
},
|
|
5614
|
+
errorFactory: createInvariantValidationError,
|
|
5615
|
+
});
|
|
5616
|
+
if (!result.valid) {
|
|
5617
|
+
return result;
|
|
5618
|
+
}
|
|
5619
|
+
}
|
|
4957
5620
|
}
|
|
4958
5621
|
|
|
4959
5622
|
for (const [fontId, font] of Object.entries(state.fonts.items)) {
|
|
@@ -5012,6 +5675,22 @@ export const assertInvariants = ({ state }) => {
|
|
|
5012
5675
|
}
|
|
5013
5676
|
}
|
|
5014
5677
|
|
|
5678
|
+
{
|
|
5679
|
+
const result = validateTagIdsAgainstScope({
|
|
5680
|
+
state,
|
|
5681
|
+
tagIds: character.tagIds,
|
|
5682
|
+
scopeKey: "characters",
|
|
5683
|
+
path: "character.tagIds",
|
|
5684
|
+
details: {
|
|
5685
|
+
characterId,
|
|
5686
|
+
},
|
|
5687
|
+
errorFactory: createInvariantValidationError,
|
|
5688
|
+
});
|
|
5689
|
+
if (!result.valid) {
|
|
5690
|
+
return result;
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5015
5694
|
for (const [spriteId, sprite] of Object.entries(
|
|
5016
5695
|
character.sprites?.items || {},
|
|
5017
5696
|
)) {
|
|
@@ -5030,6 +5709,23 @@ export const assertInvariants = ({ state }) => {
|
|
|
5030
5709
|
return result;
|
|
5031
5710
|
}
|
|
5032
5711
|
|
|
5712
|
+
{
|
|
5713
|
+
const tagResult = validateTagIdsAgainstScope({
|
|
5714
|
+
state,
|
|
5715
|
+
tagIds: sprite.tagIds,
|
|
5716
|
+
scopeKey: `${CHARACTER_SPRITE_TAG_SCOPE_PREFIX}${characterId}`,
|
|
5717
|
+
path: "character.sprite.tagIds",
|
|
5718
|
+
details: {
|
|
5719
|
+
characterId,
|
|
5720
|
+
spriteId,
|
|
5721
|
+
},
|
|
5722
|
+
errorFactory: createInvariantValidationError,
|
|
5723
|
+
});
|
|
5724
|
+
if (!tagResult.valid) {
|
|
5725
|
+
return tagResult;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
|
|
5033
5729
|
if (sprite.thumbnailFileId === undefined) {
|
|
5034
5730
|
continue;
|
|
5035
5731
|
}
|
|
@@ -5051,6 +5747,30 @@ export const assertInvariants = ({ state }) => {
|
|
|
5051
5747
|
}
|
|
5052
5748
|
}
|
|
5053
5749
|
|
|
5750
|
+
for (const [transformId, transform] of Object.entries(
|
|
5751
|
+
state.transforms.items,
|
|
5752
|
+
)) {
|
|
5753
|
+
if (transform.type !== "transform") {
|
|
5754
|
+
continue;
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5757
|
+
{
|
|
5758
|
+
const result = validateTagIdsAgainstScope({
|
|
5759
|
+
state,
|
|
5760
|
+
tagIds: transform.tagIds,
|
|
5761
|
+
scopeKey: "transforms",
|
|
5762
|
+
path: "transform.tagIds",
|
|
5763
|
+
details: {
|
|
5764
|
+
transformId,
|
|
5765
|
+
},
|
|
5766
|
+
errorFactory: createInvariantValidationError,
|
|
5767
|
+
});
|
|
5768
|
+
if (!result.valid) {
|
|
5769
|
+
return result;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
|
|
5054
5774
|
for (const [layoutId, layout] of Object.entries(state.layouts.items)) {
|
|
5055
5775
|
if (layout.type !== "layout" || layout.thumbnailFileId === undefined) {
|
|
5056
5776
|
continue;
|
|
@@ -5362,7 +6082,7 @@ export const assertInvariants = ({ state }) => {
|
|
|
5362
6082
|
!isLayoutConditionTarget(state, rule.when.target)
|
|
5363
6083
|
) {
|
|
5364
6084
|
return invalidInvariant(
|
|
5365
|
-
`${ownerLabel} element conditionalOverrides when target must reference an existing variable or supported
|
|
6085
|
+
`${ownerLabel} element conditionalOverrides when target must reference an existing variable or supported layout condition`,
|
|
5366
6086
|
{
|
|
5367
6087
|
[ownerIdField]: ownerId,
|
|
5368
6088
|
elementId,
|
|
@@ -5507,6 +6227,18 @@ const runValidateState = ({ state }) => {
|
|
|
5507
6227
|
);
|
|
5508
6228
|
}
|
|
5509
6229
|
|
|
6230
|
+
{
|
|
6231
|
+
const result = validateTagsRoot({
|
|
6232
|
+
state: normalizedState,
|
|
6233
|
+
tags: normalizedState.tags,
|
|
6234
|
+
path: "state.tags",
|
|
6235
|
+
errorFactory: createStateValidationError,
|
|
6236
|
+
});
|
|
6237
|
+
if (result?.valid === false) {
|
|
6238
|
+
return result;
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
|
|
5510
6242
|
for (const collectionKey of COLLECTION_KEYS) {
|
|
5511
6243
|
{
|
|
5512
6244
|
const result = validateCollection({
|
|
@@ -5722,6 +6454,46 @@ const validateRequiredUniqueIdArray = ({ value, path, errorFactory }) => {
|
|
|
5722
6454
|
}
|
|
5723
6455
|
};
|
|
5724
6456
|
|
|
6457
|
+
const validateOptionalUniqueIdArray = ({
|
|
6458
|
+
value,
|
|
6459
|
+
path,
|
|
6460
|
+
errorFactory,
|
|
6461
|
+
allowEmpty = true,
|
|
6462
|
+
}) => {
|
|
6463
|
+
if (value === undefined) {
|
|
6464
|
+
return VALID_RESULT;
|
|
6465
|
+
}
|
|
6466
|
+
|
|
6467
|
+
if (!Array.isArray(value) || (!allowEmpty && value.length === 0)) {
|
|
6468
|
+
return invalidFromErrorFactory(
|
|
6469
|
+
errorFactory,
|
|
6470
|
+
allowEmpty
|
|
6471
|
+
? `${path} must be an array when provided`
|
|
6472
|
+
: `${path} must be a non-empty array when provided`,
|
|
6473
|
+
);
|
|
6474
|
+
}
|
|
6475
|
+
|
|
6476
|
+
const seen = new Set();
|
|
6477
|
+
|
|
6478
|
+
for (const [index, entry] of value.entries()) {
|
|
6479
|
+
if (!isNonEmptyString(entry)) {
|
|
6480
|
+
return invalidFromErrorFactory(
|
|
6481
|
+
errorFactory,
|
|
6482
|
+
`${path}[${index}] must be a non-empty string`,
|
|
6483
|
+
);
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
if (seen.has(entry)) {
|
|
6487
|
+
return invalidFromErrorFactory(
|
|
6488
|
+
errorFactory,
|
|
6489
|
+
`${path}[${index}] must be unique`,
|
|
6490
|
+
);
|
|
6491
|
+
}
|
|
6492
|
+
|
|
6493
|
+
seen.add(entry);
|
|
6494
|
+
}
|
|
6495
|
+
};
|
|
6496
|
+
|
|
5725
6497
|
const validateSectionCreateData = ({ data, errorFactory }) => {
|
|
5726
6498
|
{
|
|
5727
6499
|
const result = validateExactKeys({
|
|
@@ -5789,49 +6561,134 @@ const validateLineCreatePayload = ({ payload, errorFactory }) => {
|
|
|
5789
6561
|
}
|
|
5790
6562
|
}
|
|
5791
6563
|
|
|
5792
|
-
if (!isNonEmptyString(item.lineId)) {
|
|
6564
|
+
if (!isNonEmptyString(item.lineId)) {
|
|
6565
|
+
return invalidFromErrorFactory(
|
|
6566
|
+
errorFactory,
|
|
6567
|
+
`${itemPath}.lineId must be a non-empty string`,
|
|
6568
|
+
);
|
|
6569
|
+
}
|
|
6570
|
+
|
|
6571
|
+
if (seenLineIds.has(item.lineId)) {
|
|
6572
|
+
return invalidFromErrorFactory(
|
|
6573
|
+
errorFactory,
|
|
6574
|
+
`${itemPath}.lineId must be unique`,
|
|
6575
|
+
);
|
|
6576
|
+
}
|
|
6577
|
+
seenLineIds.add(item.lineId);
|
|
6578
|
+
|
|
6579
|
+
{
|
|
6580
|
+
const result = validateAllowedKeys({
|
|
6581
|
+
value: item.data,
|
|
6582
|
+
allowedKeys: ["actions"],
|
|
6583
|
+
path: `${itemPath}.data`,
|
|
6584
|
+
errorFactory,
|
|
6585
|
+
});
|
|
6586
|
+
if (result?.valid === false) {
|
|
6587
|
+
return result;
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
|
|
6591
|
+
if (item.data.actions !== undefined && !isPlainObject(item.data.actions)) {
|
|
6592
|
+
return invalidFromErrorFactory(
|
|
6593
|
+
errorFactory,
|
|
6594
|
+
`${itemPath}.data.actions must be an object`,
|
|
6595
|
+
);
|
|
6596
|
+
}
|
|
6597
|
+
}
|
|
6598
|
+
};
|
|
6599
|
+
|
|
6600
|
+
const validateLineUpdateActionsData = ({ data, errorFactory }) => {
|
|
6601
|
+
if (!isPlainObject(data)) {
|
|
6602
|
+
return invalidFromErrorFactory(
|
|
6603
|
+
errorFactory,
|
|
6604
|
+
"payload.data must be an object",
|
|
6605
|
+
);
|
|
6606
|
+
}
|
|
6607
|
+
};
|
|
6608
|
+
|
|
6609
|
+
const validateLineUpdateActionsPreserve = ({
|
|
6610
|
+
preserve,
|
|
6611
|
+
data,
|
|
6612
|
+
replace,
|
|
6613
|
+
errorFactory,
|
|
6614
|
+
}) => {
|
|
6615
|
+
if (preserve === undefined) {
|
|
6616
|
+
return VALID_RESULT;
|
|
6617
|
+
}
|
|
6618
|
+
|
|
6619
|
+
if (replace === true) {
|
|
6620
|
+
return invalidFromErrorFactory(
|
|
6621
|
+
errorFactory,
|
|
6622
|
+
"payload.preserve is only supported when payload.replace is not true",
|
|
6623
|
+
);
|
|
6624
|
+
}
|
|
6625
|
+
|
|
6626
|
+
if (!Array.isArray(preserve)) {
|
|
6627
|
+
return invalidFromErrorFactory(
|
|
6628
|
+
errorFactory,
|
|
6629
|
+
"payload.preserve must be an array when provided",
|
|
6630
|
+
);
|
|
6631
|
+
}
|
|
6632
|
+
|
|
6633
|
+
const seen = new Set();
|
|
6634
|
+
for (let index = 0; index < preserve.length; index += 1) {
|
|
6635
|
+
const path = preserve[index];
|
|
6636
|
+
const preservePath = `payload.preserve[${index}]`;
|
|
6637
|
+
|
|
6638
|
+
if (!isNonEmptyString(path)) {
|
|
5793
6639
|
return invalidFromErrorFactory(
|
|
5794
6640
|
errorFactory,
|
|
5795
|
-
`${
|
|
6641
|
+
`${preservePath} must be a non-empty string`,
|
|
5796
6642
|
);
|
|
5797
6643
|
}
|
|
5798
6644
|
|
|
5799
|
-
if (
|
|
6645
|
+
if (!LINE_UPDATE_ACTIONS_PRESERVE_PATHS_SET.has(path)) {
|
|
5800
6646
|
return invalidFromErrorFactory(
|
|
5801
6647
|
errorFactory,
|
|
5802
|
-
`${
|
|
6648
|
+
`${preservePath} must be one of: ${LINE_UPDATE_ACTIONS_PRESERVE_PATHS.join(", ")}`,
|
|
5803
6649
|
);
|
|
5804
6650
|
}
|
|
5805
|
-
seenLineIds.add(item.lineId);
|
|
5806
6651
|
|
|
5807
|
-
{
|
|
5808
|
-
|
|
5809
|
-
value: item.data,
|
|
5810
|
-
allowedKeys: ["actions"],
|
|
5811
|
-
path: `${itemPath}.data`,
|
|
6652
|
+
if (seen.has(path)) {
|
|
6653
|
+
return invalidFromErrorFactory(
|
|
5812
6654
|
errorFactory,
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
return result;
|
|
5816
|
-
}
|
|
6655
|
+
`${preservePath} must not duplicate another preserve path`,
|
|
6656
|
+
);
|
|
5817
6657
|
}
|
|
6658
|
+
seen.add(path);
|
|
5818
6659
|
|
|
5819
|
-
if (
|
|
6660
|
+
if (path === "dialogue.content" && !isPlainObject(data?.dialogue)) {
|
|
5820
6661
|
return invalidFromErrorFactory(
|
|
5821
6662
|
errorFactory,
|
|
5822
|
-
|
|
6663
|
+
"payload.data.dialogue must be an object when preserving dialogue.content",
|
|
5823
6664
|
);
|
|
5824
6665
|
}
|
|
5825
6666
|
}
|
|
6667
|
+
|
|
6668
|
+
return VALID_RESULT;
|
|
5826
6669
|
};
|
|
5827
6670
|
|
|
5828
|
-
const
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
6671
|
+
const applyLineUpdateActionsPreserve = ({ currentActions, data, preserve }) => {
|
|
6672
|
+
const nextData = structuredClone(data || {});
|
|
6673
|
+
if (!Array.isArray(preserve) || preserve.length === 0) {
|
|
6674
|
+
return nextData;
|
|
6675
|
+
}
|
|
6676
|
+
|
|
6677
|
+
if (
|
|
6678
|
+
preserve.includes("dialogue.content") &&
|
|
6679
|
+
isPlainObject(nextData.dialogue) &&
|
|
6680
|
+
!Object.hasOwn(nextData.dialogue, "content")
|
|
6681
|
+
) {
|
|
6682
|
+
const currentContent = currentActions?.dialogue?.content;
|
|
6683
|
+
if (currentContent !== undefined) {
|
|
6684
|
+
nextData.dialogue = {
|
|
6685
|
+
...structuredClone(nextData.dialogue),
|
|
6686
|
+
content: structuredClone(currentContent),
|
|
6687
|
+
};
|
|
6688
|
+
}
|
|
5834
6689
|
}
|
|
6690
|
+
|
|
6691
|
+
return nextData;
|
|
5835
6692
|
};
|
|
5836
6693
|
|
|
5837
6694
|
const validateImageCreateData = ({ data, errorFactory }) => {
|
|
@@ -5859,6 +6716,7 @@ const validateImageCreateData = ({ data, errorFactory }) => {
|
|
|
5859
6716
|
"type",
|
|
5860
6717
|
"name",
|
|
5861
6718
|
"description",
|
|
6719
|
+
"tagIds",
|
|
5862
6720
|
"thumbnailFileId",
|
|
5863
6721
|
"fileId",
|
|
5864
6722
|
"width",
|
|
@@ -5887,6 +6745,17 @@ const validateImageCreateData = ({ data, errorFactory }) => {
|
|
|
5887
6745
|
}
|
|
5888
6746
|
|
|
5889
6747
|
if (data.type === "image") {
|
|
6748
|
+
{
|
|
6749
|
+
const result = validateOptionalUniqueIdArray({
|
|
6750
|
+
value: data.tagIds,
|
|
6751
|
+
path: "payload.data.tagIds",
|
|
6752
|
+
errorFactory,
|
|
6753
|
+
});
|
|
6754
|
+
if (result?.valid === false) {
|
|
6755
|
+
return result;
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
|
|
5890
6759
|
if (
|
|
5891
6760
|
data.thumbnailFileId !== undefined &&
|
|
5892
6761
|
!isNonEmptyString(data.thumbnailFileId)
|
|
@@ -5927,6 +6796,7 @@ const validateImageUpdateData = ({ data, errorFactory }) => {
|
|
|
5927
6796
|
allowedKeys: [
|
|
5928
6797
|
"name",
|
|
5929
6798
|
"description",
|
|
6799
|
+
"tagIds",
|
|
5930
6800
|
"thumbnailFileId",
|
|
5931
6801
|
"fileId",
|
|
5932
6802
|
"width",
|
|
@@ -5961,6 +6831,17 @@ const validateImageUpdateData = ({ data, errorFactory }) => {
|
|
|
5961
6831
|
);
|
|
5962
6832
|
}
|
|
5963
6833
|
|
|
6834
|
+
{
|
|
6835
|
+
const result = validateOptionalUniqueIdArray({
|
|
6836
|
+
value: data.tagIds,
|
|
6837
|
+
path: "payload.data.tagIds",
|
|
6838
|
+
errorFactory,
|
|
6839
|
+
});
|
|
6840
|
+
if (result?.valid === false) {
|
|
6841
|
+
return result;
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
6844
|
+
|
|
5964
6845
|
if (
|
|
5965
6846
|
data.thumbnailFileId !== undefined &&
|
|
5966
6847
|
!isNonEmptyString(data.thumbnailFileId)
|
|
@@ -6225,6 +7106,7 @@ const validateSoundCreateData = ({ data, errorFactory }) => {
|
|
|
6225
7106
|
"type",
|
|
6226
7107
|
"name",
|
|
6227
7108
|
"description",
|
|
7109
|
+
"tagIds",
|
|
6228
7110
|
"fileId",
|
|
6229
7111
|
"waveformDataFileId",
|
|
6230
7112
|
"duration",
|
|
@@ -6252,6 +7134,17 @@ const validateSoundCreateData = ({ data, errorFactory }) => {
|
|
|
6252
7134
|
}
|
|
6253
7135
|
|
|
6254
7136
|
if (data.type === "sound") {
|
|
7137
|
+
{
|
|
7138
|
+
const result = validateOptionalUniqueIdArray({
|
|
7139
|
+
value: data.tagIds,
|
|
7140
|
+
path: "payload.data.tagIds",
|
|
7141
|
+
errorFactory,
|
|
7142
|
+
});
|
|
7143
|
+
if (result?.valid === false) {
|
|
7144
|
+
return result;
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
|
|
6255
7148
|
if (!isNonEmptyString(data.fileId)) {
|
|
6256
7149
|
return invalidFromErrorFactory(
|
|
6257
7150
|
errorFactory,
|
|
@@ -6286,6 +7179,7 @@ const validateSoundUpdateData = ({ data, errorFactory }) => {
|
|
|
6286
7179
|
allowedKeys: [
|
|
6287
7180
|
"name",
|
|
6288
7181
|
"description",
|
|
7182
|
+
"tagIds",
|
|
6289
7183
|
"fileId",
|
|
6290
7184
|
"waveformDataFileId",
|
|
6291
7185
|
"duration",
|
|
@@ -6319,6 +7213,17 @@ const validateSoundUpdateData = ({ data, errorFactory }) => {
|
|
|
6319
7213
|
);
|
|
6320
7214
|
}
|
|
6321
7215
|
|
|
7216
|
+
{
|
|
7217
|
+
const result = validateOptionalUniqueIdArray({
|
|
7218
|
+
value: data.tagIds,
|
|
7219
|
+
path: "payload.data.tagIds",
|
|
7220
|
+
errorFactory,
|
|
7221
|
+
});
|
|
7222
|
+
if (result?.valid === false) {
|
|
7223
|
+
return result;
|
|
7224
|
+
}
|
|
7225
|
+
}
|
|
7226
|
+
|
|
6322
7227
|
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
6323
7228
|
return invalidFromErrorFactory(
|
|
6324
7229
|
errorFactory,
|
|
@@ -6370,6 +7275,7 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
6370
7275
|
"type",
|
|
6371
7276
|
"name",
|
|
6372
7277
|
"description",
|
|
7278
|
+
"tagIds",
|
|
6373
7279
|
"fileId",
|
|
6374
7280
|
"thumbnailFileId",
|
|
6375
7281
|
"duration",
|
|
@@ -6399,6 +7305,17 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
6399
7305
|
}
|
|
6400
7306
|
|
|
6401
7307
|
if (data.type === "video") {
|
|
7308
|
+
{
|
|
7309
|
+
const result = validateOptionalUniqueIdArray({
|
|
7310
|
+
value: data.tagIds,
|
|
7311
|
+
path: "payload.data.tagIds",
|
|
7312
|
+
errorFactory,
|
|
7313
|
+
});
|
|
7314
|
+
if (result?.valid === false) {
|
|
7315
|
+
return result;
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
|
|
6402
7319
|
if (!isNonEmptyString(data.fileId)) {
|
|
6403
7320
|
return invalidFromErrorFactory(
|
|
6404
7321
|
errorFactory,
|
|
@@ -6443,6 +7360,7 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
6443
7360
|
allowedKeys: [
|
|
6444
7361
|
"name",
|
|
6445
7362
|
"description",
|
|
7363
|
+
"tagIds",
|
|
6446
7364
|
"fileId",
|
|
6447
7365
|
"thumbnailFileId",
|
|
6448
7366
|
"duration",
|
|
@@ -6478,6 +7396,17 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
6478
7396
|
);
|
|
6479
7397
|
}
|
|
6480
7398
|
|
|
7399
|
+
{
|
|
7400
|
+
const result = validateOptionalUniqueIdArray({
|
|
7401
|
+
value: data.tagIds,
|
|
7402
|
+
path: "payload.data.tagIds",
|
|
7403
|
+
errorFactory,
|
|
7404
|
+
});
|
|
7405
|
+
if (result?.valid === false) {
|
|
7406
|
+
return result;
|
|
7407
|
+
}
|
|
7408
|
+
}
|
|
7409
|
+
|
|
6481
7410
|
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
6482
7411
|
return invalidFromErrorFactory(
|
|
6483
7412
|
errorFactory,
|
|
@@ -6965,6 +7894,7 @@ const validateTransformCreateData = ({ data, errorFactory }) => {
|
|
|
6965
7894
|
"type",
|
|
6966
7895
|
"name",
|
|
6967
7896
|
"description",
|
|
7897
|
+
"tagIds",
|
|
6968
7898
|
"x",
|
|
6969
7899
|
"y",
|
|
6970
7900
|
"scaleX",
|
|
@@ -6996,6 +7926,17 @@ const validateTransformCreateData = ({ data, errorFactory }) => {
|
|
|
6996
7926
|
}
|
|
6997
7927
|
|
|
6998
7928
|
if (data.type === "transform") {
|
|
7929
|
+
{
|
|
7930
|
+
const result = validateOptionalUniqueIdArray({
|
|
7931
|
+
value: data.tagIds,
|
|
7932
|
+
path: "payload.data.tagIds",
|
|
7933
|
+
errorFactory,
|
|
7934
|
+
});
|
|
7935
|
+
if (result?.valid === false) {
|
|
7936
|
+
return result;
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
|
|
6999
7940
|
for (const key of [
|
|
7000
7941
|
"x",
|
|
7001
7942
|
"y",
|
|
@@ -7201,6 +8142,7 @@ const validateTransformUpdateData = ({ data, errorFactory }) => {
|
|
|
7201
8142
|
allowedKeys: [
|
|
7202
8143
|
"name",
|
|
7203
8144
|
"description",
|
|
8145
|
+
"tagIds",
|
|
7204
8146
|
"x",
|
|
7205
8147
|
"y",
|
|
7206
8148
|
"scaleX",
|
|
@@ -7238,6 +8180,17 @@ const validateTransformUpdateData = ({ data, errorFactory }) => {
|
|
|
7238
8180
|
);
|
|
7239
8181
|
}
|
|
7240
8182
|
|
|
8183
|
+
{
|
|
8184
|
+
const result = validateOptionalUniqueIdArray({
|
|
8185
|
+
value: data.tagIds,
|
|
8186
|
+
path: "payload.data.tagIds",
|
|
8187
|
+
errorFactory,
|
|
8188
|
+
});
|
|
8189
|
+
if (result?.valid === false) {
|
|
8190
|
+
return result;
|
|
8191
|
+
}
|
|
8192
|
+
}
|
|
8193
|
+
|
|
7241
8194
|
for (const key of [
|
|
7242
8195
|
"x",
|
|
7243
8196
|
"y",
|
|
@@ -7591,6 +8544,7 @@ const validateCharacterSpriteCreateData = ({ data, errorFactory }) => {
|
|
|
7591
8544
|
"type",
|
|
7592
8545
|
"name",
|
|
7593
8546
|
"description",
|
|
8547
|
+
"tagIds",
|
|
7594
8548
|
"fileId",
|
|
7595
8549
|
"thumbnailFileId",
|
|
7596
8550
|
"width",
|
|
@@ -7619,6 +8573,17 @@ const validateCharacterSpriteCreateData = ({ data, errorFactory }) => {
|
|
|
7619
8573
|
}
|
|
7620
8574
|
|
|
7621
8575
|
if (data.type === "image") {
|
|
8576
|
+
{
|
|
8577
|
+
const result = validateOptionalUniqueIdArray({
|
|
8578
|
+
value: data.tagIds,
|
|
8579
|
+
path: "payload.data.tagIds",
|
|
8580
|
+
errorFactory,
|
|
8581
|
+
});
|
|
8582
|
+
if (result?.valid === false) {
|
|
8583
|
+
return result;
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8586
|
+
|
|
7622
8587
|
if (!isNonEmptyString(data.fileId)) {
|
|
7623
8588
|
return invalidFromErrorFactory(
|
|
7624
8589
|
errorFactory,
|
|
@@ -7659,6 +8624,7 @@ const validateCharacterSpriteUpdateData = ({ data, errorFactory }) => {
|
|
|
7659
8624
|
allowedKeys: [
|
|
7660
8625
|
"name",
|
|
7661
8626
|
"description",
|
|
8627
|
+
"tagIds",
|
|
7662
8628
|
"fileId",
|
|
7663
8629
|
"thumbnailFileId",
|
|
7664
8630
|
"width",
|
|
@@ -7686,6 +8652,17 @@ const validateCharacterSpriteUpdateData = ({ data, errorFactory }) => {
|
|
|
7686
8652
|
);
|
|
7687
8653
|
}
|
|
7688
8654
|
|
|
8655
|
+
{
|
|
8656
|
+
const result = validateOptionalUniqueIdArray({
|
|
8657
|
+
value: data.tagIds,
|
|
8658
|
+
path: "payload.data.tagIds",
|
|
8659
|
+
errorFactory,
|
|
8660
|
+
});
|
|
8661
|
+
if (result?.valid === false) {
|
|
8662
|
+
return result;
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8665
|
+
|
|
7689
8666
|
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
7690
8667
|
return invalidFromErrorFactory(
|
|
7691
8668
|
errorFactory,
|
|
@@ -7750,6 +8727,7 @@ const validateCharacterCreateData = ({ data, errorFactory }) => {
|
|
|
7750
8727
|
"type",
|
|
7751
8728
|
"name",
|
|
7752
8729
|
"description",
|
|
8730
|
+
"tagIds",
|
|
7753
8731
|
"shortcut",
|
|
7754
8732
|
"fileId",
|
|
7755
8733
|
"sprites",
|
|
@@ -7777,6 +8755,17 @@ const validateCharacterCreateData = ({ data, errorFactory }) => {
|
|
|
7777
8755
|
}
|
|
7778
8756
|
|
|
7779
8757
|
if (data.type === "character") {
|
|
8758
|
+
{
|
|
8759
|
+
const result = validateOptionalUniqueIdArray({
|
|
8760
|
+
value: data.tagIds,
|
|
8761
|
+
path: "payload.data.tagIds",
|
|
8762
|
+
errorFactory,
|
|
8763
|
+
});
|
|
8764
|
+
if (result?.valid === false) {
|
|
8765
|
+
return result;
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
|
|
7780
8769
|
if (data.shortcut !== undefined && !isString(data.shortcut)) {
|
|
7781
8770
|
return invalidFromErrorFactory(
|
|
7782
8771
|
errorFactory,
|
|
@@ -7796,7 +8785,13 @@ const validateCharacterCreateData = ({ data, errorFactory }) => {
|
|
|
7796
8785
|
const result = validateNestedCollection({
|
|
7797
8786
|
collection: data.sprites,
|
|
7798
8787
|
path: "payload.data.sprites",
|
|
7799
|
-
itemValidator:
|
|
8788
|
+
itemValidator: ({ items, path, errorFactory }) =>
|
|
8789
|
+
validateCharacterSpriteItems({
|
|
8790
|
+
items,
|
|
8791
|
+
path,
|
|
8792
|
+
errorFactory,
|
|
8793
|
+
allowTagIds: false,
|
|
8794
|
+
}),
|
|
7800
8795
|
treeValidator: validateGenericFolderOwnership,
|
|
7801
8796
|
folderLabel: "folder sprite item",
|
|
7802
8797
|
errorFactory,
|
|
@@ -7816,6 +8811,7 @@ const validateCharacterUpdateData = ({ data, errorFactory }) => {
|
|
|
7816
8811
|
allowedKeys: [
|
|
7817
8812
|
"name",
|
|
7818
8813
|
"description",
|
|
8814
|
+
"tagIds",
|
|
7819
8815
|
"shortcut",
|
|
7820
8816
|
"fileId",
|
|
7821
8817
|
],
|
|
@@ -7855,6 +8851,17 @@ const validateCharacterUpdateData = ({ data, errorFactory }) => {
|
|
|
7855
8851
|
);
|
|
7856
8852
|
}
|
|
7857
8853
|
|
|
8854
|
+
{
|
|
8855
|
+
const result = validateOptionalUniqueIdArray({
|
|
8856
|
+
value: data.tagIds,
|
|
8857
|
+
path: "payload.data.tagIds",
|
|
8858
|
+
errorFactory,
|
|
8859
|
+
});
|
|
8860
|
+
if (result?.valid === false) {
|
|
8861
|
+
return result;
|
|
8862
|
+
}
|
|
8863
|
+
}
|
|
8864
|
+
|
|
7858
8865
|
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
7859
8866
|
return invalidFromErrorFactory(
|
|
7860
8867
|
errorFactory,
|
|
@@ -8527,7 +9534,7 @@ const validateVisualElementReferenceTargets = ({
|
|
|
8527
9534
|
if (!isLayoutConditionTarget(state, rule?.when?.target)) {
|
|
8528
9535
|
return invalidFromErrorFactory(
|
|
8529
9536
|
errorFactory,
|
|
8530
|
-
`${ownerLabel} element conditionalOverrides.${index}.when.target must reference an existing variable or supported
|
|
9537
|
+
`${ownerLabel} element conditionalOverrides.${index}.when.target must reference an existing variable or supported layout condition`,
|
|
8531
9538
|
{
|
|
8532
9539
|
[ownerIdField]: ownerId,
|
|
8533
9540
|
elementId,
|
|
@@ -8701,6 +9708,7 @@ const createFolderedCollectionCommandDefinitions = ({
|
|
|
8701
9708
|
validateCreateState = () => {},
|
|
8702
9709
|
validateUpdateState = () => {},
|
|
8703
9710
|
validateDeleteState = () => {},
|
|
9711
|
+
afterDelete = () => {},
|
|
8704
9712
|
includeUpdate = true,
|
|
8705
9713
|
}) => {
|
|
8706
9714
|
const existingMessage = `payload.${idField} must reference an existing ${itemLabel}`;
|
|
@@ -8948,6 +9956,7 @@ const createFolderedCollectionCommandDefinitions = ({
|
|
|
8948
9956
|
},
|
|
8949
9957
|
reduce: ({ state, payload }) => {
|
|
8950
9958
|
const deletedIds = new Set();
|
|
9959
|
+
const deletedItemsById = new Map();
|
|
8951
9960
|
|
|
8952
9961
|
for (const itemId of payload[deleteArrayField]) {
|
|
8953
9962
|
const removedNode = removeTreeNode({
|
|
@@ -8963,6 +9972,10 @@ const createFolderedCollectionCommandDefinitions = ({
|
|
|
8963
9972
|
node: removedNode,
|
|
8964
9973
|
})) {
|
|
8965
9974
|
deletedIds.add(descendantId);
|
|
9975
|
+
deletedItemsById.set(
|
|
9976
|
+
descendantId,
|
|
9977
|
+
state[collectionKey].items[descendantId],
|
|
9978
|
+
);
|
|
8966
9979
|
}
|
|
8967
9980
|
}
|
|
8968
9981
|
|
|
@@ -8970,6 +9983,13 @@ const createFolderedCollectionCommandDefinitions = ({
|
|
|
8970
9983
|
delete state[collectionKey].items[itemId];
|
|
8971
9984
|
}
|
|
8972
9985
|
|
|
9986
|
+
afterDelete({
|
|
9987
|
+
state,
|
|
9988
|
+
payload,
|
|
9989
|
+
deletedIds,
|
|
9990
|
+
deletedItemsById,
|
|
9991
|
+
});
|
|
9992
|
+
|
|
8973
9993
|
return state;
|
|
8974
9994
|
},
|
|
8975
9995
|
},
|
|
@@ -9300,7 +10320,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
9300
10320
|
}
|
|
9301
10321
|
},
|
|
9302
10322
|
validateAgainstState: () => {},
|
|
9303
|
-
reduce: ({ payload }) =>
|
|
10323
|
+
reduce: ({ payload }) =>
|
|
10324
|
+
structuredClone(normalizeStateCollections(payload.state)),
|
|
9304
10325
|
},
|
|
9305
10326
|
...createFolderedCollectionCommandDefinitions({
|
|
9306
10327
|
familyName: "file",
|
|
@@ -10445,7 +11466,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
10445
11466
|
{
|
|
10446
11467
|
const result = validateAllowedKeys({
|
|
10447
11468
|
value: payload,
|
|
10448
|
-
allowedKeys: ["lineId", "data", "replace"],
|
|
11469
|
+
allowedKeys: ["lineId", "data", "replace", "preserve"],
|
|
10449
11470
|
path: "payload",
|
|
10450
11471
|
errorFactory: createPayloadValidationError,
|
|
10451
11472
|
});
|
|
@@ -10476,6 +11497,18 @@ const COMMAND_DEFINITIONS = [
|
|
|
10476
11497
|
"payload.replace must be a boolean when provided",
|
|
10477
11498
|
);
|
|
10478
11499
|
}
|
|
11500
|
+
|
|
11501
|
+
{
|
|
11502
|
+
const result = validateLineUpdateActionsPreserve({
|
|
11503
|
+
preserve: payload.preserve,
|
|
11504
|
+
data: payload.data,
|
|
11505
|
+
replace: payload.replace,
|
|
11506
|
+
errorFactory: createPayloadValidationError,
|
|
11507
|
+
});
|
|
11508
|
+
if (result?.valid === false) {
|
|
11509
|
+
return result;
|
|
11510
|
+
}
|
|
11511
|
+
}
|
|
10479
11512
|
},
|
|
10480
11513
|
validateAgainstState: ({ state, payload }) => {
|
|
10481
11514
|
if (!findLineLocation({ state, lineId: payload.lineId })) {
|
|
@@ -10487,12 +11520,17 @@ const COMMAND_DEFINITIONS = [
|
|
|
10487
11520
|
reduce: ({ state, payload }) => {
|
|
10488
11521
|
const location = findLineLocation({ state, lineId: payload.lineId });
|
|
10489
11522
|
const line = location.line;
|
|
11523
|
+
const nextData = applyLineUpdateActionsPreserve({
|
|
11524
|
+
currentActions: line.actions,
|
|
11525
|
+
data: payload.data,
|
|
11526
|
+
preserve: payload.preserve,
|
|
11527
|
+
});
|
|
10490
11528
|
line.actions =
|
|
10491
11529
|
payload.replace === true
|
|
10492
|
-
? structuredClone(
|
|
11530
|
+
? structuredClone(nextData)
|
|
10493
11531
|
: {
|
|
10494
11532
|
...structuredClone(line.actions),
|
|
10495
|
-
...structuredClone(
|
|
11533
|
+
...structuredClone(nextData),
|
|
10496
11534
|
};
|
|
10497
11535
|
return state;
|
|
10498
11536
|
},
|
|
@@ -10775,6 +11813,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
10775
11813
|
if (!result.valid) {
|
|
10776
11814
|
return result;
|
|
10777
11815
|
}
|
|
11816
|
+
|
|
11817
|
+
return validateTagIdsAgainstScope({
|
|
11818
|
+
state,
|
|
11819
|
+
tagIds: payload.data.tagIds,
|
|
11820
|
+
scopeKey: "images",
|
|
11821
|
+
path: "payload.data.tagIds",
|
|
11822
|
+
details: {
|
|
11823
|
+
imageId: payload.imageId,
|
|
11824
|
+
},
|
|
11825
|
+
});
|
|
10778
11826
|
}
|
|
10779
11827
|
},
|
|
10780
11828
|
reduce: ({ state, payload }) => {
|
|
@@ -10799,6 +11847,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
10799
11847
|
if (payload.data.height !== undefined) {
|
|
10800
11848
|
nextImage.height = payload.data.height;
|
|
10801
11849
|
}
|
|
11850
|
+
|
|
11851
|
+
assignOptionalTagIds({
|
|
11852
|
+
target: nextImage,
|
|
11853
|
+
tagIds: payload.data.tagIds,
|
|
11854
|
+
});
|
|
10802
11855
|
}
|
|
10803
11856
|
|
|
10804
11857
|
state.images.items[payload.imageId] = nextImage;
|
|
@@ -10860,7 +11913,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
10860
11913
|
(payload.data.fileId !== undefined ||
|
|
10861
11914
|
payload.data.thumbnailFileId !== undefined ||
|
|
10862
11915
|
payload.data.width !== undefined ||
|
|
10863
|
-
payload.data.height !== undefined
|
|
11916
|
+
payload.data.height !== undefined ||
|
|
11917
|
+
payload.data.tagIds !== undefined)
|
|
10864
11918
|
) {
|
|
10865
11919
|
return invalidPrecondition(
|
|
10866
11920
|
"folder image items cannot update file fields",
|
|
@@ -10879,14 +11933,24 @@ const COMMAND_DEFINITIONS = [
|
|
|
10879
11933
|
if (!result.valid) {
|
|
10880
11934
|
return result;
|
|
10881
11935
|
}
|
|
11936
|
+
|
|
11937
|
+
return validateTagIdsAgainstScope({
|
|
11938
|
+
state,
|
|
11939
|
+
tagIds: payload.data.tagIds,
|
|
11940
|
+
scopeKey: "images",
|
|
11941
|
+
path: "payload.data.tagIds",
|
|
11942
|
+
details: {
|
|
11943
|
+
imageId: payload.imageId,
|
|
11944
|
+
},
|
|
11945
|
+
});
|
|
10882
11946
|
}
|
|
10883
11947
|
},
|
|
10884
11948
|
reduce: ({ state, payload }) => {
|
|
10885
11949
|
const currentImage = state.images.items[payload.imageId];
|
|
10886
|
-
state.images.items[payload.imageId] = {
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
};
|
|
11950
|
+
state.images.items[payload.imageId] = applyTagIdsUpdate({
|
|
11951
|
+
currentItem: currentImage,
|
|
11952
|
+
data: payload.data,
|
|
11953
|
+
});
|
|
10890
11954
|
return state;
|
|
10891
11955
|
},
|
|
10892
11956
|
},
|
|
@@ -11192,6 +12256,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
11192
12256
|
if (!result.valid) {
|
|
11193
12257
|
return result;
|
|
11194
12258
|
}
|
|
12259
|
+
|
|
12260
|
+
return validateTagIdsAgainstScope({
|
|
12261
|
+
state,
|
|
12262
|
+
tagIds: payload.data.tagIds,
|
|
12263
|
+
scopeKey: "sounds",
|
|
12264
|
+
path: "payload.data.tagIds",
|
|
12265
|
+
details: {
|
|
12266
|
+
soundId: payload.soundId,
|
|
12267
|
+
},
|
|
12268
|
+
});
|
|
11195
12269
|
}
|
|
11196
12270
|
},
|
|
11197
12271
|
reduce: ({ state, payload }) => {
|
|
@@ -11213,6 +12287,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
11213
12287
|
if (payload.data.duration !== undefined) {
|
|
11214
12288
|
nextSound.duration = payload.data.duration;
|
|
11215
12289
|
}
|
|
12290
|
+
|
|
12291
|
+
assignOptionalTagIds({
|
|
12292
|
+
target: nextSound,
|
|
12293
|
+
tagIds: payload.data.tagIds,
|
|
12294
|
+
});
|
|
11216
12295
|
}
|
|
11217
12296
|
|
|
11218
12297
|
state.sounds.items[payload.soundId] = nextSound;
|
|
@@ -11273,7 +12352,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
11273
12352
|
currentSound.type === "folder" &&
|
|
11274
12353
|
(payload.data.fileId !== undefined ||
|
|
11275
12354
|
payload.data.waveformDataFileId !== undefined ||
|
|
11276
|
-
payload.data.duration !== undefined
|
|
12355
|
+
payload.data.duration !== undefined ||
|
|
12356
|
+
payload.data.tagIds !== undefined)
|
|
11277
12357
|
) {
|
|
11278
12358
|
return invalidPrecondition(
|
|
11279
12359
|
"folder sound items cannot update file fields",
|
|
@@ -11293,14 +12373,24 @@ const COMMAND_DEFINITIONS = [
|
|
|
11293
12373
|
if (!result.valid) {
|
|
11294
12374
|
return result;
|
|
11295
12375
|
}
|
|
12376
|
+
|
|
12377
|
+
return validateTagIdsAgainstScope({
|
|
12378
|
+
state,
|
|
12379
|
+
tagIds: payload.data.tagIds,
|
|
12380
|
+
scopeKey: "sounds",
|
|
12381
|
+
path: "payload.data.tagIds",
|
|
12382
|
+
details: {
|
|
12383
|
+
soundId: payload.soundId,
|
|
12384
|
+
},
|
|
12385
|
+
});
|
|
11296
12386
|
}
|
|
11297
12387
|
},
|
|
11298
12388
|
reduce: ({ state, payload }) => {
|
|
11299
12389
|
const currentSound = state.sounds.items[payload.soundId];
|
|
11300
|
-
state.sounds.items[payload.soundId] = {
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
};
|
|
12390
|
+
state.sounds.items[payload.soundId] = applyTagIdsUpdate({
|
|
12391
|
+
currentItem: currentSound,
|
|
12392
|
+
data: payload.data,
|
|
12393
|
+
});
|
|
11304
12394
|
return state;
|
|
11305
12395
|
},
|
|
11306
12396
|
},
|
|
@@ -11605,6 +12695,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
11605
12695
|
if (!result.valid) {
|
|
11606
12696
|
return result;
|
|
11607
12697
|
}
|
|
12698
|
+
|
|
12699
|
+
return validateTagIdsAgainstScope({
|
|
12700
|
+
state,
|
|
12701
|
+
tagIds: payload.data.tagIds,
|
|
12702
|
+
scopeKey: "videos",
|
|
12703
|
+
path: "payload.data.tagIds",
|
|
12704
|
+
details: {
|
|
12705
|
+
videoId: payload.videoId,
|
|
12706
|
+
},
|
|
12707
|
+
});
|
|
11608
12708
|
}
|
|
11609
12709
|
},
|
|
11610
12710
|
reduce: ({ state, payload }) => {
|
|
@@ -11630,6 +12730,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
11630
12730
|
if (payload.data.height !== undefined) {
|
|
11631
12731
|
nextVideo.height = payload.data.height;
|
|
11632
12732
|
}
|
|
12733
|
+
|
|
12734
|
+
assignOptionalTagIds({
|
|
12735
|
+
target: nextVideo,
|
|
12736
|
+
tagIds: payload.data.tagIds,
|
|
12737
|
+
});
|
|
11633
12738
|
}
|
|
11634
12739
|
|
|
11635
12740
|
state.videos.items[payload.videoId] = nextVideo;
|
|
@@ -11692,7 +12797,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
11692
12797
|
payload.data.thumbnailFileId !== undefined ||
|
|
11693
12798
|
payload.data.duration !== undefined ||
|
|
11694
12799
|
payload.data.width !== undefined ||
|
|
11695
|
-
payload.data.height !== undefined
|
|
12800
|
+
payload.data.height !== undefined ||
|
|
12801
|
+
payload.data.tagIds !== undefined)
|
|
11696
12802
|
) {
|
|
11697
12803
|
return invalidPrecondition(
|
|
11698
12804
|
"folder video items cannot update file fields",
|
|
@@ -11711,14 +12817,24 @@ const COMMAND_DEFINITIONS = [
|
|
|
11711
12817
|
if (!result.valid) {
|
|
11712
12818
|
return result;
|
|
11713
12819
|
}
|
|
12820
|
+
|
|
12821
|
+
return validateTagIdsAgainstScope({
|
|
12822
|
+
state,
|
|
12823
|
+
tagIds: payload.data.tagIds,
|
|
12824
|
+
scopeKey: "videos",
|
|
12825
|
+
path: "payload.data.tagIds",
|
|
12826
|
+
details: {
|
|
12827
|
+
videoId: payload.videoId,
|
|
12828
|
+
},
|
|
12829
|
+
});
|
|
11714
12830
|
}
|
|
11715
12831
|
},
|
|
11716
12832
|
reduce: ({ state, payload }) => {
|
|
11717
12833
|
const currentVideo = state.videos.items[payload.videoId];
|
|
11718
|
-
state.videos.items[payload.videoId] = {
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
};
|
|
12834
|
+
state.videos.items[payload.videoId] = applyTagIdsUpdate({
|
|
12835
|
+
currentItem: currentVideo,
|
|
12836
|
+
data: payload.data,
|
|
12837
|
+
});
|
|
11722
12838
|
return state;
|
|
11723
12839
|
},
|
|
11724
12840
|
},
|
|
@@ -13142,7 +14258,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
13142
14258
|
|
|
13143
14259
|
return nextItem;
|
|
13144
14260
|
},
|
|
13145
|
-
validateUpdateState: ({ payload, currentItem }) => {
|
|
14261
|
+
validateUpdateState: ({ state, payload, currentItem }) => {
|
|
13146
14262
|
if (
|
|
13147
14263
|
currentItem.type === "folder" &&
|
|
13148
14264
|
Object.keys(payload.data).some(
|
|
@@ -13162,28 +14278,57 @@ const COMMAND_DEFINITIONS = [
|
|
|
13162
14278
|
itemLabel: "transform item",
|
|
13163
14279
|
createDataValidator: validateTransformCreateData,
|
|
13164
14280
|
updateDataValidator: validateTransformUpdateData,
|
|
13165
|
-
createItem: ({ payload }) =>
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
14281
|
+
createItem: ({ payload }) => {
|
|
14282
|
+
const item = {
|
|
14283
|
+
id: payload.transformId,
|
|
14284
|
+
type: payload.data.type,
|
|
14285
|
+
name: payload.data.name,
|
|
14286
|
+
...(payload.data.description !== undefined
|
|
14287
|
+
? {
|
|
14288
|
+
description: payload.data.description,
|
|
14289
|
+
}
|
|
14290
|
+
: {}),
|
|
14291
|
+
};
|
|
14292
|
+
|
|
14293
|
+
if (payload.data.type !== "transform") {
|
|
14294
|
+
return item;
|
|
14295
|
+
}
|
|
14296
|
+
|
|
14297
|
+
item.x = payload.data.x;
|
|
14298
|
+
item.y = payload.data.y;
|
|
14299
|
+
item.scaleX = payload.data.scaleX;
|
|
14300
|
+
item.scaleY = payload.data.scaleY;
|
|
14301
|
+
item.anchorX = payload.data.anchorX;
|
|
14302
|
+
item.anchorY = payload.data.anchorY;
|
|
14303
|
+
item.rotation = payload.data.rotation;
|
|
14304
|
+
assignOptionalTagIds({
|
|
14305
|
+
target: item,
|
|
14306
|
+
tagIds: payload.data.tagIds,
|
|
14307
|
+
});
|
|
14308
|
+
|
|
14309
|
+
return item;
|
|
14310
|
+
},
|
|
14311
|
+
updateItem: ({ currentItem, payload }) =>
|
|
14312
|
+
applyTagIdsUpdate({
|
|
14313
|
+
currentItem,
|
|
14314
|
+
data: payload.data,
|
|
14315
|
+
}),
|
|
14316
|
+
validateCreateState: ({ state, payload }) => {
|
|
14317
|
+
if (payload.data.type !== "transform") {
|
|
14318
|
+
return;
|
|
14319
|
+
}
|
|
14320
|
+
|
|
14321
|
+
return validateTagIdsAgainstScope({
|
|
14322
|
+
state,
|
|
14323
|
+
tagIds: payload.data.tagIds,
|
|
14324
|
+
scopeKey: "transforms",
|
|
14325
|
+
path: "payload.data.tagIds",
|
|
14326
|
+
details: {
|
|
14327
|
+
transformId: payload.transformId,
|
|
14328
|
+
},
|
|
14329
|
+
});
|
|
14330
|
+
},
|
|
14331
|
+
validateUpdateState: ({ state, payload, currentItem }) => {
|
|
13187
14332
|
if (
|
|
13188
14333
|
currentItem.type === "folder" &&
|
|
13189
14334
|
Object.keys(payload.data).some(
|
|
@@ -13194,6 +14339,18 @@ const COMMAND_DEFINITIONS = [
|
|
|
13194
14339
|
"folder transform items cannot update transform fields",
|
|
13195
14340
|
);
|
|
13196
14341
|
}
|
|
14342
|
+
|
|
14343
|
+
if (currentItem.type === "transform") {
|
|
14344
|
+
return validateTagIdsAgainstScope({
|
|
14345
|
+
state,
|
|
14346
|
+
tagIds: payload.data.tagIds,
|
|
14347
|
+
scopeKey: "transforms",
|
|
14348
|
+
path: "payload.data.tagIds",
|
|
14349
|
+
details: {
|
|
14350
|
+
transformId: payload.transformId,
|
|
14351
|
+
},
|
|
14352
|
+
});
|
|
14353
|
+
}
|
|
13197
14354
|
},
|
|
13198
14355
|
}),
|
|
13199
14356
|
...createFolderedCollectionCommandDefinitions({
|
|
@@ -13220,7 +14377,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
13220
14377
|
value: payload.data.value,
|
|
13221
14378
|
}),
|
|
13222
14379
|
}),
|
|
13223
|
-
validateUpdateState: ({ payload, currentItem }) => {
|
|
14380
|
+
validateUpdateState: ({ state, payload, currentItem }) => {
|
|
13224
14381
|
if (
|
|
13225
14382
|
currentItem.type === "folder" &&
|
|
13226
14383
|
Object.keys(payload.data).some(
|
|
@@ -13351,6 +14508,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
13351
14508
|
item.fileId = payload.data.fileId;
|
|
13352
14509
|
}
|
|
13353
14510
|
|
|
14511
|
+
assignOptionalTagIds({
|
|
14512
|
+
target: item,
|
|
14513
|
+
tagIds: payload.data.tagIds,
|
|
14514
|
+
});
|
|
14515
|
+
|
|
13354
14516
|
item.sprites =
|
|
13355
14517
|
payload.data.sprites === undefined
|
|
13356
14518
|
? { items: {}, tree: [] }
|
|
@@ -13358,6 +14520,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
13358
14520
|
|
|
13359
14521
|
return item;
|
|
13360
14522
|
},
|
|
14523
|
+
updateItem: ({ currentItem, payload }) =>
|
|
14524
|
+
applyTagIdsUpdate({
|
|
14525
|
+
currentItem,
|
|
14526
|
+
data: payload.data,
|
|
14527
|
+
}),
|
|
13361
14528
|
validateCreateState: ({ state, payload }) => {
|
|
13362
14529
|
if (payload.data.type !== "character") {
|
|
13363
14530
|
return;
|
|
@@ -13377,6 +14544,21 @@ const COMMAND_DEFINITIONS = [
|
|
|
13377
14544
|
}
|
|
13378
14545
|
}
|
|
13379
14546
|
|
|
14547
|
+
{
|
|
14548
|
+
const result = validateTagIdsAgainstScope({
|
|
14549
|
+
state,
|
|
14550
|
+
tagIds: payload.data.tagIds,
|
|
14551
|
+
scopeKey: "characters",
|
|
14552
|
+
path: "payload.data.tagIds",
|
|
14553
|
+
details: {
|
|
14554
|
+
characterId: payload.characterId,
|
|
14555
|
+
},
|
|
14556
|
+
});
|
|
14557
|
+
if (!result.valid) {
|
|
14558
|
+
return result;
|
|
14559
|
+
}
|
|
14560
|
+
}
|
|
14561
|
+
|
|
13380
14562
|
for (const [spriteId, sprite] of Object.entries(
|
|
13381
14563
|
payload.data.sprites?.items || {},
|
|
13382
14564
|
)) {
|
|
@@ -13384,6 +14566,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
13384
14566
|
continue;
|
|
13385
14567
|
}
|
|
13386
14568
|
|
|
14569
|
+
if (sprite.tagIds !== undefined) {
|
|
14570
|
+
return invalidPrecondition(
|
|
14571
|
+
"payload.data.sprites.items.*.tagIds are not supported during character.create",
|
|
14572
|
+
{
|
|
14573
|
+
characterId: payload.characterId,
|
|
14574
|
+
spriteId,
|
|
14575
|
+
},
|
|
14576
|
+
);
|
|
14577
|
+
}
|
|
14578
|
+
|
|
13387
14579
|
const result = validateFileReference({
|
|
13388
14580
|
state,
|
|
13389
14581
|
fileId: sprite.fileId,
|
|
@@ -13441,6 +14633,27 @@ const COMMAND_DEFINITIONS = [
|
|
|
13441
14633
|
if (!result.valid) {
|
|
13442
14634
|
return result;
|
|
13443
14635
|
}
|
|
14636
|
+
|
|
14637
|
+
return validateTagIdsAgainstScope({
|
|
14638
|
+
state,
|
|
14639
|
+
tagIds: payload.data.tagIds,
|
|
14640
|
+
scopeKey: "characters",
|
|
14641
|
+
path: "payload.data.tagIds",
|
|
14642
|
+
details: {
|
|
14643
|
+
characterId: payload.characterId,
|
|
14644
|
+
},
|
|
14645
|
+
});
|
|
14646
|
+
}
|
|
14647
|
+
},
|
|
14648
|
+
afterDelete: ({ state, deletedItemsById }) => {
|
|
14649
|
+
for (const [characterId, item] of deletedItemsById.entries()) {
|
|
14650
|
+
if (item?.type !== "character") {
|
|
14651
|
+
continue;
|
|
14652
|
+
}
|
|
14653
|
+
|
|
14654
|
+
delete state.tags[
|
|
14655
|
+
`${CHARACTER_SPRITE_TAG_SCOPE_PREFIX}${characterId}`
|
|
14656
|
+
];
|
|
13444
14657
|
}
|
|
13445
14658
|
},
|
|
13446
14659
|
}),
|
|
@@ -13711,6 +14924,17 @@ const COMMAND_DEFINITIONS = [
|
|
|
13711
14924
|
if (!result.valid) {
|
|
13712
14925
|
return result;
|
|
13713
14926
|
}
|
|
14927
|
+
|
|
14928
|
+
return validateTagIdsAgainstScope({
|
|
14929
|
+
state,
|
|
14930
|
+
tagIds: payload.data.tagIds,
|
|
14931
|
+
scopeKey: `${CHARACTER_SPRITE_TAG_SCOPE_PREFIX}${payload.characterId}`,
|
|
14932
|
+
path: "payload.data.tagIds",
|
|
14933
|
+
details: {
|
|
14934
|
+
characterId: payload.characterId,
|
|
14935
|
+
spriteId: payload.spriteId,
|
|
14936
|
+
},
|
|
14937
|
+
});
|
|
13714
14938
|
}
|
|
13715
14939
|
},
|
|
13716
14940
|
reduce: ({ state, payload }) => {
|
|
@@ -13719,10 +14943,15 @@ const COMMAND_DEFINITIONS = [
|
|
|
13719
14943
|
characterId: payload.characterId,
|
|
13720
14944
|
});
|
|
13721
14945
|
|
|
13722
|
-
|
|
14946
|
+
const nextSprite = {
|
|
13723
14947
|
id: payload.spriteId,
|
|
13724
14948
|
...structuredClone(payload.data),
|
|
13725
14949
|
};
|
|
14950
|
+
if (payload.data.tagIds !== undefined && payload.data.tagIds.length === 0) {
|
|
14951
|
+
delete nextSprite.tagIds;
|
|
14952
|
+
}
|
|
14953
|
+
|
|
14954
|
+
collection.items[payload.spriteId] = nextSprite;
|
|
13726
14955
|
|
|
13727
14956
|
insertTreeNode({
|
|
13728
14957
|
tree: collection.tree,
|
|
@@ -13816,6 +15045,17 @@ const COMMAND_DEFINITIONS = [
|
|
|
13816
15045
|
if (!result.valid) {
|
|
13817
15046
|
return result;
|
|
13818
15047
|
}
|
|
15048
|
+
|
|
15049
|
+
return validateTagIdsAgainstScope({
|
|
15050
|
+
state,
|
|
15051
|
+
tagIds: payload.data.tagIds,
|
|
15052
|
+
scopeKey: `${CHARACTER_SPRITE_TAG_SCOPE_PREFIX}${payload.characterId}`,
|
|
15053
|
+
path: "payload.data.tagIds",
|
|
15054
|
+
details: {
|
|
15055
|
+
characterId: payload.characterId,
|
|
15056
|
+
spriteId: payload.spriteId,
|
|
15057
|
+
},
|
|
15058
|
+
});
|
|
13819
15059
|
}
|
|
13820
15060
|
},
|
|
13821
15061
|
reduce: ({ state, payload }) => {
|
|
@@ -13825,10 +15065,10 @@ const COMMAND_DEFINITIONS = [
|
|
|
13825
15065
|
});
|
|
13826
15066
|
const currentItem = collection.items[payload.spriteId];
|
|
13827
15067
|
|
|
13828
|
-
collection.items[payload.spriteId] = {
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
};
|
|
15068
|
+
collection.items[payload.spriteId] = applyTagIdsUpdate({
|
|
15069
|
+
currentItem,
|
|
15070
|
+
data: payload.data,
|
|
15071
|
+
});
|
|
13832
15072
|
|
|
13833
15073
|
return state;
|
|
13834
15074
|
},
|
|
@@ -14060,6 +15300,297 @@ const COMMAND_DEFINITIONS = [
|
|
|
14060
15300
|
return state;
|
|
14061
15301
|
},
|
|
14062
15302
|
},
|
|
15303
|
+
{
|
|
15304
|
+
type: "tag.create",
|
|
15305
|
+
validatePayload: ({ payload }) => {
|
|
15306
|
+
{
|
|
15307
|
+
const result = validateExactKeys({
|
|
15308
|
+
value: payload,
|
|
15309
|
+
expectedKeys: ["scopeKey", "tagId", "data"],
|
|
15310
|
+
path: "payload",
|
|
15311
|
+
errorFactory: createPayloadValidationError,
|
|
15312
|
+
});
|
|
15313
|
+
if (result?.valid === false) {
|
|
15314
|
+
return result;
|
|
15315
|
+
}
|
|
15316
|
+
}
|
|
15317
|
+
|
|
15318
|
+
{
|
|
15319
|
+
const result = validateTagScopeKey({
|
|
15320
|
+
scopeKey: payload.scopeKey,
|
|
15321
|
+
path: "payload.scopeKey",
|
|
15322
|
+
errorFactory: createPayloadValidationError,
|
|
15323
|
+
});
|
|
15324
|
+
if (result?.valid === false) {
|
|
15325
|
+
return result;
|
|
15326
|
+
}
|
|
15327
|
+
}
|
|
15328
|
+
|
|
15329
|
+
if (!isNonEmptyString(payload.tagId)) {
|
|
15330
|
+
return invalidPayload("payload.tagId must be a non-empty string");
|
|
15331
|
+
}
|
|
15332
|
+
|
|
15333
|
+
{
|
|
15334
|
+
const result = validateTagCreateData({
|
|
15335
|
+
data: payload.data,
|
|
15336
|
+
errorFactory: createPayloadValidationError,
|
|
15337
|
+
});
|
|
15338
|
+
if (result?.valid === false) {
|
|
15339
|
+
return result;
|
|
15340
|
+
}
|
|
15341
|
+
}
|
|
15342
|
+
},
|
|
15343
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15344
|
+
{
|
|
15345
|
+
const result = validateTagScopeAgainstState({
|
|
15346
|
+
state,
|
|
15347
|
+
scopeKey: payload.scopeKey,
|
|
15348
|
+
path: "payload.scopeKey",
|
|
15349
|
+
});
|
|
15350
|
+
if (!result.valid) {
|
|
15351
|
+
return result;
|
|
15352
|
+
}
|
|
15353
|
+
}
|
|
15354
|
+
|
|
15355
|
+
const collection = getTagScopeCollection({
|
|
15356
|
+
state,
|
|
15357
|
+
scopeKey: payload.scopeKey,
|
|
15358
|
+
});
|
|
15359
|
+
if (isPlainObject(collection?.items?.[payload.tagId])) {
|
|
15360
|
+
return invalidPrecondition("payload.tagId must not already exist");
|
|
15361
|
+
}
|
|
15362
|
+
|
|
15363
|
+
return validateUniqueTagNameInScope({
|
|
15364
|
+
collection,
|
|
15365
|
+
name: payload.data.name,
|
|
15366
|
+
path: "payload.data.name",
|
|
15367
|
+
});
|
|
15368
|
+
},
|
|
15369
|
+
reduce: ({ state, payload }) => {
|
|
15370
|
+
const collection = ensureTagScopeCollection({
|
|
15371
|
+
state,
|
|
15372
|
+
scopeKey: payload.scopeKey,
|
|
15373
|
+
});
|
|
15374
|
+
const nextTag = {
|
|
15375
|
+
id: payload.tagId,
|
|
15376
|
+
type: "tag",
|
|
15377
|
+
name: payload.data.name,
|
|
15378
|
+
};
|
|
15379
|
+
|
|
15380
|
+
if (payload.data.color !== undefined) {
|
|
15381
|
+
nextTag.color = payload.data.color;
|
|
15382
|
+
}
|
|
15383
|
+
|
|
15384
|
+
collection.items[payload.tagId] = nextTag;
|
|
15385
|
+
collection.tree.push({
|
|
15386
|
+
id: payload.tagId,
|
|
15387
|
+
});
|
|
15388
|
+
|
|
15389
|
+
return state;
|
|
15390
|
+
},
|
|
15391
|
+
},
|
|
15392
|
+
{
|
|
15393
|
+
type: "tag.update",
|
|
15394
|
+
validatePayload: ({ payload }) => {
|
|
15395
|
+
{
|
|
15396
|
+
const result = validateExactKeys({
|
|
15397
|
+
value: payload,
|
|
15398
|
+
expectedKeys: ["scopeKey", "tagId", "data"],
|
|
15399
|
+
path: "payload",
|
|
15400
|
+
errorFactory: createPayloadValidationError,
|
|
15401
|
+
});
|
|
15402
|
+
if (result?.valid === false) {
|
|
15403
|
+
return result;
|
|
15404
|
+
}
|
|
15405
|
+
}
|
|
15406
|
+
|
|
15407
|
+
{
|
|
15408
|
+
const result = validateTagScopeKey({
|
|
15409
|
+
scopeKey: payload.scopeKey,
|
|
15410
|
+
path: "payload.scopeKey",
|
|
15411
|
+
errorFactory: createPayloadValidationError,
|
|
15412
|
+
});
|
|
15413
|
+
if (result?.valid === false) {
|
|
15414
|
+
return result;
|
|
15415
|
+
}
|
|
15416
|
+
}
|
|
15417
|
+
|
|
15418
|
+
if (!isNonEmptyString(payload.tagId)) {
|
|
15419
|
+
return invalidPayload("payload.tagId must be a non-empty string");
|
|
15420
|
+
}
|
|
15421
|
+
|
|
15422
|
+
{
|
|
15423
|
+
const result = validateTagUpdateData({
|
|
15424
|
+
data: payload.data,
|
|
15425
|
+
errorFactory: createPayloadValidationError,
|
|
15426
|
+
});
|
|
15427
|
+
if (result?.valid === false) {
|
|
15428
|
+
return result;
|
|
15429
|
+
}
|
|
15430
|
+
}
|
|
15431
|
+
},
|
|
15432
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15433
|
+
{
|
|
15434
|
+
const result = validateTagScopeAgainstState({
|
|
15435
|
+
state,
|
|
15436
|
+
scopeKey: payload.scopeKey,
|
|
15437
|
+
path: "payload.scopeKey",
|
|
15438
|
+
});
|
|
15439
|
+
if (!result.valid) {
|
|
15440
|
+
return result;
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
|
|
15444
|
+
const collection = getTagScopeCollection({
|
|
15445
|
+
state,
|
|
15446
|
+
scopeKey: payload.scopeKey,
|
|
15447
|
+
});
|
|
15448
|
+
const currentTag = collection?.items?.[payload.tagId];
|
|
15449
|
+
if (!isPlainObject(currentTag) || currentTag.type !== "tag") {
|
|
15450
|
+
return invalidPrecondition(
|
|
15451
|
+
"payload.tagId must reference an existing tag in payload.scopeKey",
|
|
15452
|
+
);
|
|
15453
|
+
}
|
|
15454
|
+
|
|
15455
|
+
if (payload.data.name === undefined) {
|
|
15456
|
+
return VALID_RESULT;
|
|
15457
|
+
}
|
|
15458
|
+
|
|
15459
|
+
return validateUniqueTagNameInScope({
|
|
15460
|
+
collection,
|
|
15461
|
+
name: payload.data.name,
|
|
15462
|
+
path: "payload.data.name",
|
|
15463
|
+
excludeTagId: payload.tagId,
|
|
15464
|
+
});
|
|
15465
|
+
},
|
|
15466
|
+
reduce: ({ state, payload }) => {
|
|
15467
|
+
const collection = ensureTagScopeCollection({
|
|
15468
|
+
state,
|
|
15469
|
+
scopeKey: payload.scopeKey,
|
|
15470
|
+
});
|
|
15471
|
+
const currentTag = collection.items[payload.tagId];
|
|
15472
|
+
const nextTag = {
|
|
15473
|
+
...structuredClone(currentTag),
|
|
15474
|
+
};
|
|
15475
|
+
|
|
15476
|
+
if (payload.data.name !== undefined) {
|
|
15477
|
+
nextTag.name = payload.data.name;
|
|
15478
|
+
}
|
|
15479
|
+
|
|
15480
|
+
if (payload.data.color === null) {
|
|
15481
|
+
delete nextTag.color;
|
|
15482
|
+
} else if (payload.data.color !== undefined) {
|
|
15483
|
+
nextTag.color = payload.data.color;
|
|
15484
|
+
}
|
|
15485
|
+
|
|
15486
|
+
collection.items[payload.tagId] = nextTag;
|
|
15487
|
+
return state;
|
|
15488
|
+
},
|
|
15489
|
+
},
|
|
15490
|
+
{
|
|
15491
|
+
type: "tag.delete",
|
|
15492
|
+
validatePayload: ({ payload }) => {
|
|
15493
|
+
{
|
|
15494
|
+
const result = validateExactKeys({
|
|
15495
|
+
value: payload,
|
|
15496
|
+
expectedKeys: ["scopeKey", "tagIds"],
|
|
15497
|
+
path: "payload",
|
|
15498
|
+
errorFactory: createPayloadValidationError,
|
|
15499
|
+
});
|
|
15500
|
+
if (result?.valid === false) {
|
|
15501
|
+
return result;
|
|
15502
|
+
}
|
|
15503
|
+
}
|
|
15504
|
+
|
|
15505
|
+
{
|
|
15506
|
+
const result = validateTagScopeKey({
|
|
15507
|
+
scopeKey: payload.scopeKey,
|
|
15508
|
+
path: "payload.scopeKey",
|
|
15509
|
+
errorFactory: createPayloadValidationError,
|
|
15510
|
+
});
|
|
15511
|
+
if (result?.valid === false) {
|
|
15512
|
+
return result;
|
|
15513
|
+
}
|
|
15514
|
+
}
|
|
15515
|
+
|
|
15516
|
+
{
|
|
15517
|
+
const result = validateRequiredUniqueIdArray({
|
|
15518
|
+
value: payload.tagIds,
|
|
15519
|
+
path: "payload.tagIds",
|
|
15520
|
+
errorFactory: createPayloadValidationError,
|
|
15521
|
+
});
|
|
15522
|
+
if (result?.valid === false) {
|
|
15523
|
+
return result;
|
|
15524
|
+
}
|
|
15525
|
+
}
|
|
15526
|
+
},
|
|
15527
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15528
|
+
{
|
|
15529
|
+
const result = validateTagScopeAgainstState({
|
|
15530
|
+
state,
|
|
15531
|
+
scopeKey: payload.scopeKey,
|
|
15532
|
+
path: "payload.scopeKey",
|
|
15533
|
+
});
|
|
15534
|
+
if (!result.valid) {
|
|
15535
|
+
return result;
|
|
15536
|
+
}
|
|
15537
|
+
}
|
|
15538
|
+
|
|
15539
|
+
const collection = getTagScopeCollection({
|
|
15540
|
+
state,
|
|
15541
|
+
scopeKey: payload.scopeKey,
|
|
15542
|
+
});
|
|
15543
|
+
for (const tagId of payload.tagIds) {
|
|
15544
|
+
const tag = collection?.items?.[tagId];
|
|
15545
|
+
if (!isPlainObject(tag) || tag.type !== "tag") {
|
|
15546
|
+
return invalidPrecondition(
|
|
15547
|
+
"payload.tagIds must reference existing tags in payload.scopeKey",
|
|
15548
|
+
{
|
|
15549
|
+
scopeKey: payload.scopeKey,
|
|
15550
|
+
tagId,
|
|
15551
|
+
},
|
|
15552
|
+
);
|
|
15553
|
+
}
|
|
15554
|
+
}
|
|
15555
|
+
|
|
15556
|
+
return VALID_RESULT;
|
|
15557
|
+
},
|
|
15558
|
+
reduce: ({ state, payload }) => {
|
|
15559
|
+
const collection = ensureTagScopeCollection({
|
|
15560
|
+
state,
|
|
15561
|
+
scopeKey: payload.scopeKey,
|
|
15562
|
+
});
|
|
15563
|
+
const deletedTagIds = new Set();
|
|
15564
|
+
|
|
15565
|
+
for (const tagId of payload.tagIds) {
|
|
15566
|
+
const removedNode = removeTreeNode({
|
|
15567
|
+
nodes: collection.tree,
|
|
15568
|
+
nodeId: tagId,
|
|
15569
|
+
});
|
|
15570
|
+
if (!removedNode) {
|
|
15571
|
+
continue;
|
|
15572
|
+
}
|
|
15573
|
+
|
|
15574
|
+
for (const deletedTagId of collectTreeDescendantIds({
|
|
15575
|
+
node: removedNode,
|
|
15576
|
+
})) {
|
|
15577
|
+
deletedTagIds.add(deletedTagId);
|
|
15578
|
+
}
|
|
15579
|
+
}
|
|
15580
|
+
|
|
15581
|
+
for (const tagId of deletedTagIds) {
|
|
15582
|
+
delete collection.items[tagId];
|
|
15583
|
+
}
|
|
15584
|
+
|
|
15585
|
+
stripDeletedTagIdsFromScopeItems({
|
|
15586
|
+
state,
|
|
15587
|
+
scopeKey: payload.scopeKey,
|
|
15588
|
+
deletedTagIds,
|
|
15589
|
+
});
|
|
15590
|
+
|
|
15591
|
+
return state;
|
|
15592
|
+
},
|
|
15593
|
+
},
|
|
14063
15594
|
{
|
|
14064
15595
|
type: "layout.element.create",
|
|
14065
15596
|
validatePayload: ({ payload }) => {
|
|
@@ -15232,11 +16763,7 @@ export const validateAgainstState = ({ state, command }) => {
|
|
|
15232
16763
|
export const processCommand = ({ state, command }) => {
|
|
15233
16764
|
return captureValidation(() => {
|
|
15234
16765
|
const normalizedState = normalizeStateCollections(state);
|
|
15235
|
-
const shouldMaterializeNormalizedState =
|
|
15236
|
-
typeof command?.type === "string" &&
|
|
15237
|
-
(command.type.startsWith("control.") ||
|
|
15238
|
-
command.type.startsWith("spritesheet.") ||
|
|
15239
|
-
command.type.startsWith("particle."));
|
|
16766
|
+
const shouldMaterializeNormalizedState = normalizedState !== state;
|
|
15240
16767
|
|
|
15241
16768
|
const stateResult = validateState({ state: normalizedState });
|
|
15242
16769
|
if (!stateResult.valid) {
|