@routevn/creator-model 1.7.3 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/model.js +2288 -993
package/src/model.js
CHANGED
|
@@ -23,6 +23,7 @@ const COLLECTION_KEYS = [
|
|
|
23
23
|
"images",
|
|
24
24
|
"spritesheets",
|
|
25
25
|
"sounds",
|
|
26
|
+
"voices",
|
|
26
27
|
"videos",
|
|
27
28
|
"animations",
|
|
28
29
|
"particles",
|
|
@@ -245,6 +246,7 @@ const normalizeStateCollections = (state) => {
|
|
|
245
246
|
"spritesheets",
|
|
246
247
|
"particles",
|
|
247
248
|
"controls",
|
|
249
|
+
"voices",
|
|
248
250
|
].filter((key) => state[key] === undefined);
|
|
249
251
|
const normalizedTags = normalizeTagsState(state.tags);
|
|
250
252
|
const hasNormalizedTags = normalizedTags !== state.tags;
|
|
@@ -392,7 +394,7 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
392
394
|
"container-ref-confirm-dialog-ok",
|
|
393
395
|
"container-ref-confirm-dialog-cancel",
|
|
394
396
|
];
|
|
395
|
-
export const SCHEMA_VERSION =
|
|
397
|
+
export const SCHEMA_VERSION = 8;
|
|
396
398
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
397
399
|
"folder",
|
|
398
400
|
"container",
|
|
@@ -404,6 +406,18 @@ const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
|
404
406
|
"container-ref-confirm-dialog-ok",
|
|
405
407
|
"container-ref-confirm-dialog-cancel",
|
|
406
408
|
];
|
|
409
|
+
const LAYOUT_TEXT_CONTENT_ELEMENT_TYPES = [
|
|
410
|
+
"text",
|
|
411
|
+
"text-revealing",
|
|
412
|
+
"text-ref-character-name",
|
|
413
|
+
"text-revealing-ref-dialogue-content",
|
|
414
|
+
"text-ref-choice-item-content",
|
|
415
|
+
"text-ref-save-load-slot-date",
|
|
416
|
+
"text-ref-dialogue-line-character-name",
|
|
417
|
+
"text-ref-dialogue-line-content",
|
|
418
|
+
"text-ref-history-line-character-name",
|
|
419
|
+
"text-ref-history-line-content",
|
|
420
|
+
];
|
|
407
421
|
const DOMAIN_ERROR_KIND_BY_NAME = {
|
|
408
422
|
PayloadValidationError: "payload",
|
|
409
423
|
PreconditionValidationError: "precondition",
|
|
@@ -1431,6 +1445,105 @@ const validateSoundItems = ({ items, path, errorFactory }) => {
|
|
|
1431
1445
|
}
|
|
1432
1446
|
};
|
|
1433
1447
|
|
|
1448
|
+
const validateVoiceItems = ({ items, path, errorFactory }) => {
|
|
1449
|
+
for (const [itemId, item] of Object.entries(items)) {
|
|
1450
|
+
const itemPath = `${path}.${itemId}`;
|
|
1451
|
+
|
|
1452
|
+
if (item?.type !== "folder" && item?.type !== "voice") {
|
|
1453
|
+
return invalidFromErrorFactory(
|
|
1454
|
+
errorFactory,
|
|
1455
|
+
`${itemPath}.type must be 'folder' or 'voice'`,
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
{
|
|
1460
|
+
const result = validateAllowedKeys({
|
|
1461
|
+
value: item,
|
|
1462
|
+
allowedKeys:
|
|
1463
|
+
item.type === "folder"
|
|
1464
|
+
? ["id", "type", "name", "description"]
|
|
1465
|
+
: [
|
|
1466
|
+
"id",
|
|
1467
|
+
"type",
|
|
1468
|
+
"name",
|
|
1469
|
+
"description",
|
|
1470
|
+
"sceneId",
|
|
1471
|
+
"fileId",
|
|
1472
|
+
"waveformDataFileId",
|
|
1473
|
+
"duration",
|
|
1474
|
+
],
|
|
1475
|
+
path: itemPath,
|
|
1476
|
+
errorFactory,
|
|
1477
|
+
});
|
|
1478
|
+
if (result?.valid === false) {
|
|
1479
|
+
return result;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
if (!isNonEmptyString(item.id)) {
|
|
1484
|
+
return invalidFromErrorFactory(
|
|
1485
|
+
errorFactory,
|
|
1486
|
+
`${itemPath}.id must be a non-empty string`,
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
if (item.id !== itemId) {
|
|
1491
|
+
return invalidFromErrorFactory(
|
|
1492
|
+
errorFactory,
|
|
1493
|
+
`${itemPath}.id must match item key '${itemId}'`,
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
if (!isNonEmptyString(item.name)) {
|
|
1498
|
+
return invalidFromErrorFactory(
|
|
1499
|
+
errorFactory,
|
|
1500
|
+
`${itemPath}.name must be a non-empty string`,
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
if (item.description !== undefined && !isString(item.description)) {
|
|
1505
|
+
return invalidFromErrorFactory(
|
|
1506
|
+
errorFactory,
|
|
1507
|
+
`${itemPath}.description must be a string when provided`,
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
if (item.type === "voice") {
|
|
1512
|
+
if (!isNonEmptyString(item.sceneId)) {
|
|
1513
|
+
return invalidFromErrorFactory(
|
|
1514
|
+
errorFactory,
|
|
1515
|
+
`${itemPath}.sceneId must be a non-empty string`,
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
if (!isNonEmptyString(item.fileId)) {
|
|
1520
|
+
return invalidFromErrorFactory(
|
|
1521
|
+
errorFactory,
|
|
1522
|
+
`${itemPath}.fileId must be a non-empty string`,
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
if (
|
|
1527
|
+
item.waveformDataFileId !== undefined &&
|
|
1528
|
+
item.waveformDataFileId !== null &&
|
|
1529
|
+
!isNonEmptyString(item.waveformDataFileId)
|
|
1530
|
+
) {
|
|
1531
|
+
return invalidFromErrorFactory(
|
|
1532
|
+
errorFactory,
|
|
1533
|
+
`${itemPath}.waveformDataFileId must be a non-empty string or null when provided`,
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if (item.duration !== undefined && !isFiniteNumber(item.duration)) {
|
|
1538
|
+
return invalidFromErrorFactory(
|
|
1539
|
+
errorFactory,
|
|
1540
|
+
`${itemPath}.duration must be a finite number`,
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1434
1547
|
const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
1435
1548
|
for (const [itemId, item] of Object.entries(items)) {
|
|
1436
1549
|
const itemPath = `${path}.${itemId}`;
|
|
@@ -3419,97 +3532,36 @@ const validateLayoutElementBlur = ({ blur, path, errorFactory }) => {
|
|
|
3419
3532
|
}
|
|
3420
3533
|
};
|
|
3421
3534
|
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3535
|
+
const LAYOUT_TEXT_REVEAL_INDICATOR_ELEMENT_TYPES = [
|
|
3536
|
+
"text-revealing",
|
|
3537
|
+
"text-revealing-ref-dialogue-content",
|
|
3538
|
+
];
|
|
3539
|
+
|
|
3540
|
+
const validateLayoutTextRevealIndicatorVisual = ({
|
|
3541
|
+
visual,
|
|
3424
3542
|
path,
|
|
3425
3543
|
errorFactory,
|
|
3426
|
-
allowPartial = false,
|
|
3427
3544
|
}) => {
|
|
3428
|
-
if (!isPlainObject(
|
|
3429
|
-
return invalidFromErrorFactory(
|
|
3545
|
+
if (!isPlainObject(visual)) {
|
|
3546
|
+
return invalidFromErrorFactory(
|
|
3547
|
+
errorFactory,
|
|
3548
|
+
`${path} must be an object when provided`,
|
|
3549
|
+
);
|
|
3430
3550
|
}
|
|
3431
3551
|
|
|
3432
|
-
const allowedKeys = [
|
|
3433
|
-
"type",
|
|
3434
|
-
"name",
|
|
3435
|
-
"x",
|
|
3436
|
-
"y",
|
|
3437
|
-
"width",
|
|
3438
|
-
"height",
|
|
3439
|
-
"aspectRatioLock",
|
|
3440
|
-
"anchorX",
|
|
3441
|
-
"anchorY",
|
|
3442
|
-
"scaleX",
|
|
3443
|
-
"scaleY",
|
|
3444
|
-
"rotation",
|
|
3445
|
-
"opacity",
|
|
3446
|
-
"blur",
|
|
3447
|
-
"fill",
|
|
3448
|
-
"border",
|
|
3449
|
-
"text",
|
|
3450
|
-
"textStyle",
|
|
3451
|
-
"displaySpeed",
|
|
3452
|
-
"revealEffect",
|
|
3453
|
-
"resourceId",
|
|
3454
|
-
"animationName",
|
|
3455
|
-
"imageId",
|
|
3456
|
-
"hoverImageId",
|
|
3457
|
-
"clickImageId",
|
|
3458
|
-
"hoverSoundId",
|
|
3459
|
-
"clickSoundId",
|
|
3460
|
-
"textStyleId",
|
|
3461
|
-
"hoverTextStyleId",
|
|
3462
|
-
"clickTextStyleId",
|
|
3463
|
-
"field",
|
|
3464
|
-
"value",
|
|
3465
|
-
"placeholder",
|
|
3466
|
-
"multiline",
|
|
3467
|
-
"disabled",
|
|
3468
|
-
"maxLength",
|
|
3469
|
-
"formRole",
|
|
3470
|
-
"padding",
|
|
3471
|
-
"conditionalOverrides",
|
|
3472
|
-
"direction",
|
|
3473
|
-
"gapX",
|
|
3474
|
-
"gapY",
|
|
3475
|
-
"containerType",
|
|
3476
|
-
"scroll",
|
|
3477
|
-
"hover",
|
|
3478
|
-
"click",
|
|
3479
|
-
"rightClick",
|
|
3480
|
-
"scrollUp",
|
|
3481
|
-
"scrollDown",
|
|
3482
|
-
"submit",
|
|
3483
|
-
"focusEvent",
|
|
3484
|
-
"blurEvent",
|
|
3485
|
-
"selectionChange",
|
|
3486
|
-
"compositionStart",
|
|
3487
|
-
"compositionUpdate",
|
|
3488
|
-
"compositionEnd",
|
|
3489
|
-
"anchorToBottom",
|
|
3490
|
-
"thumbImageId",
|
|
3491
|
-
"barImageId",
|
|
3492
|
-
"hoverThumbImageId",
|
|
3493
|
-
"hoverBarImageId",
|
|
3494
|
-
"min",
|
|
3495
|
-
"max",
|
|
3496
|
-
"step",
|
|
3497
|
-
"initialValue",
|
|
3498
|
-
"variableId",
|
|
3499
|
-
"particleId",
|
|
3500
|
-
"fragmentLayoutId",
|
|
3501
|
-
"paginationMode",
|
|
3502
|
-
"paginationVariableId",
|
|
3503
|
-
"paginationSize",
|
|
3504
|
-
"choiceItemIndex",
|
|
3505
|
-
"$when",
|
|
3506
|
-
"change",
|
|
3507
|
-
];
|
|
3508
|
-
|
|
3509
3552
|
{
|
|
3510
3553
|
const result = validateAllowedKeys({
|
|
3511
|
-
value:
|
|
3512
|
-
allowedKeys
|
|
3554
|
+
value: visual,
|
|
3555
|
+
allowedKeys: [
|
|
3556
|
+
"kind",
|
|
3557
|
+
"imageId",
|
|
3558
|
+
"resourceId",
|
|
3559
|
+
"animationName",
|
|
3560
|
+
"width",
|
|
3561
|
+
"height",
|
|
3562
|
+
"offsetX",
|
|
3563
|
+
"offsetY",
|
|
3564
|
+
],
|
|
3513
3565
|
path,
|
|
3514
3566
|
errorFactory,
|
|
3515
3567
|
});
|
|
@@ -3518,101 +3570,140 @@ const validateLayoutElementData = ({
|
|
|
3518
3570
|
}
|
|
3519
3571
|
}
|
|
3520
3572
|
|
|
3521
|
-
|
|
3522
|
-
|
|
3573
|
+
const kind =
|
|
3574
|
+
visual.kind === "spritesheet" ||
|
|
3575
|
+
visual.resourceId !== undefined ||
|
|
3576
|
+
visual.animationName !== undefined
|
|
3577
|
+
? "spritesheet"
|
|
3578
|
+
: "image";
|
|
3579
|
+
|
|
3580
|
+
if (
|
|
3581
|
+
visual.kind !== undefined &&
|
|
3582
|
+
visual.kind !== "image" &&
|
|
3583
|
+
visual.kind !== "spritesheet"
|
|
3584
|
+
) {
|
|
3585
|
+
return invalidFromErrorFactory(
|
|
3586
|
+
errorFactory,
|
|
3587
|
+
`${path}.kind must be 'image' or 'spritesheet' when provided`,
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
if (kind === "image" && !isNonEmptyString(visual.imageId)) {
|
|
3592
|
+
return invalidFromErrorFactory(
|
|
3593
|
+
errorFactory,
|
|
3594
|
+
`${path}.imageId must be a non-empty string`,
|
|
3595
|
+
);
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
if (kind === "spritesheet") {
|
|
3599
|
+
if (!isNonEmptyString(visual.resourceId)) {
|
|
3523
3600
|
return invalidFromErrorFactory(
|
|
3524
3601
|
errorFactory,
|
|
3525
|
-
`${path}.
|
|
3602
|
+
`${path}.resourceId must be a non-empty string`,
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
if (!isNonEmptyString(visual.animationName)) {
|
|
3607
|
+
return invalidFromErrorFactory(
|
|
3608
|
+
errorFactory,
|
|
3609
|
+
`${path}.animationName must be a non-empty string`,
|
|
3526
3610
|
);
|
|
3527
3611
|
}
|
|
3528
3612
|
}
|
|
3529
3613
|
|
|
3530
|
-
|
|
3531
|
-
if (
|
|
3614
|
+
for (const key of ["width", "height"]) {
|
|
3615
|
+
if (
|
|
3616
|
+
visual[key] !== undefined &&
|
|
3617
|
+
(!isFiniteNumber(visual[key]) || visual[key] <= 0)
|
|
3618
|
+
) {
|
|
3532
3619
|
return invalidFromErrorFactory(
|
|
3533
3620
|
errorFactory,
|
|
3534
|
-
`${path}
|
|
3621
|
+
`${path}.${key} must be a finite number greater than 0 when provided`,
|
|
3535
3622
|
);
|
|
3536
3623
|
}
|
|
3537
3624
|
}
|
|
3538
3625
|
|
|
3539
|
-
for (const key of [
|
|
3540
|
-
|
|
3541
|
-
"y",
|
|
3542
|
-
"width",
|
|
3543
|
-
"height",
|
|
3544
|
-
"anchorX",
|
|
3545
|
-
"anchorY",
|
|
3546
|
-
"scaleX",
|
|
3547
|
-
"scaleY",
|
|
3548
|
-
"rotation",
|
|
3549
|
-
"displaySpeed",
|
|
3550
|
-
"gapX",
|
|
3551
|
-
"gapY",
|
|
3552
|
-
"min",
|
|
3553
|
-
"max",
|
|
3554
|
-
"step",
|
|
3555
|
-
"paginationSize",
|
|
3556
|
-
"choiceItemIndex",
|
|
3557
|
-
"opacity",
|
|
3558
|
-
"maxLength",
|
|
3559
|
-
]) {
|
|
3560
|
-
if (data[key] !== undefined && !isFiniteNumber(data[key])) {
|
|
3626
|
+
for (const key of ["offsetX", "offsetY"]) {
|
|
3627
|
+
if (visual[key] !== undefined && !isFiniteNumber(visual[key])) {
|
|
3561
3628
|
return invalidFromErrorFactory(
|
|
3562
3629
|
errorFactory,
|
|
3563
3630
|
`${path}.${key} must be a finite number when provided`,
|
|
3564
3631
|
);
|
|
3565
3632
|
}
|
|
3566
3633
|
}
|
|
3634
|
+
};
|
|
3567
3635
|
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
) {
|
|
3636
|
+
const validateLayoutTextRevealIndicator = ({
|
|
3637
|
+
indicator,
|
|
3638
|
+
path,
|
|
3639
|
+
errorFactory,
|
|
3640
|
+
}) => {
|
|
3641
|
+
if (!isPlainObject(indicator)) {
|
|
3574
3642
|
return invalidFromErrorFactory(
|
|
3575
3643
|
errorFactory,
|
|
3576
|
-
`${path}
|
|
3644
|
+
`${path} must be an object when provided`,
|
|
3577
3645
|
);
|
|
3578
3646
|
}
|
|
3579
3647
|
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3648
|
+
{
|
|
3649
|
+
const result = validateAllowedKeys({
|
|
3650
|
+
value: indicator,
|
|
3651
|
+
allowedKeys: ["revealing", "complete", "offsetX", "offsetY"],
|
|
3652
|
+
path,
|
|
3585
3653
|
errorFactory,
|
|
3586
|
-
|
|
3587
|
-
)
|
|
3654
|
+
});
|
|
3655
|
+
if (result?.valid === false) {
|
|
3656
|
+
return result;
|
|
3657
|
+
}
|
|
3588
3658
|
}
|
|
3589
3659
|
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3660
|
+
for (const key of ["revealing", "complete"]) {
|
|
3661
|
+
if (indicator[key] !== undefined) {
|
|
3662
|
+
const result = validateLayoutTextRevealIndicatorVisual({
|
|
3663
|
+
visual: indicator[key],
|
|
3664
|
+
path: `${path}.${key}`,
|
|
3665
|
+
errorFactory,
|
|
3666
|
+
});
|
|
3667
|
+
if (result?.valid === false) {
|
|
3668
|
+
return result;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3599
3671
|
}
|
|
3600
3672
|
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3673
|
+
for (const key of ["offsetX", "offsetY"]) {
|
|
3674
|
+
if (indicator[key] !== undefined && !isFiniteNumber(indicator[key])) {
|
|
3675
|
+
return invalidFromErrorFactory(
|
|
3676
|
+
errorFactory,
|
|
3677
|
+
`${path}.${key} must be a finite number when provided`,
|
|
3678
|
+
);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
};
|
|
3682
|
+
|
|
3683
|
+
const validateLayoutTextContent = ({ content, path, errorFactory }) => {
|
|
3684
|
+
if (!Array.isArray(content)) {
|
|
3605
3685
|
return invalidFromErrorFactory(
|
|
3606
3686
|
errorFactory,
|
|
3607
|
-
`${path}
|
|
3687
|
+
`${path} must be an array when provided`,
|
|
3608
3688
|
);
|
|
3609
3689
|
}
|
|
3610
3690
|
|
|
3611
|
-
|
|
3691
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
3692
|
+
const item = content[index];
|
|
3693
|
+
const itemPath = `${path}[${index}]`;
|
|
3694
|
+
|
|
3695
|
+
if (!isPlainObject(item)) {
|
|
3696
|
+
return invalidFromErrorFactory(
|
|
3697
|
+
errorFactory,
|
|
3698
|
+
`${itemPath} must be an object`,
|
|
3699
|
+
);
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3612
3702
|
{
|
|
3613
|
-
const result =
|
|
3614
|
-
|
|
3615
|
-
|
|
3703
|
+
const result = validateAllowedKeys({
|
|
3704
|
+
value: item,
|
|
3705
|
+
allowedKeys: ["text", "reference"],
|
|
3706
|
+
path: itemPath,
|
|
3616
3707
|
errorFactory,
|
|
3617
3708
|
});
|
|
3618
3709
|
if (result?.valid === false) {
|
|
@@ -3620,19 +3711,107 @@ const validateLayoutElementData = ({
|
|
|
3620
3711
|
}
|
|
3621
3712
|
}
|
|
3622
3713
|
|
|
3623
|
-
|
|
3714
|
+
const hasText = Object.hasOwn(item, "text");
|
|
3715
|
+
const hasReference = Object.hasOwn(item, "reference");
|
|
3716
|
+
|
|
3717
|
+
if (hasText === hasReference) {
|
|
3624
3718
|
return invalidFromErrorFactory(
|
|
3625
3719
|
errorFactory,
|
|
3626
|
-
`${
|
|
3720
|
+
`${itemPath} must include exactly one of text or reference`,
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
if (hasText) {
|
|
3725
|
+
if (!isString(item.text)) {
|
|
3726
|
+
return invalidFromErrorFactory(
|
|
3727
|
+
errorFactory,
|
|
3728
|
+
`${itemPath}.text must be a string`,
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
continue;
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
if (!isPlainObject(item.reference)) {
|
|
3735
|
+
return invalidFromErrorFactory(
|
|
3736
|
+
errorFactory,
|
|
3737
|
+
`${itemPath}.reference must be an object`,
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
{
|
|
3742
|
+
const result = validateAllowedKeys({
|
|
3743
|
+
value: item.reference,
|
|
3744
|
+
allowedKeys: ["resourceId"],
|
|
3745
|
+
path: `${itemPath}.reference`,
|
|
3746
|
+
errorFactory,
|
|
3747
|
+
});
|
|
3748
|
+
if (result?.valid === false) {
|
|
3749
|
+
return result;
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
if (!isNonEmptyString(item.reference.resourceId)) {
|
|
3754
|
+
return invalidFromErrorFactory(
|
|
3755
|
+
errorFactory,
|
|
3756
|
+
`${itemPath}.reference.resourceId must be a non-empty string`,
|
|
3627
3757
|
);
|
|
3628
3758
|
}
|
|
3629
3759
|
}
|
|
3760
|
+
};
|
|
3761
|
+
|
|
3762
|
+
const getLayoutTextContentReferenceEntries = (content) => {
|
|
3763
|
+
if (!Array.isArray(content)) {
|
|
3764
|
+
return [];
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
const entries = [];
|
|
3768
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
3769
|
+
const resourceId = content[index]?.reference?.resourceId;
|
|
3770
|
+
if (resourceId !== undefined) {
|
|
3771
|
+
entries.push({
|
|
3772
|
+
index,
|
|
3773
|
+
resourceId,
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
return entries;
|
|
3778
|
+
};
|
|
3630
3779
|
|
|
3631
|
-
|
|
3780
|
+
const validateLayoutElementData = ({
|
|
3781
|
+
data,
|
|
3782
|
+
path,
|
|
3783
|
+
errorFactory,
|
|
3784
|
+
allowPartial = false,
|
|
3785
|
+
}) => {
|
|
3786
|
+
if (!isPlainObject(data)) {
|
|
3787
|
+
return invalidFromErrorFactory(errorFactory, `${path} must be an object`);
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3790
|
+
const allowedKeys = [
|
|
3791
|
+
"type",
|
|
3792
|
+
"name",
|
|
3793
|
+
"x",
|
|
3794
|
+
"y",
|
|
3795
|
+
"width",
|
|
3796
|
+
"height",
|
|
3797
|
+
"aspectRatioLock",
|
|
3798
|
+
"anchorX",
|
|
3799
|
+
"anchorY",
|
|
3800
|
+
"scaleX",
|
|
3801
|
+
"scaleY",
|
|
3802
|
+
"rotation",
|
|
3803
|
+
"opacity",
|
|
3804
|
+
"blur",
|
|
3805
|
+
"fill",
|
|
3806
|
+
"border",
|
|
3632
3807
|
"text",
|
|
3808
|
+
"content",
|
|
3809
|
+
"textStyle",
|
|
3810
|
+
"displaySpeed",
|
|
3811
|
+
"revealEffect",
|
|
3812
|
+
"indicator",
|
|
3633
3813
|
"resourceId",
|
|
3634
3814
|
"animationName",
|
|
3635
|
-
"particleId",
|
|
3636
3815
|
"imageId",
|
|
3637
3816
|
"hoverImageId",
|
|
3638
3817
|
"clickImageId",
|
|
@@ -3644,79 +3823,306 @@ const validateLayoutElementData = ({
|
|
|
3644
3823
|
"field",
|
|
3645
3824
|
"value",
|
|
3646
3825
|
"placeholder",
|
|
3826
|
+
"multiline",
|
|
3827
|
+
"disabled",
|
|
3828
|
+
"maxLength",
|
|
3829
|
+
"formRole",
|
|
3830
|
+
"padding",
|
|
3831
|
+
"conditionalOverrides",
|
|
3832
|
+
"direction",
|
|
3833
|
+
"gapX",
|
|
3834
|
+
"gapY",
|
|
3647
3835
|
"containerType",
|
|
3648
|
-
"
|
|
3649
|
-
"
|
|
3650
|
-
"
|
|
3651
|
-
"
|
|
3652
|
-
"
|
|
3836
|
+
"scroll",
|
|
3837
|
+
"hover",
|
|
3838
|
+
"click",
|
|
3839
|
+
"rightClick",
|
|
3840
|
+
"scrollUp",
|
|
3841
|
+
"scrollDown",
|
|
3842
|
+
"submit",
|
|
3843
|
+
"focusEvent",
|
|
3844
|
+
"blurEvent",
|
|
3845
|
+
"selectionChange",
|
|
3846
|
+
"compositionStart",
|
|
3847
|
+
"compositionUpdate",
|
|
3848
|
+
"compositionEnd",
|
|
3849
|
+
"anchorToBottom",
|
|
3653
3850
|
"thumbImageId",
|
|
3654
3851
|
"barImageId",
|
|
3655
3852
|
"hoverThumbImageId",
|
|
3656
3853
|
"hoverBarImageId",
|
|
3854
|
+
"min",
|
|
3855
|
+
"max",
|
|
3856
|
+
"step",
|
|
3857
|
+
"initialValue",
|
|
3858
|
+
"variableId",
|
|
3859
|
+
"particleId",
|
|
3860
|
+
"fragmentLayoutId",
|
|
3861
|
+
"paginationMode",
|
|
3862
|
+
"paginationVariableId",
|
|
3863
|
+
"paginationSize",
|
|
3864
|
+
"choiceItemIndex",
|
|
3657
3865
|
"$when",
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
return invalidFromErrorFactory(
|
|
3661
|
-
errorFactory,
|
|
3662
|
-
`${path}.${key} must be a string when provided`,
|
|
3663
|
-
);
|
|
3664
|
-
}
|
|
3665
|
-
}
|
|
3866
|
+
"change",
|
|
3867
|
+
];
|
|
3666
3868
|
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3869
|
+
{
|
|
3870
|
+
const result = validateAllowedKeys({
|
|
3871
|
+
value: data,
|
|
3872
|
+
allowedKeys,
|
|
3873
|
+
path,
|
|
3874
|
+
errorFactory,
|
|
3875
|
+
});
|
|
3876
|
+
if (result?.valid === false) {
|
|
3877
|
+
return result;
|
|
3676
3878
|
}
|
|
3879
|
+
}
|
|
3677
3880
|
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
!isNonEmptyString(data.animationName)
|
|
3681
|
-
) {
|
|
3881
|
+
if (!allowPartial || data.type !== undefined) {
|
|
3882
|
+
if (!LAYOUT_ELEMENT_BASE_TYPES.includes(data.type)) {
|
|
3682
3883
|
return invalidFromErrorFactory(
|
|
3683
3884
|
errorFactory,
|
|
3684
|
-
`${path}.
|
|
3885
|
+
`${path}.type must be a supported layout element type`,
|
|
3685
3886
|
);
|
|
3686
3887
|
}
|
|
3687
3888
|
}
|
|
3688
3889
|
|
|
3689
|
-
if (data.
|
|
3690
|
-
if (
|
|
3691
|
-
(!allowPartial || data.particleId !== undefined) &&
|
|
3692
|
-
!isNonEmptyString(data.particleId)
|
|
3693
|
-
) {
|
|
3890
|
+
if (!allowPartial || data.name !== undefined) {
|
|
3891
|
+
if (!isNonEmptyString(data.name)) {
|
|
3694
3892
|
return invalidFromErrorFactory(
|
|
3695
3893
|
errorFactory,
|
|
3696
|
-
`${path}.
|
|
3894
|
+
`${path}.name must be a non-empty string`,
|
|
3697
3895
|
);
|
|
3698
3896
|
}
|
|
3699
3897
|
}
|
|
3700
3898
|
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3899
|
+
for (const key of [
|
|
3900
|
+
"x",
|
|
3901
|
+
"y",
|
|
3902
|
+
"width",
|
|
3903
|
+
"height",
|
|
3904
|
+
"anchorX",
|
|
3905
|
+
"anchorY",
|
|
3906
|
+
"scaleX",
|
|
3907
|
+
"scaleY",
|
|
3908
|
+
"rotation",
|
|
3909
|
+
"displaySpeed",
|
|
3910
|
+
"gapX",
|
|
3911
|
+
"gapY",
|
|
3912
|
+
"min",
|
|
3913
|
+
"max",
|
|
3914
|
+
"step",
|
|
3915
|
+
"paginationSize",
|
|
3916
|
+
"choiceItemIndex",
|
|
3917
|
+
"opacity",
|
|
3918
|
+
"maxLength",
|
|
3919
|
+
]) {
|
|
3920
|
+
if (data[key] !== undefined && !isFiniteNumber(data[key])) {
|
|
3706
3921
|
return invalidFromErrorFactory(
|
|
3707
3922
|
errorFactory,
|
|
3708
|
-
`${path}
|
|
3923
|
+
`${path}.${key} must be a finite number when provided`,
|
|
3709
3924
|
);
|
|
3710
3925
|
}
|
|
3711
3926
|
}
|
|
3712
3927
|
|
|
3713
|
-
if (
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3928
|
+
if (
|
|
3929
|
+
data.choiceItemIndex !== undefined &&
|
|
3930
|
+
(!Number.isInteger(data.choiceItemIndex) ||
|
|
3931
|
+
data.choiceItemIndex < 0 ||
|
|
3932
|
+
data.choiceItemIndex > 19)
|
|
3933
|
+
) {
|
|
3934
|
+
return invalidFromErrorFactory(
|
|
3935
|
+
errorFactory,
|
|
3936
|
+
`${path}.choiceItemIndex must be an integer between 0 and 19 when provided`,
|
|
3937
|
+
);
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
if (
|
|
3941
|
+
data.aspectRatioLock !== undefined &&
|
|
3942
|
+
(!isFiniteNumber(data.aspectRatioLock) || data.aspectRatioLock <= 0)
|
|
3943
|
+
) {
|
|
3944
|
+
return invalidFromErrorFactory(
|
|
3945
|
+
errorFactory,
|
|
3946
|
+
`${path}.aspectRatioLock must be a finite number greater than 0 when provided`,
|
|
3947
|
+
);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
if (
|
|
3951
|
+
data.initialValue !== undefined &&
|
|
3952
|
+
!isFiniteNumber(data.initialValue) &&
|
|
3953
|
+
!isString(data.initialValue)
|
|
3954
|
+
) {
|
|
3955
|
+
return invalidFromErrorFactory(
|
|
3956
|
+
errorFactory,
|
|
3957
|
+
`${path}.initialValue must be a finite number or string when provided`,
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
if (
|
|
3962
|
+
data.opacity !== undefined &&
|
|
3963
|
+
(!isFiniteNumber(data.opacity) || data.opacity < 0 || data.opacity > 1)
|
|
3964
|
+
) {
|
|
3965
|
+
return invalidFromErrorFactory(
|
|
3966
|
+
errorFactory,
|
|
3967
|
+
`${path}.opacity must be a finite number between 0 and 1 when provided`,
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
if (data.blur !== undefined) {
|
|
3972
|
+
{
|
|
3973
|
+
const result = validateLayoutElementBlur({
|
|
3974
|
+
blur: data.blur,
|
|
3975
|
+
path: `${path}.blur`,
|
|
3976
|
+
errorFactory,
|
|
3977
|
+
});
|
|
3978
|
+
if (result?.valid === false) {
|
|
3979
|
+
return result;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
if (data.type !== undefined && data.type !== "sprite") {
|
|
3984
|
+
return invalidFromErrorFactory(
|
|
3985
|
+
errorFactory,
|
|
3986
|
+
`${path}.blur can only be provided for sprite elements`,
|
|
3987
|
+
);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
if (data.content !== undefined) {
|
|
3992
|
+
{
|
|
3993
|
+
const result = validateLayoutTextContent({
|
|
3994
|
+
content: data.content,
|
|
3995
|
+
path: `${path}.content`,
|
|
3996
|
+
errorFactory,
|
|
3997
|
+
});
|
|
3998
|
+
if (result?.valid === false) {
|
|
3999
|
+
return result;
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
if (
|
|
4004
|
+
data.type !== undefined &&
|
|
4005
|
+
!LAYOUT_TEXT_CONTENT_ELEMENT_TYPES.includes(data.type)
|
|
4006
|
+
) {
|
|
4007
|
+
return invalidFromErrorFactory(
|
|
4008
|
+
errorFactory,
|
|
4009
|
+
`${path}.content can only be provided for text elements`,
|
|
4010
|
+
);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
if (data.indicator !== undefined) {
|
|
4015
|
+
{
|
|
4016
|
+
const result = validateLayoutTextRevealIndicator({
|
|
4017
|
+
indicator: data.indicator,
|
|
4018
|
+
path: `${path}.indicator`,
|
|
4019
|
+
errorFactory,
|
|
4020
|
+
});
|
|
4021
|
+
if (result?.valid === false) {
|
|
4022
|
+
return result;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
if (
|
|
4027
|
+
data.type !== undefined &&
|
|
4028
|
+
!LAYOUT_TEXT_REVEAL_INDICATOR_ELEMENT_TYPES.includes(data.type)
|
|
4029
|
+
) {
|
|
4030
|
+
return invalidFromErrorFactory(
|
|
4031
|
+
errorFactory,
|
|
4032
|
+
`${path}.indicator can only be provided for text revealing elements`,
|
|
4033
|
+
);
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
for (const key of [
|
|
4038
|
+
"text",
|
|
4039
|
+
"resourceId",
|
|
4040
|
+
"animationName",
|
|
4041
|
+
"particleId",
|
|
4042
|
+
"imageId",
|
|
4043
|
+
"hoverImageId",
|
|
4044
|
+
"clickImageId",
|
|
4045
|
+
"hoverSoundId",
|
|
4046
|
+
"clickSoundId",
|
|
4047
|
+
"textStyleId",
|
|
4048
|
+
"hoverTextStyleId",
|
|
4049
|
+
"clickTextStyleId",
|
|
4050
|
+
"field",
|
|
4051
|
+
"value",
|
|
4052
|
+
"placeholder",
|
|
4053
|
+
"containerType",
|
|
4054
|
+
"variableId",
|
|
4055
|
+
"fragmentLayoutId",
|
|
4056
|
+
"paginationMode",
|
|
4057
|
+
"paginationVariableId",
|
|
4058
|
+
"revealEffect",
|
|
4059
|
+
"thumbImageId",
|
|
4060
|
+
"barImageId",
|
|
4061
|
+
"hoverThumbImageId",
|
|
4062
|
+
"hoverBarImageId",
|
|
4063
|
+
"$when",
|
|
4064
|
+
]) {
|
|
4065
|
+
if (data[key] !== undefined && !isString(data[key])) {
|
|
4066
|
+
return invalidFromErrorFactory(
|
|
4067
|
+
errorFactory,
|
|
4068
|
+
`${path}.${key} must be a string when provided`,
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
if (data.type === "spritesheet-animation") {
|
|
4074
|
+
if (
|
|
4075
|
+
(!allowPartial || data.resourceId !== undefined) &&
|
|
4076
|
+
!isNonEmptyString(data.resourceId)
|
|
4077
|
+
) {
|
|
4078
|
+
return invalidFromErrorFactory(
|
|
4079
|
+
errorFactory,
|
|
4080
|
+
`${path}.resourceId must be a non-empty string`,
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
|
|
4084
|
+
if (
|
|
4085
|
+
(!allowPartial || data.animationName !== undefined) &&
|
|
4086
|
+
!isNonEmptyString(data.animationName)
|
|
4087
|
+
) {
|
|
4088
|
+
return invalidFromErrorFactory(
|
|
4089
|
+
errorFactory,
|
|
4090
|
+
`${path}.animationName must be a non-empty string`,
|
|
4091
|
+
);
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
if (data.type === "particle") {
|
|
4096
|
+
if (
|
|
4097
|
+
(!allowPartial || data.particleId !== undefined) &&
|
|
4098
|
+
!isNonEmptyString(data.particleId)
|
|
4099
|
+
) {
|
|
4100
|
+
return invalidFromErrorFactory(
|
|
4101
|
+
errorFactory,
|
|
4102
|
+
`${path}.particleId must be a non-empty string`,
|
|
4103
|
+
);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
|
|
4107
|
+
if (data.type === "input") {
|
|
4108
|
+
if (
|
|
4109
|
+
(!allowPartial || data.field !== undefined) &&
|
|
4110
|
+
!isNonEmptyString(data.field)
|
|
4111
|
+
) {
|
|
4112
|
+
return invalidFromErrorFactory(
|
|
4113
|
+
errorFactory,
|
|
4114
|
+
`${path}.field must be a non-empty string`,
|
|
4115
|
+
);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
|
|
4119
|
+
if (data.conditionalOverrides !== undefined) {
|
|
4120
|
+
if (!Array.isArray(data.conditionalOverrides)) {
|
|
4121
|
+
return invalidFromErrorFactory(
|
|
4122
|
+
errorFactory,
|
|
4123
|
+
`${path}.conditionalOverrides must be an array when provided`,
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
3720
4126
|
|
|
3721
4127
|
for (let index = 0; index < data.conditionalOverrides.length; index += 1) {
|
|
3722
4128
|
const rule = data.conditionalOverrides[index];
|
|
@@ -4143,9 +4549,11 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
4143
4549
|
"fill",
|
|
4144
4550
|
"border",
|
|
4145
4551
|
"text",
|
|
4552
|
+
"content",
|
|
4146
4553
|
"textStyle",
|
|
4147
4554
|
"displaySpeed",
|
|
4148
4555
|
"revealEffect",
|
|
4556
|
+
"indicator",
|
|
4149
4557
|
"resourceId",
|
|
4150
4558
|
"animationName",
|
|
4151
4559
|
"particleId",
|
|
@@ -6104,6 +6512,17 @@ const validateCollection = ({ collection, path }) => {
|
|
|
6104
6512
|
return result;
|
|
6105
6513
|
}
|
|
6106
6514
|
}
|
|
6515
|
+
} else if (path === "state.voices") {
|
|
6516
|
+
{
|
|
6517
|
+
const result = validateVoiceItems({
|
|
6518
|
+
items: collection.items,
|
|
6519
|
+
path: `${path}.items`,
|
|
6520
|
+
errorFactory: createStateValidationError,
|
|
6521
|
+
});
|
|
6522
|
+
if (result?.valid === false) {
|
|
6523
|
+
return result;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6107
6526
|
} else if (path === "state.videos") {
|
|
6108
6527
|
{
|
|
6109
6528
|
const result = validateVideoItems({
|
|
@@ -6289,6 +6708,18 @@ const validateCollection = ({ collection, path }) => {
|
|
|
6289
6708
|
return result;
|
|
6290
6709
|
}
|
|
6291
6710
|
}
|
|
6711
|
+
} else if (path === "state.voices") {
|
|
6712
|
+
{
|
|
6713
|
+
const result = validateGenericFolderOwnership({
|
|
6714
|
+
nodes: collection.tree,
|
|
6715
|
+
items: collection.items,
|
|
6716
|
+
path: `${path}.tree`,
|
|
6717
|
+
folderLabel: "folder voice item",
|
|
6718
|
+
});
|
|
6719
|
+
if (result?.valid === false) {
|
|
6720
|
+
return result;
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6292
6723
|
} else if (path === "state.videos") {
|
|
6293
6724
|
{
|
|
6294
6725
|
const result = validateVideoTreeFolderOwnership({
|
|
@@ -6809,6 +7240,55 @@ export const assertInvariants = ({ state }) => {
|
|
|
6809
7240
|
}
|
|
6810
7241
|
}
|
|
6811
7242
|
|
|
7243
|
+
for (const [voiceId, voice] of Object.entries(state.voices.items)) {
|
|
7244
|
+
if (voice.type !== "voice") {
|
|
7245
|
+
continue;
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
if (!isPlainObject(sceneItems[voice.sceneId])) {
|
|
7249
|
+
return invalidInvariant(
|
|
7250
|
+
"voice.sceneId must reference an existing scene",
|
|
7251
|
+
{ voiceId, sceneId: voice.sceneId },
|
|
7252
|
+
);
|
|
7253
|
+
}
|
|
7254
|
+
|
|
7255
|
+
if (sceneItems[voice.sceneId].type === "folder") {
|
|
7256
|
+
return invalidInvariant(
|
|
7257
|
+
"voice.sceneId must reference a non-folder scene",
|
|
7258
|
+
{ voiceId, sceneId: voice.sceneId },
|
|
7259
|
+
);
|
|
7260
|
+
}
|
|
7261
|
+
|
|
7262
|
+
{
|
|
7263
|
+
const result = validateFileReference({
|
|
7264
|
+
state,
|
|
7265
|
+
fileId: voice.fileId,
|
|
7266
|
+
path: "voice.fileId",
|
|
7267
|
+
details: { voiceId, fileId: voice.fileId },
|
|
7268
|
+
errorFactory: createInvariantValidationError,
|
|
7269
|
+
});
|
|
7270
|
+
if (!result.valid) {
|
|
7271
|
+
return result;
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
7274
|
+
|
|
7275
|
+
if (
|
|
7276
|
+
voice.waveformDataFileId !== undefined &&
|
|
7277
|
+
voice.waveformDataFileId !== null
|
|
7278
|
+
) {
|
|
7279
|
+
const result = validateFileReference({
|
|
7280
|
+
state,
|
|
7281
|
+
fileId: voice.waveformDataFileId,
|
|
7282
|
+
path: "voice.waveformDataFileId",
|
|
7283
|
+
details: { voiceId, waveformDataFileId: voice.waveformDataFileId },
|
|
7284
|
+
errorFactory: createInvariantValidationError,
|
|
7285
|
+
});
|
|
7286
|
+
if (!result.valid) {
|
|
7287
|
+
return result;
|
|
7288
|
+
}
|
|
7289
|
+
}
|
|
7290
|
+
}
|
|
7291
|
+
|
|
6812
7292
|
for (const [videoId, video] of Object.entries(state.videos.items)) {
|
|
6813
7293
|
if (video.type !== "video") {
|
|
6814
7294
|
continue;
|
|
@@ -7420,15 +7900,16 @@ export const assertInvariants = ({ state }) => {
|
|
|
7420
7900
|
elementId,
|
|
7421
7901
|
targetId,
|
|
7422
7902
|
animationName,
|
|
7903
|
+
fieldPrefix = "",
|
|
7423
7904
|
}) => {
|
|
7424
7905
|
const spritesheet = state.spritesheets?.items?.[targetId];
|
|
7425
7906
|
if (!isPlainObject(spritesheet) || spritesheet.type === "folder") {
|
|
7426
7907
|
return invalidInvariant(
|
|
7427
|
-
`${ownerLabel} element resourceId must reference an existing non-folder spritesheet`,
|
|
7908
|
+
`${ownerLabel} element ${fieldPrefix}resourceId must reference an existing non-folder spritesheet`,
|
|
7428
7909
|
{
|
|
7429
7910
|
[ownerIdField]: ownerId,
|
|
7430
7911
|
elementId,
|
|
7431
|
-
field:
|
|
7912
|
+
field: `${fieldPrefix}resourceId`,
|
|
7432
7913
|
targetId,
|
|
7433
7914
|
},
|
|
7434
7915
|
);
|
|
@@ -7439,11 +7920,11 @@ export const assertInvariants = ({ state }) => {
|
|
|
7439
7920
|
!isPlainObject(spritesheet.animations?.[animationName])
|
|
7440
7921
|
) {
|
|
7441
7922
|
return invalidInvariant(
|
|
7442
|
-
`${ownerLabel} element animationName must reference an existing spritesheet animation`,
|
|
7923
|
+
`${ownerLabel} element ${fieldPrefix}animationName must reference an existing spritesheet animation`,
|
|
7443
7924
|
{
|
|
7444
7925
|
[ownerIdField]: ownerId,
|
|
7445
7926
|
elementId,
|
|
7446
|
-
field:
|
|
7927
|
+
field: `${fieldPrefix}animationName`,
|
|
7447
7928
|
targetId: animationName,
|
|
7448
7929
|
},
|
|
7449
7930
|
);
|
|
@@ -7542,29 +8023,66 @@ export const assertInvariants = ({ state }) => {
|
|
|
7542
8023
|
}
|
|
7543
8024
|
}
|
|
7544
8025
|
|
|
7545
|
-
for (const
|
|
7546
|
-
|
|
7547
|
-
|
|
8026
|
+
for (const stateName of ["revealing", "complete"]) {
|
|
8027
|
+
const visual = element.indicator?.[stateName];
|
|
8028
|
+
if (
|
|
8029
|
+
visual?.kind === "spritesheet" ||
|
|
8030
|
+
visual?.resourceId !== undefined ||
|
|
8031
|
+
visual?.animationName !== undefined
|
|
8032
|
+
) {
|
|
8033
|
+
const result = assertSpritesheetAnimationReference({
|
|
7548
8034
|
ownerIdField,
|
|
7549
8035
|
ownerId,
|
|
7550
8036
|
ownerLabel,
|
|
7551
8037
|
elementId,
|
|
7552
|
-
|
|
7553
|
-
|
|
8038
|
+
targetId: visual.resourceId,
|
|
8039
|
+
animationName: visual.animationName,
|
|
8040
|
+
fieldPrefix: `indicator.${stateName}.`,
|
|
7554
8041
|
});
|
|
7555
8042
|
if (!result.valid) {
|
|
7556
8043
|
return result;
|
|
7557
8044
|
}
|
|
8045
|
+
continue;
|
|
7558
8046
|
}
|
|
7559
|
-
}
|
|
7560
8047
|
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
8048
|
+
if (visual?.imageId !== undefined) {
|
|
8049
|
+
const result = assertImageReference({
|
|
8050
|
+
ownerIdField,
|
|
8051
|
+
ownerId,
|
|
8052
|
+
ownerLabel,
|
|
8053
|
+
elementId,
|
|
8054
|
+
field: `indicator.${stateName}.imageId`,
|
|
8055
|
+
targetId: visual.imageId,
|
|
8056
|
+
});
|
|
8057
|
+
if (!result.valid) {
|
|
8058
|
+
return result;
|
|
8059
|
+
}
|
|
8060
|
+
}
|
|
8061
|
+
}
|
|
8062
|
+
|
|
8063
|
+
for (const field of ["hoverSoundId", "clickSoundId"]) {
|
|
8064
|
+
if (element[field] !== undefined) {
|
|
8065
|
+
const result = assertSoundReference({
|
|
8066
|
+
ownerIdField,
|
|
8067
|
+
ownerId,
|
|
8068
|
+
ownerLabel,
|
|
8069
|
+
elementId,
|
|
8070
|
+
field,
|
|
8071
|
+
targetId: element[field],
|
|
8072
|
+
});
|
|
8073
|
+
if (!result.valid) {
|
|
8074
|
+
return result;
|
|
8075
|
+
}
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
|
|
8079
|
+
for (const field of [
|
|
8080
|
+
"textStyleId",
|
|
8081
|
+
"hoverTextStyleId",
|
|
8082
|
+
"clickTextStyleId",
|
|
8083
|
+
]) {
|
|
8084
|
+
if (element[field] !== undefined) {
|
|
8085
|
+
const result = assertTextStyleReference({
|
|
7568
8086
|
ownerIdField,
|
|
7569
8087
|
ownerId,
|
|
7570
8088
|
ownerLabel,
|
|
@@ -7639,6 +8157,23 @@ export const assertInvariants = ({ state }) => {
|
|
|
7639
8157
|
}
|
|
7640
8158
|
}
|
|
7641
8159
|
|
|
8160
|
+
for (const {
|
|
8161
|
+
index,
|
|
8162
|
+
resourceId,
|
|
8163
|
+
} of getLayoutTextContentReferenceEntries(element.content)) {
|
|
8164
|
+
if (!isVariableReferenceTarget(state, resourceId)) {
|
|
8165
|
+
return invalidInvariant(
|
|
8166
|
+
`${ownerLabel} element content.${index}.reference.resourceId must reference an existing non-folder variable`,
|
|
8167
|
+
{
|
|
8168
|
+
[ownerIdField]: ownerId,
|
|
8169
|
+
elementId,
|
|
8170
|
+
field: `content.${index}.reference.resourceId`,
|
|
8171
|
+
targetId: resourceId,
|
|
8172
|
+
},
|
|
8173
|
+
);
|
|
8174
|
+
}
|
|
8175
|
+
}
|
|
8176
|
+
|
|
7642
8177
|
if (element.variableId !== undefined) {
|
|
7643
8178
|
const result = assertVariableReference({
|
|
7644
8179
|
ownerIdField,
|
|
@@ -8912,6 +9447,167 @@ const validateSoundUpdateData = ({ data, errorFactory }) => {
|
|
|
8912
9447
|
}
|
|
8913
9448
|
};
|
|
8914
9449
|
|
|
9450
|
+
const validateVoiceCreateData = ({ data, errorFactory }) => {
|
|
9451
|
+
if (!isPlainObject(data)) {
|
|
9452
|
+
return invalidFromErrorFactory(
|
|
9453
|
+
errorFactory,
|
|
9454
|
+
"payload.data must be an object",
|
|
9455
|
+
);
|
|
9456
|
+
}
|
|
9457
|
+
|
|
9458
|
+
if (data.type !== "folder" && data.type !== "voice") {
|
|
9459
|
+
return invalidFromErrorFactory(
|
|
9460
|
+
errorFactory,
|
|
9461
|
+
"payload.data.type must be 'folder' or 'voice'",
|
|
9462
|
+
);
|
|
9463
|
+
}
|
|
9464
|
+
|
|
9465
|
+
{
|
|
9466
|
+
const result = validateAllowedKeys({
|
|
9467
|
+
value: data,
|
|
9468
|
+
allowedKeys:
|
|
9469
|
+
data.type === "folder"
|
|
9470
|
+
? ["type", "name", "description"]
|
|
9471
|
+
: [
|
|
9472
|
+
"type",
|
|
9473
|
+
"name",
|
|
9474
|
+
"description",
|
|
9475
|
+
"sceneId",
|
|
9476
|
+
"fileId",
|
|
9477
|
+
"waveformDataFileId",
|
|
9478
|
+
"duration",
|
|
9479
|
+
],
|
|
9480
|
+
path: "payload.data",
|
|
9481
|
+
errorFactory,
|
|
9482
|
+
});
|
|
9483
|
+
if (result?.valid === false) {
|
|
9484
|
+
return result;
|
|
9485
|
+
}
|
|
9486
|
+
}
|
|
9487
|
+
|
|
9488
|
+
if (!isNonEmptyString(data.name)) {
|
|
9489
|
+
return invalidFromErrorFactory(
|
|
9490
|
+
errorFactory,
|
|
9491
|
+
"payload.data.name must be a non-empty string",
|
|
9492
|
+
);
|
|
9493
|
+
}
|
|
9494
|
+
|
|
9495
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
9496
|
+
return invalidFromErrorFactory(
|
|
9497
|
+
errorFactory,
|
|
9498
|
+
"payload.data.description must be a string when provided",
|
|
9499
|
+
);
|
|
9500
|
+
}
|
|
9501
|
+
|
|
9502
|
+
if (data.type === "voice") {
|
|
9503
|
+
if (!isNonEmptyString(data.sceneId)) {
|
|
9504
|
+
return invalidFromErrorFactory(
|
|
9505
|
+
errorFactory,
|
|
9506
|
+
"payload.data.sceneId must be a non-empty string",
|
|
9507
|
+
);
|
|
9508
|
+
}
|
|
9509
|
+
|
|
9510
|
+
if (!isNonEmptyString(data.fileId)) {
|
|
9511
|
+
return invalidFromErrorFactory(
|
|
9512
|
+
errorFactory,
|
|
9513
|
+
"payload.data.fileId must be a non-empty string",
|
|
9514
|
+
);
|
|
9515
|
+
}
|
|
9516
|
+
|
|
9517
|
+
if (
|
|
9518
|
+
data.waveformDataFileId !== undefined &&
|
|
9519
|
+
data.waveformDataFileId !== null &&
|
|
9520
|
+
!isNonEmptyString(data.waveformDataFileId)
|
|
9521
|
+
) {
|
|
9522
|
+
return invalidFromErrorFactory(
|
|
9523
|
+
errorFactory,
|
|
9524
|
+
"payload.data.waveformDataFileId must be a non-empty string or null when provided",
|
|
9525
|
+
);
|
|
9526
|
+
}
|
|
9527
|
+
|
|
9528
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
9529
|
+
return invalidFromErrorFactory(
|
|
9530
|
+
errorFactory,
|
|
9531
|
+
"payload.data.duration must be a finite number",
|
|
9532
|
+
);
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
};
|
|
9536
|
+
|
|
9537
|
+
const validateVoiceUpdateData = ({ data, errorFactory }) => {
|
|
9538
|
+
{
|
|
9539
|
+
const result = validateAllowedKeys({
|
|
9540
|
+
value: data,
|
|
9541
|
+
allowedKeys: [
|
|
9542
|
+
"name",
|
|
9543
|
+
"description",
|
|
9544
|
+
"sceneId",
|
|
9545
|
+
"fileId",
|
|
9546
|
+
"waveformDataFileId",
|
|
9547
|
+
"duration",
|
|
9548
|
+
],
|
|
9549
|
+
path: "payload.data",
|
|
9550
|
+
errorFactory,
|
|
9551
|
+
});
|
|
9552
|
+
if (result?.valid === false) {
|
|
9553
|
+
return result;
|
|
9554
|
+
}
|
|
9555
|
+
}
|
|
9556
|
+
|
|
9557
|
+
if (Object.keys(data).length === 0) {
|
|
9558
|
+
return invalidFromErrorFactory(
|
|
9559
|
+
errorFactory,
|
|
9560
|
+
"payload.data must include at least one updatable field",
|
|
9561
|
+
);
|
|
9562
|
+
}
|
|
9563
|
+
|
|
9564
|
+
if (data.name !== undefined && !isNonEmptyString(data.name)) {
|
|
9565
|
+
return invalidFromErrorFactory(
|
|
9566
|
+
errorFactory,
|
|
9567
|
+
"payload.data.name must be a non-empty string when provided",
|
|
9568
|
+
);
|
|
9569
|
+
}
|
|
9570
|
+
|
|
9571
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
9572
|
+
return invalidFromErrorFactory(
|
|
9573
|
+
errorFactory,
|
|
9574
|
+
"payload.data.description must be a string when provided",
|
|
9575
|
+
);
|
|
9576
|
+
}
|
|
9577
|
+
|
|
9578
|
+
if (data.sceneId !== undefined && !isNonEmptyString(data.sceneId)) {
|
|
9579
|
+
return invalidFromErrorFactory(
|
|
9580
|
+
errorFactory,
|
|
9581
|
+
"payload.data.sceneId must be a non-empty string when provided",
|
|
9582
|
+
);
|
|
9583
|
+
}
|
|
9584
|
+
|
|
9585
|
+
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
9586
|
+
return invalidFromErrorFactory(
|
|
9587
|
+
errorFactory,
|
|
9588
|
+
"payload.data.fileId must be a non-empty string when provided",
|
|
9589
|
+
);
|
|
9590
|
+
}
|
|
9591
|
+
|
|
9592
|
+
if (
|
|
9593
|
+
data.waveformDataFileId !== undefined &&
|
|
9594
|
+
data.waveformDataFileId !== null &&
|
|
9595
|
+
!isNonEmptyString(data.waveformDataFileId)
|
|
9596
|
+
) {
|
|
9597
|
+
return invalidFromErrorFactory(
|
|
9598
|
+
errorFactory,
|
|
9599
|
+
"payload.data.waveformDataFileId must be a non-empty string or null when provided",
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9602
|
+
|
|
9603
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
9604
|
+
return invalidFromErrorFactory(
|
|
9605
|
+
errorFactory,
|
|
9606
|
+
"payload.data.duration must be a finite number",
|
|
9607
|
+
);
|
|
9608
|
+
}
|
|
9609
|
+
};
|
|
9610
|
+
|
|
8915
9611
|
const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
8916
9612
|
if (!isPlainObject(data)) {
|
|
8917
9613
|
return invalidFromErrorFactory(
|
|
@@ -11383,6 +12079,36 @@ const validateVisualElementReferenceTargets = ({
|
|
|
11383
12079
|
);
|
|
11384
12080
|
}
|
|
11385
12081
|
|
|
12082
|
+
if (
|
|
12083
|
+
data.indicator !== undefined &&
|
|
12084
|
+
!LAYOUT_TEXT_REVEAL_INDICATOR_ELEMENT_TYPES.includes(data.type)
|
|
12085
|
+
) {
|
|
12086
|
+
return invalidFromErrorFactory(
|
|
12087
|
+
errorFactory,
|
|
12088
|
+
`${ownerLabel} element indicator can only be provided for text revealing elements`,
|
|
12089
|
+
{
|
|
12090
|
+
[ownerIdField]: ownerId,
|
|
12091
|
+
elementId,
|
|
12092
|
+
field: "indicator",
|
|
12093
|
+
},
|
|
12094
|
+
);
|
|
12095
|
+
}
|
|
12096
|
+
|
|
12097
|
+
if (
|
|
12098
|
+
data.content !== undefined &&
|
|
12099
|
+
!LAYOUT_TEXT_CONTENT_ELEMENT_TYPES.includes(data.type)
|
|
12100
|
+
) {
|
|
12101
|
+
return invalidFromErrorFactory(
|
|
12102
|
+
errorFactory,
|
|
12103
|
+
`${ownerLabel} element content can only be provided for text elements`,
|
|
12104
|
+
{
|
|
12105
|
+
[ownerIdField]: ownerId,
|
|
12106
|
+
elementId,
|
|
12107
|
+
field: "content",
|
|
12108
|
+
},
|
|
12109
|
+
);
|
|
12110
|
+
}
|
|
12111
|
+
|
|
11386
12112
|
if (
|
|
11387
12113
|
data.type === "spritesheet-animation" ||
|
|
11388
12114
|
data.resourceId !== undefined ||
|
|
@@ -11483,6 +12209,66 @@ const validateVisualElementReferenceTargets = ({
|
|
|
11483
12209
|
}
|
|
11484
12210
|
}
|
|
11485
12211
|
|
|
12212
|
+
for (const stateName of ["revealing", "complete"]) {
|
|
12213
|
+
const visual = data.indicator?.[stateName];
|
|
12214
|
+
if (
|
|
12215
|
+
visual?.kind === "spritesheet" ||
|
|
12216
|
+
visual?.resourceId !== undefined ||
|
|
12217
|
+
visual?.animationName !== undefined
|
|
12218
|
+
) {
|
|
12219
|
+
const spritesheet = state.spritesheets?.items?.[visual.resourceId];
|
|
12220
|
+
if (!isPlainObject(spritesheet) || spritesheet.type === "folder") {
|
|
12221
|
+
return invalidFromErrorFactory(
|
|
12222
|
+
errorFactory,
|
|
12223
|
+
`${ownerLabel} element indicator.${stateName}.resourceId must reference an existing non-folder spritesheet`,
|
|
12224
|
+
{
|
|
12225
|
+
[ownerIdField]: ownerId,
|
|
12226
|
+
elementId,
|
|
12227
|
+
field: `indicator.${stateName}.resourceId`,
|
|
12228
|
+
targetId: visual.resourceId,
|
|
12229
|
+
},
|
|
12230
|
+
);
|
|
12231
|
+
}
|
|
12232
|
+
|
|
12233
|
+
if (
|
|
12234
|
+
!isNonEmptyString(visual.animationName) ||
|
|
12235
|
+
!isPlainObject(spritesheet.animations?.[visual.animationName])
|
|
12236
|
+
) {
|
|
12237
|
+
return invalidFromErrorFactory(
|
|
12238
|
+
errorFactory,
|
|
12239
|
+
`${ownerLabel} element indicator.${stateName}.animationName must reference an existing spritesheet animation`,
|
|
12240
|
+
{
|
|
12241
|
+
[ownerIdField]: ownerId,
|
|
12242
|
+
elementId,
|
|
12243
|
+
field: `indicator.${stateName}.animationName`,
|
|
12244
|
+
targetId: visual.animationName,
|
|
12245
|
+
},
|
|
12246
|
+
);
|
|
12247
|
+
}
|
|
12248
|
+
|
|
12249
|
+
continue;
|
|
12250
|
+
}
|
|
12251
|
+
|
|
12252
|
+
const imageId = visual?.imageId;
|
|
12253
|
+
if (imageId === undefined) {
|
|
12254
|
+
continue;
|
|
12255
|
+
}
|
|
12256
|
+
|
|
12257
|
+
const image = state.images.items[imageId];
|
|
12258
|
+
if (!isPlainObject(image) || image.type === "folder") {
|
|
12259
|
+
return invalidFromErrorFactory(
|
|
12260
|
+
errorFactory,
|
|
12261
|
+
`${ownerLabel} element indicator.${stateName}.imageId must reference an existing non-folder image`,
|
|
12262
|
+
{
|
|
12263
|
+
[ownerIdField]: ownerId,
|
|
12264
|
+
elementId,
|
|
12265
|
+
field: `indicator.${stateName}.imageId`,
|
|
12266
|
+
targetId: imageId,
|
|
12267
|
+
},
|
|
12268
|
+
);
|
|
12269
|
+
}
|
|
12270
|
+
}
|
|
12271
|
+
|
|
11486
12272
|
if (data.hoverSoundId !== undefined) {
|
|
11487
12273
|
const sound = state.sounds.items[data.hoverSoundId];
|
|
11488
12274
|
if (!isPlainObject(sound) || sound.type === "folder") {
|
|
@@ -11710,22 +12496,39 @@ const validateVisualElementReferenceTargets = ({
|
|
|
11710
12496
|
}
|
|
11711
12497
|
}
|
|
11712
12498
|
|
|
11713
|
-
|
|
11714
|
-
|
|
12499
|
+
for (const { index, resourceId } of getLayoutTextContentReferenceEntries(
|
|
12500
|
+
data.content,
|
|
12501
|
+
)) {
|
|
12502
|
+
if (!isVariableReferenceTarget(state, resourceId)) {
|
|
11715
12503
|
return invalidFromErrorFactory(
|
|
11716
12504
|
errorFactory,
|
|
11717
|
-
`${ownerLabel} element
|
|
12505
|
+
`${ownerLabel} element content.${index}.reference.resourceId must reference an existing non-folder variable`,
|
|
11718
12506
|
{
|
|
11719
12507
|
[ownerIdField]: ownerId,
|
|
11720
12508
|
elementId,
|
|
11721
|
-
|
|
12509
|
+
field: `content.${index}.reference.resourceId`,
|
|
12510
|
+
targetId: resourceId,
|
|
11722
12511
|
},
|
|
11723
12512
|
);
|
|
11724
12513
|
}
|
|
11725
12514
|
}
|
|
11726
12515
|
|
|
11727
|
-
if (data.
|
|
11728
|
-
|
|
12516
|
+
if (data.variableId !== undefined) {
|
|
12517
|
+
if (!isVariableReferenceTarget(state, data.variableId)) {
|
|
12518
|
+
return invalidFromErrorFactory(
|
|
12519
|
+
errorFactory,
|
|
12520
|
+
`${ownerLabel} element variableId must reference an existing non-folder variable`,
|
|
12521
|
+
{
|
|
12522
|
+
[ownerIdField]: ownerId,
|
|
12523
|
+
elementId,
|
|
12524
|
+
variableId: data.variableId,
|
|
12525
|
+
},
|
|
12526
|
+
);
|
|
12527
|
+
}
|
|
12528
|
+
}
|
|
12529
|
+
|
|
12530
|
+
if (data.fragmentLayoutId !== undefined) {
|
|
12531
|
+
const fragmentLayout = state.layouts.items[data.fragmentLayoutId];
|
|
11729
12532
|
if (
|
|
11730
12533
|
!isPlainObject(fragmentLayout) ||
|
|
11731
12534
|
fragmentLayout.type !== "layout" ||
|
|
@@ -12385,6 +13188,28 @@ const findReferencedFileUsage = ({ state, fileId }) => {
|
|
|
12385
13188
|
}
|
|
12386
13189
|
}
|
|
12387
13190
|
|
|
13191
|
+
for (const [voiceId, voice] of Object.entries(state.voices.items)) {
|
|
13192
|
+
if (voice.type !== "voice") {
|
|
13193
|
+
continue;
|
|
13194
|
+
}
|
|
13195
|
+
|
|
13196
|
+
if (voice.fileId === fileId) {
|
|
13197
|
+
return {
|
|
13198
|
+
kind: "voice",
|
|
13199
|
+
field: "fileId",
|
|
13200
|
+
ownerId: voiceId,
|
|
13201
|
+
};
|
|
13202
|
+
}
|
|
13203
|
+
|
|
13204
|
+
if (voice.waveformDataFileId === fileId) {
|
|
13205
|
+
return {
|
|
13206
|
+
kind: "voice",
|
|
13207
|
+
field: "waveformDataFileId",
|
|
13208
|
+
ownerId: voiceId,
|
|
13209
|
+
};
|
|
13210
|
+
}
|
|
13211
|
+
}
|
|
13212
|
+
|
|
12388
13213
|
for (const [videoId, video] of Object.entries(state.videos.items)) {
|
|
12389
13214
|
if (video.type !== "video") {
|
|
12390
13215
|
continue;
|
|
@@ -12775,31 +13600,425 @@ const COMMAND_DEFINITIONS = [
|
|
|
12775
13600
|
return;
|
|
12776
13601
|
}
|
|
12777
13602
|
|
|
12778
|
-
const scene = state.scenes.items[initialSceneId];
|
|
12779
|
-
if (!isPlainObject(scene)) {
|
|
12780
|
-
return invalidPrecondition(
|
|
12781
|
-
"payload.data.initialSceneId must reference an existing scene",
|
|
12782
|
-
);
|
|
12783
|
-
}
|
|
13603
|
+
const scene = state.scenes.items[initialSceneId];
|
|
13604
|
+
if (!isPlainObject(scene)) {
|
|
13605
|
+
return invalidPrecondition(
|
|
13606
|
+
"payload.data.initialSceneId must reference an existing scene",
|
|
13607
|
+
);
|
|
13608
|
+
}
|
|
13609
|
+
|
|
13610
|
+
if (scene.type === "folder") {
|
|
13611
|
+
return invalidPrecondition(
|
|
13612
|
+
"payload.data.initialSceneId must reference a non-folder scene",
|
|
13613
|
+
);
|
|
13614
|
+
}
|
|
13615
|
+
},
|
|
13616
|
+
reduce: ({ state, payload }) => {
|
|
13617
|
+
state.story.initialSceneId = payload.data.initialSceneId;
|
|
13618
|
+
return state;
|
|
13619
|
+
},
|
|
13620
|
+
},
|
|
13621
|
+
{
|
|
13622
|
+
type: "scene.create",
|
|
13623
|
+
validatePayload: ({ payload }) => {
|
|
13624
|
+
{
|
|
13625
|
+
const result = validateAllowedKeys({
|
|
13626
|
+
value: payload,
|
|
13627
|
+
allowedKeys: [
|
|
13628
|
+
"sceneId",
|
|
13629
|
+
"parentId",
|
|
13630
|
+
"data",
|
|
13631
|
+
"index",
|
|
13632
|
+
"position",
|
|
13633
|
+
"positionTargetId",
|
|
13634
|
+
],
|
|
13635
|
+
path: "payload",
|
|
13636
|
+
errorFactory: createPayloadValidationError,
|
|
13637
|
+
});
|
|
13638
|
+
if (result?.valid === false) {
|
|
13639
|
+
return result;
|
|
13640
|
+
}
|
|
13641
|
+
}
|
|
13642
|
+
|
|
13643
|
+
if (!isNonEmptyString(payload.sceneId)) {
|
|
13644
|
+
return invalidPayload("payload.sceneId must be a non-empty string");
|
|
13645
|
+
}
|
|
13646
|
+
|
|
13647
|
+
if (
|
|
13648
|
+
payload.parentId !== undefined &&
|
|
13649
|
+
payload.parentId !== null &&
|
|
13650
|
+
!isNonEmptyString(payload.parentId)
|
|
13651
|
+
) {
|
|
13652
|
+
return invalidPayload(
|
|
13653
|
+
"payload.parentId must be a non-empty string when provided",
|
|
13654
|
+
);
|
|
13655
|
+
}
|
|
13656
|
+
|
|
13657
|
+
{
|
|
13658
|
+
const result = validateSceneCreateData({
|
|
13659
|
+
data: payload.data,
|
|
13660
|
+
errorFactory: createPayloadValidationError,
|
|
13661
|
+
});
|
|
13662
|
+
if (result?.valid === false) {
|
|
13663
|
+
return result;
|
|
13664
|
+
}
|
|
13665
|
+
}
|
|
13666
|
+
|
|
13667
|
+
{
|
|
13668
|
+
const result = validatePlacementFields({
|
|
13669
|
+
payload,
|
|
13670
|
+
errorFactory: createPayloadValidationError,
|
|
13671
|
+
});
|
|
13672
|
+
if (result?.valid === false) {
|
|
13673
|
+
return result;
|
|
13674
|
+
}
|
|
13675
|
+
}
|
|
13676
|
+
},
|
|
13677
|
+
validateAgainstState: ({ state, payload }) => {
|
|
13678
|
+
if (Object.hasOwn(state.scenes.items, payload.sceneId)) {
|
|
13679
|
+
return invalidPrecondition("payload.sceneId must not already exist");
|
|
13680
|
+
}
|
|
13681
|
+
|
|
13682
|
+
const parentId = payload.parentId ?? null;
|
|
13683
|
+
|
|
13684
|
+
if (parentId !== null) {
|
|
13685
|
+
const parentScene = state.scenes.items[parentId];
|
|
13686
|
+
if (!isPlainObject(parentScene)) {
|
|
13687
|
+
return invalidPrecondition(
|
|
13688
|
+
"payload.parentId must reference an existing scene",
|
|
13689
|
+
);
|
|
13690
|
+
}
|
|
13691
|
+
|
|
13692
|
+
if (parentScene.type !== "folder") {
|
|
13693
|
+
return invalidPrecondition(
|
|
13694
|
+
"payload.parentId must reference a folder scene",
|
|
13695
|
+
);
|
|
13696
|
+
}
|
|
13697
|
+
}
|
|
13698
|
+
|
|
13699
|
+
if (payload.positionTargetId !== undefined) {
|
|
13700
|
+
if (!isPlainObject(state.scenes.items[payload.positionTargetId])) {
|
|
13701
|
+
return invalidPrecondition(
|
|
13702
|
+
"payload.positionTargetId must reference an existing scene",
|
|
13703
|
+
);
|
|
13704
|
+
}
|
|
13705
|
+
|
|
13706
|
+
const targetParentId =
|
|
13707
|
+
findTreeParentId({
|
|
13708
|
+
nodes: state.scenes.tree,
|
|
13709
|
+
nodeId: payload.positionTargetId,
|
|
13710
|
+
}) ?? null;
|
|
13711
|
+
|
|
13712
|
+
if (targetParentId !== parentId) {
|
|
13713
|
+
return invalidPrecondition(
|
|
13714
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
13715
|
+
);
|
|
13716
|
+
}
|
|
13717
|
+
}
|
|
13718
|
+
},
|
|
13719
|
+
reduce: ({ state, payload }) => {
|
|
13720
|
+
const nextScene = {
|
|
13721
|
+
id: payload.sceneId,
|
|
13722
|
+
type: payload.data.type ?? "scene",
|
|
13723
|
+
name: payload.data.name,
|
|
13724
|
+
};
|
|
13725
|
+
|
|
13726
|
+
if (payload.data.description !== undefined) {
|
|
13727
|
+
nextScene.description = payload.data.description;
|
|
13728
|
+
}
|
|
13729
|
+
|
|
13730
|
+
if (nextScene.type === "scene") {
|
|
13731
|
+
nextScene.sections = createEmptyNestedCollection();
|
|
13732
|
+
}
|
|
13733
|
+
|
|
13734
|
+
if (payload.data.position !== undefined) {
|
|
13735
|
+
nextScene.position = structuredClone(payload.data.position);
|
|
13736
|
+
}
|
|
13737
|
+
|
|
13738
|
+
state.scenes.items[payload.sceneId] = nextScene;
|
|
13739
|
+
|
|
13740
|
+
insertTreeNode({
|
|
13741
|
+
tree: state.scenes.tree,
|
|
13742
|
+
node: {
|
|
13743
|
+
id: payload.sceneId,
|
|
13744
|
+
children: [],
|
|
13745
|
+
},
|
|
13746
|
+
parentId: payload.parentId ?? null,
|
|
13747
|
+
index: payload.index,
|
|
13748
|
+
position: payload.position,
|
|
13749
|
+
positionTargetId: payload.positionTargetId,
|
|
13750
|
+
});
|
|
13751
|
+
|
|
13752
|
+
return state;
|
|
13753
|
+
},
|
|
13754
|
+
},
|
|
13755
|
+
{
|
|
13756
|
+
type: "scene.update",
|
|
13757
|
+
validatePayload: ({ payload }) => {
|
|
13758
|
+
{
|
|
13759
|
+
const result = validateExactKeys({
|
|
13760
|
+
value: payload,
|
|
13761
|
+
expectedKeys: ["sceneId", "data"],
|
|
13762
|
+
path: "payload",
|
|
13763
|
+
errorFactory: createPayloadValidationError,
|
|
13764
|
+
});
|
|
13765
|
+
if (result?.valid === false) {
|
|
13766
|
+
return result;
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
|
|
13770
|
+
if (!isNonEmptyString(payload.sceneId)) {
|
|
13771
|
+
return invalidPayload("payload.sceneId must be a non-empty string");
|
|
13772
|
+
}
|
|
13773
|
+
|
|
13774
|
+
{
|
|
13775
|
+
const result = validateSceneUpdateData({
|
|
13776
|
+
data: payload.data,
|
|
13777
|
+
errorFactory: createPayloadValidationError,
|
|
13778
|
+
});
|
|
13779
|
+
if (result?.valid === false) {
|
|
13780
|
+
return result;
|
|
13781
|
+
}
|
|
13782
|
+
}
|
|
13783
|
+
},
|
|
13784
|
+
validateAgainstState: ({ state, payload }) => {
|
|
13785
|
+
if (!isPlainObject(state.scenes.items[payload.sceneId])) {
|
|
13786
|
+
return invalidPrecondition(
|
|
13787
|
+
"payload.sceneId must reference an existing scene",
|
|
13788
|
+
);
|
|
13789
|
+
}
|
|
13790
|
+
},
|
|
13791
|
+
reduce: ({ state, payload }) => {
|
|
13792
|
+
const currentScene = state.scenes.items[payload.sceneId];
|
|
13793
|
+
const nextScene = structuredClone(currentScene);
|
|
13794
|
+
|
|
13795
|
+
if (payload.data.name !== undefined) {
|
|
13796
|
+
nextScene.name = payload.data.name;
|
|
13797
|
+
}
|
|
13798
|
+
|
|
13799
|
+
if (payload.data.description !== undefined) {
|
|
13800
|
+
nextScene.description = payload.data.description;
|
|
13801
|
+
}
|
|
13802
|
+
|
|
13803
|
+
if (payload.data.position !== undefined) {
|
|
13804
|
+
nextScene.position = {
|
|
13805
|
+
...(isPlainObject(nextScene.position) ? nextScene.position : {}),
|
|
13806
|
+
...structuredClone(payload.data.position),
|
|
13807
|
+
};
|
|
13808
|
+
}
|
|
13809
|
+
|
|
13810
|
+
state.scenes.items[payload.sceneId] = nextScene;
|
|
13811
|
+
return state;
|
|
13812
|
+
},
|
|
13813
|
+
},
|
|
13814
|
+
{
|
|
13815
|
+
type: "scene.delete",
|
|
13816
|
+
validatePayload: ({ payload }) => {
|
|
13817
|
+
{
|
|
13818
|
+
const result = validateExactKeys({
|
|
13819
|
+
value: payload,
|
|
13820
|
+
expectedKeys: ["sceneIds"],
|
|
13821
|
+
path: "payload",
|
|
13822
|
+
errorFactory: createPayloadValidationError,
|
|
13823
|
+
});
|
|
13824
|
+
if (result?.valid === false) {
|
|
13825
|
+
return result;
|
|
13826
|
+
}
|
|
13827
|
+
}
|
|
13828
|
+
|
|
13829
|
+
{
|
|
13830
|
+
const result = validateRequiredUniqueIdArray({
|
|
13831
|
+
value: payload.sceneIds,
|
|
13832
|
+
path: "payload.sceneIds",
|
|
13833
|
+
errorFactory: createPayloadValidationError,
|
|
13834
|
+
});
|
|
13835
|
+
if (result?.valid === false) {
|
|
13836
|
+
return result;
|
|
13837
|
+
}
|
|
13838
|
+
}
|
|
13839
|
+
},
|
|
13840
|
+
validateAgainstState: ({ state, payload }) => {
|
|
13841
|
+
for (const sceneId of payload.sceneIds) {
|
|
13842
|
+
if (!isPlainObject(state.scenes.items[sceneId])) {
|
|
13843
|
+
return invalidPrecondition(
|
|
13844
|
+
"payload.sceneIds must reference existing scenes",
|
|
13845
|
+
{ sceneId },
|
|
13846
|
+
);
|
|
13847
|
+
}
|
|
13848
|
+
}
|
|
13849
|
+
},
|
|
13850
|
+
reduce: ({ state, payload }) => {
|
|
13851
|
+
const deletedSceneIds = new Set();
|
|
13852
|
+
|
|
13853
|
+
for (const sceneId of payload.sceneIds) {
|
|
13854
|
+
const removedNode = removeTreeNode({
|
|
13855
|
+
nodes: state.scenes.tree,
|
|
13856
|
+
nodeId: sceneId,
|
|
13857
|
+
});
|
|
13858
|
+
|
|
13859
|
+
if (!removedNode) {
|
|
13860
|
+
continue;
|
|
13861
|
+
}
|
|
13862
|
+
|
|
13863
|
+
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
13864
|
+
deletedSceneIds.add(id);
|
|
13865
|
+
}
|
|
13866
|
+
}
|
|
13867
|
+
|
|
13868
|
+
for (const sceneId of deletedSceneIds) {
|
|
13869
|
+
delete state.scenes.items[sceneId];
|
|
13870
|
+
}
|
|
13871
|
+
|
|
13872
|
+
if (
|
|
13873
|
+
state.story.initialSceneId !== null &&
|
|
13874
|
+
!isPlainObject(state.scenes.items[state.story.initialSceneId])
|
|
13875
|
+
) {
|
|
13876
|
+
state.story.initialSceneId = findFirstNonFolderSceneId({ state });
|
|
13877
|
+
}
|
|
13878
|
+
|
|
13879
|
+
return state;
|
|
13880
|
+
},
|
|
13881
|
+
},
|
|
13882
|
+
{
|
|
13883
|
+
type: "scene.move",
|
|
13884
|
+
validatePayload: ({ payload }) => {
|
|
13885
|
+
{
|
|
13886
|
+
const result = validateAllowedKeys({
|
|
13887
|
+
value: payload,
|
|
13888
|
+
allowedKeys: [
|
|
13889
|
+
"sceneId",
|
|
13890
|
+
"parentId",
|
|
13891
|
+
"index",
|
|
13892
|
+
"position",
|
|
13893
|
+
"positionTargetId",
|
|
13894
|
+
],
|
|
13895
|
+
path: "payload",
|
|
13896
|
+
errorFactory: createPayloadValidationError,
|
|
13897
|
+
});
|
|
13898
|
+
if (result?.valid === false) {
|
|
13899
|
+
return result;
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
|
|
13903
|
+
if (!isNonEmptyString(payload.sceneId)) {
|
|
13904
|
+
return invalidPayload("payload.sceneId must be a non-empty string");
|
|
13905
|
+
}
|
|
13906
|
+
|
|
13907
|
+
if (
|
|
13908
|
+
payload.parentId !== undefined &&
|
|
13909
|
+
payload.parentId !== null &&
|
|
13910
|
+
!isNonEmptyString(payload.parentId)
|
|
13911
|
+
) {
|
|
13912
|
+
return invalidPayload(
|
|
13913
|
+
"payload.parentId must be a non-empty string when provided",
|
|
13914
|
+
);
|
|
13915
|
+
}
|
|
13916
|
+
|
|
13917
|
+
{
|
|
13918
|
+
const result = validatePlacementFields({
|
|
13919
|
+
payload,
|
|
13920
|
+
errorFactory: createPayloadValidationError,
|
|
13921
|
+
});
|
|
13922
|
+
if (result?.valid === false) {
|
|
13923
|
+
return result;
|
|
13924
|
+
}
|
|
13925
|
+
}
|
|
13926
|
+
},
|
|
13927
|
+
validateAgainstState: ({ state, payload }) => {
|
|
13928
|
+
const scene = state.scenes.items[payload.sceneId];
|
|
13929
|
+
if (!isPlainObject(scene)) {
|
|
13930
|
+
return invalidPrecondition(
|
|
13931
|
+
"payload.sceneId must reference an existing scene",
|
|
13932
|
+
);
|
|
13933
|
+
}
|
|
13934
|
+
|
|
13935
|
+
const sceneNode = findTreeNode({
|
|
13936
|
+
nodes: state.scenes.tree,
|
|
13937
|
+
nodeId: payload.sceneId,
|
|
13938
|
+
});
|
|
13939
|
+
|
|
13940
|
+
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
13941
|
+
const parentScene = state.scenes.items[payload.parentId];
|
|
13942
|
+
if (!isPlainObject(parentScene)) {
|
|
13943
|
+
return invalidPrecondition(
|
|
13944
|
+
"payload.parentId must reference an existing scene",
|
|
13945
|
+
);
|
|
13946
|
+
}
|
|
13947
|
+
|
|
13948
|
+
if (parentScene.type !== "folder") {
|
|
13949
|
+
return invalidPrecondition(
|
|
13950
|
+
"payload.parentId must reference a folder scene",
|
|
13951
|
+
);
|
|
13952
|
+
}
|
|
13953
|
+
|
|
13954
|
+
const descendantIds = new Set(
|
|
13955
|
+
collectTreeDescendantIds({
|
|
13956
|
+
node: sceneNode,
|
|
13957
|
+
}),
|
|
13958
|
+
);
|
|
13959
|
+
|
|
13960
|
+
if (descendantIds.has(payload.parentId)) {
|
|
13961
|
+
return invalidPrecondition(
|
|
13962
|
+
"payload.parentId must not target the moved scene or its descendants",
|
|
13963
|
+
);
|
|
13964
|
+
}
|
|
13965
|
+
}
|
|
13966
|
+
|
|
13967
|
+
if (payload.positionTargetId !== undefined) {
|
|
13968
|
+
if (payload.positionTargetId === payload.sceneId) {
|
|
13969
|
+
return invalidPrecondition(
|
|
13970
|
+
"payload.positionTargetId must not reference the moved scene",
|
|
13971
|
+
);
|
|
13972
|
+
}
|
|
13973
|
+
|
|
13974
|
+
if (!isPlainObject(state.scenes.items[payload.positionTargetId])) {
|
|
13975
|
+
return invalidPrecondition(
|
|
13976
|
+
"payload.positionTargetId must reference an existing scene",
|
|
13977
|
+
);
|
|
13978
|
+
}
|
|
13979
|
+
|
|
13980
|
+
const targetParentId = getNodeParentId({
|
|
13981
|
+
tree: state.scenes.tree,
|
|
13982
|
+
nodeId: payload.positionTargetId,
|
|
13983
|
+
});
|
|
12784
13984
|
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
13985
|
+
if (targetParentId !== (payload.parentId ?? null)) {
|
|
13986
|
+
return invalidPrecondition(
|
|
13987
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
13988
|
+
);
|
|
13989
|
+
}
|
|
12789
13990
|
}
|
|
12790
13991
|
},
|
|
12791
13992
|
reduce: ({ state, payload }) => {
|
|
12792
|
-
|
|
13993
|
+
const sceneNodeResult = removeNodeOrResult({
|
|
13994
|
+
tree: state.scenes.tree,
|
|
13995
|
+
nodeId: payload.sceneId,
|
|
13996
|
+
errorMessage: "scene move target missing from tree",
|
|
13997
|
+
});
|
|
13998
|
+
if (!sceneNodeResult.valid) {
|
|
13999
|
+
return sceneNodeResult;
|
|
14000
|
+
}
|
|
14001
|
+
|
|
14002
|
+
insertTreeNode({
|
|
14003
|
+
tree: state.scenes.tree,
|
|
14004
|
+
node: sceneNodeResult.node,
|
|
14005
|
+
parentId: payload.parentId ?? null,
|
|
14006
|
+
index: payload.index,
|
|
14007
|
+
position: payload.position,
|
|
14008
|
+
positionTargetId: payload.positionTargetId,
|
|
14009
|
+
});
|
|
14010
|
+
|
|
12793
14011
|
return state;
|
|
12794
14012
|
},
|
|
12795
14013
|
},
|
|
12796
14014
|
{
|
|
12797
|
-
type: "
|
|
14015
|
+
type: "section.create",
|
|
12798
14016
|
validatePayload: ({ payload }) => {
|
|
12799
14017
|
{
|
|
12800
14018
|
const result = validateAllowedKeys({
|
|
12801
14019
|
value: payload,
|
|
12802
14020
|
allowedKeys: [
|
|
14021
|
+
"sectionId",
|
|
12803
14022
|
"sceneId",
|
|
12804
14023
|
"parentId",
|
|
12805
14024
|
"data",
|
|
@@ -12815,6 +14034,10 @@ const COMMAND_DEFINITIONS = [
|
|
|
12815
14034
|
}
|
|
12816
14035
|
}
|
|
12817
14036
|
|
|
14037
|
+
if (!isNonEmptyString(payload.sectionId)) {
|
|
14038
|
+
return invalidPayload("payload.sectionId must be a non-empty string");
|
|
14039
|
+
}
|
|
14040
|
+
|
|
12818
14041
|
if (!isNonEmptyString(payload.sceneId)) {
|
|
12819
14042
|
return invalidPayload("payload.sceneId must be a non-empty string");
|
|
12820
14043
|
}
|
|
@@ -12830,7 +14053,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
12830
14053
|
}
|
|
12831
14054
|
|
|
12832
14055
|
{
|
|
12833
|
-
const result =
|
|
14056
|
+
const result = validateSectionCreateData({
|
|
12834
14057
|
data: payload.data,
|
|
12835
14058
|
errorFactory: createPayloadValidationError,
|
|
12836
14059
|
});
|
|
@@ -12850,41 +14073,65 @@ const COMMAND_DEFINITIONS = [
|
|
|
12850
14073
|
}
|
|
12851
14074
|
},
|
|
12852
14075
|
validateAgainstState: ({ state, payload }) => {
|
|
12853
|
-
|
|
12854
|
-
|
|
14076
|
+
const scene = state.scenes.items[payload.sceneId];
|
|
14077
|
+
if (!isPlainObject(scene)) {
|
|
14078
|
+
return invalidPrecondition(
|
|
14079
|
+
"payload.sceneId must reference an existing scene",
|
|
14080
|
+
);
|
|
12855
14081
|
}
|
|
12856
14082
|
|
|
12857
|
-
|
|
14083
|
+
if (scene.type === "folder") {
|
|
14084
|
+
return invalidPrecondition(
|
|
14085
|
+
"payload.sceneId must reference a non-folder scene",
|
|
14086
|
+
);
|
|
14087
|
+
}
|
|
12858
14088
|
|
|
12859
|
-
if (
|
|
12860
|
-
|
|
12861
|
-
|
|
14089
|
+
if (findSectionLocation({ state, sectionId: payload.sectionId })) {
|
|
14090
|
+
return invalidPrecondition("payload.sectionId must not already exist");
|
|
14091
|
+
}
|
|
14092
|
+
|
|
14093
|
+
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
14094
|
+
const parentLocation = findSectionLocation({
|
|
14095
|
+
state,
|
|
14096
|
+
sectionId: payload.parentId,
|
|
14097
|
+
});
|
|
14098
|
+
if (!parentLocation) {
|
|
12862
14099
|
return invalidPrecondition(
|
|
12863
|
-
"payload.parentId must reference an existing
|
|
14100
|
+
"payload.parentId must reference an existing section",
|
|
12864
14101
|
);
|
|
12865
14102
|
}
|
|
12866
14103
|
|
|
12867
|
-
if (
|
|
14104
|
+
if (parentLocation.sceneId !== payload.sceneId) {
|
|
12868
14105
|
return invalidPrecondition(
|
|
12869
|
-
"payload.parentId must reference a
|
|
14106
|
+
"payload.parentId must reference a section in the same scene",
|
|
12870
14107
|
);
|
|
12871
14108
|
}
|
|
12872
14109
|
}
|
|
12873
14110
|
|
|
12874
14111
|
if (payload.positionTargetId !== undefined) {
|
|
12875
|
-
|
|
14112
|
+
const targetLocation = findSectionLocation({
|
|
14113
|
+
state,
|
|
14114
|
+
sectionId: payload.positionTargetId,
|
|
14115
|
+
});
|
|
14116
|
+
if (!targetLocation) {
|
|
12876
14117
|
return invalidPrecondition(
|
|
12877
|
-
"payload.positionTargetId must reference an existing
|
|
14118
|
+
"payload.positionTargetId must reference an existing section",
|
|
12878
14119
|
);
|
|
12879
14120
|
}
|
|
12880
14121
|
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
14122
|
+
if (targetLocation.sceneId !== payload.sceneId) {
|
|
14123
|
+
return invalidPrecondition(
|
|
14124
|
+
"payload.positionTargetId must reference a section in the same scene",
|
|
14125
|
+
);
|
|
14126
|
+
}
|
|
12886
14127
|
|
|
12887
|
-
|
|
14128
|
+
const sections = scene.sections ?? createEmptyNestedCollection();
|
|
14129
|
+
const targetParentId = getNodeParentId({
|
|
14130
|
+
tree: sections.tree,
|
|
14131
|
+
nodeId: payload.positionTargetId,
|
|
14132
|
+
});
|
|
14133
|
+
|
|
14134
|
+
if (targetParentId !== (payload.parentId ?? null)) {
|
|
12888
14135
|
return invalidPrecondition(
|
|
12889
14136
|
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
12890
14137
|
);
|
|
@@ -12892,30 +14139,19 @@ const COMMAND_DEFINITIONS = [
|
|
|
12892
14139
|
}
|
|
12893
14140
|
},
|
|
12894
14141
|
reduce: ({ state, payload }) => {
|
|
12895
|
-
const
|
|
12896
|
-
|
|
12897
|
-
|
|
14142
|
+
const scene = state.scenes.items[payload.sceneId];
|
|
14143
|
+
scene.sections ??= createEmptyNestedCollection();
|
|
14144
|
+
const sections = scene.sections;
|
|
14145
|
+
sections.items[payload.sectionId] = {
|
|
14146
|
+
id: payload.sectionId,
|
|
12898
14147
|
name: payload.data.name,
|
|
14148
|
+
lines: createEmptyNestedCollection(),
|
|
12899
14149
|
};
|
|
12900
14150
|
|
|
12901
|
-
if (payload.data.description !== undefined) {
|
|
12902
|
-
nextScene.description = payload.data.description;
|
|
12903
|
-
}
|
|
12904
|
-
|
|
12905
|
-
if (nextScene.type === "scene") {
|
|
12906
|
-
nextScene.sections = createEmptyNestedCollection();
|
|
12907
|
-
}
|
|
12908
|
-
|
|
12909
|
-
if (payload.data.position !== undefined) {
|
|
12910
|
-
nextScene.position = structuredClone(payload.data.position);
|
|
12911
|
-
}
|
|
12912
|
-
|
|
12913
|
-
state.scenes.items[payload.sceneId] = nextScene;
|
|
12914
|
-
|
|
12915
14151
|
insertTreeNode({
|
|
12916
|
-
tree:
|
|
14152
|
+
tree: sections.tree,
|
|
12917
14153
|
node: {
|
|
12918
|
-
id: payload.
|
|
14154
|
+
id: payload.sectionId,
|
|
12919
14155
|
children: [],
|
|
12920
14156
|
},
|
|
12921
14157
|
parentId: payload.parentId ?? null,
|
|
@@ -12928,12 +14164,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
12928
14164
|
},
|
|
12929
14165
|
},
|
|
12930
14166
|
{
|
|
12931
|
-
type: "
|
|
14167
|
+
type: "section.update",
|
|
12932
14168
|
validatePayload: ({ payload }) => {
|
|
12933
14169
|
{
|
|
12934
14170
|
const result = validateExactKeys({
|
|
12935
14171
|
value: payload,
|
|
12936
|
-
expectedKeys: ["
|
|
14172
|
+
expectedKeys: ["sectionId", "data"],
|
|
12937
14173
|
path: "payload",
|
|
12938
14174
|
errorFactory: createPayloadValidationError,
|
|
12939
14175
|
});
|
|
@@ -12942,12 +14178,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
12942
14178
|
}
|
|
12943
14179
|
}
|
|
12944
14180
|
|
|
12945
|
-
if (!isNonEmptyString(payload.
|
|
12946
|
-
return invalidPayload("payload.
|
|
14181
|
+
if (!isNonEmptyString(payload.sectionId)) {
|
|
14182
|
+
return invalidPayload("payload.sectionId must be a non-empty string");
|
|
12947
14183
|
}
|
|
12948
14184
|
|
|
12949
14185
|
{
|
|
12950
|
-
const result =
|
|
14186
|
+
const result = validateSectionUpdateData({
|
|
12951
14187
|
data: payload.data,
|
|
12952
14188
|
errorFactory: createPayloadValidationError,
|
|
12953
14189
|
});
|
|
@@ -12957,42 +14193,32 @@ const COMMAND_DEFINITIONS = [
|
|
|
12957
14193
|
}
|
|
12958
14194
|
},
|
|
12959
14195
|
validateAgainstState: ({ state, payload }) => {
|
|
12960
|
-
if (!
|
|
14196
|
+
if (!findSectionLocation({ state, sectionId: payload.sectionId })) {
|
|
12961
14197
|
return invalidPrecondition(
|
|
12962
|
-
"payload.
|
|
14198
|
+
"payload.sectionId must reference an existing section",
|
|
12963
14199
|
);
|
|
12964
14200
|
}
|
|
12965
14201
|
},
|
|
12966
14202
|
reduce: ({ state, payload }) => {
|
|
12967
|
-
const
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
}
|
|
12977
|
-
|
|
12978
|
-
if (payload.data.position !== undefined) {
|
|
12979
|
-
nextScene.position = {
|
|
12980
|
-
...(isPlainObject(nextScene.position) ? nextScene.position : {}),
|
|
12981
|
-
...structuredClone(payload.data.position),
|
|
12982
|
-
};
|
|
12983
|
-
}
|
|
12984
|
-
|
|
12985
|
-
state.scenes.items[payload.sceneId] = nextScene;
|
|
14203
|
+
const location = findSectionLocation({
|
|
14204
|
+
state,
|
|
14205
|
+
sectionId: payload.sectionId,
|
|
14206
|
+
});
|
|
14207
|
+
const section = location.section;
|
|
14208
|
+
location.sections.items[payload.sectionId] = {
|
|
14209
|
+
...section,
|
|
14210
|
+
name: payload.data.name,
|
|
14211
|
+
};
|
|
12986
14212
|
return state;
|
|
12987
14213
|
},
|
|
12988
14214
|
},
|
|
12989
14215
|
{
|
|
12990
|
-
type: "
|
|
14216
|
+
type: "section.delete",
|
|
12991
14217
|
validatePayload: ({ payload }) => {
|
|
12992
14218
|
{
|
|
12993
14219
|
const result = validateExactKeys({
|
|
12994
14220
|
value: payload,
|
|
12995
|
-
expectedKeys: ["
|
|
14221
|
+
expectedKeys: ["sectionIds"],
|
|
12996
14222
|
path: "payload",
|
|
12997
14223
|
errorFactory: createPayloadValidationError,
|
|
12998
14224
|
});
|
|
@@ -13003,8 +14229,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
13003
14229
|
|
|
13004
14230
|
{
|
|
13005
14231
|
const result = validateRequiredUniqueIdArray({
|
|
13006
|
-
value: payload.
|
|
13007
|
-
path: "payload.
|
|
14232
|
+
value: payload.sectionIds,
|
|
14233
|
+
path: "payload.sectionIds",
|
|
13008
14234
|
errorFactory: createPayloadValidationError,
|
|
13009
14235
|
});
|
|
13010
14236
|
if (result?.valid === false) {
|
|
@@ -13013,54 +14239,66 @@ const COMMAND_DEFINITIONS = [
|
|
|
13013
14239
|
}
|
|
13014
14240
|
},
|
|
13015
14241
|
validateAgainstState: ({ state, payload }) => {
|
|
13016
|
-
for (const
|
|
13017
|
-
if (!
|
|
14242
|
+
for (const sectionId of payload.sectionIds) {
|
|
14243
|
+
if (!findSectionLocation({ state, sectionId })) {
|
|
13018
14244
|
return invalidPrecondition(
|
|
13019
|
-
"payload.
|
|
13020
|
-
{
|
|
14245
|
+
"payload.sectionIds must reference existing sections",
|
|
14246
|
+
{ sectionId },
|
|
13021
14247
|
);
|
|
13022
14248
|
}
|
|
13023
14249
|
}
|
|
13024
14250
|
},
|
|
13025
14251
|
reduce: ({ state, payload }) => {
|
|
13026
|
-
const
|
|
14252
|
+
const deletedSectionIdsByScene = new Map();
|
|
14253
|
+
|
|
14254
|
+
for (const sectionId of payload.sectionIds) {
|
|
14255
|
+
const location = findSectionLocation({ state, sectionId });
|
|
14256
|
+
if (!location) {
|
|
14257
|
+
continue;
|
|
14258
|
+
}
|
|
13027
14259
|
|
|
13028
|
-
for (const sceneId of payload.sceneIds) {
|
|
13029
14260
|
const removedNode = removeTreeNode({
|
|
13030
|
-
nodes:
|
|
13031
|
-
nodeId:
|
|
14261
|
+
nodes: location.sections.tree,
|
|
14262
|
+
nodeId: sectionId,
|
|
13032
14263
|
});
|
|
13033
14264
|
|
|
13034
14265
|
if (!removedNode) {
|
|
13035
14266
|
continue;
|
|
13036
14267
|
}
|
|
13037
14268
|
|
|
14269
|
+
let deletedSectionIds = deletedSectionIdsByScene.get(location.sceneId);
|
|
14270
|
+
if (!deletedSectionIds) {
|
|
14271
|
+
deletedSectionIds = new Set();
|
|
14272
|
+
deletedSectionIdsByScene.set(location.sceneId, deletedSectionIds);
|
|
14273
|
+
}
|
|
14274
|
+
|
|
13038
14275
|
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
13039
|
-
|
|
14276
|
+
deletedSectionIds.add(id);
|
|
13040
14277
|
}
|
|
13041
14278
|
}
|
|
13042
14279
|
|
|
13043
|
-
for (const sceneId of
|
|
13044
|
-
|
|
13045
|
-
|
|
14280
|
+
for (const [sceneId, deletedSectionIds] of deletedSectionIdsByScene) {
|
|
14281
|
+
const sections = state.scenes.items[sceneId]?.sections;
|
|
14282
|
+
if (!sections) {
|
|
14283
|
+
continue;
|
|
14284
|
+
}
|
|
13046
14285
|
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
) {
|
|
13051
|
-
state.story.initialSceneId = findFirstNonFolderSceneId({ state });
|
|
14286
|
+
for (const sectionId of deletedSectionIds) {
|
|
14287
|
+
delete sections.items[sectionId];
|
|
14288
|
+
}
|
|
13052
14289
|
}
|
|
13053
14290
|
|
|
13054
14291
|
return state;
|
|
13055
14292
|
},
|
|
13056
14293
|
},
|
|
13057
14294
|
{
|
|
13058
|
-
type: "
|
|
14295
|
+
type: "section.move",
|
|
13059
14296
|
validatePayload: ({ payload }) => {
|
|
13060
14297
|
{
|
|
13061
14298
|
const result = validateAllowedKeys({
|
|
13062
14299
|
value: payload,
|
|
13063
14300
|
allowedKeys: [
|
|
14301
|
+
"sectionId",
|
|
13064
14302
|
"sceneId",
|
|
13065
14303
|
"parentId",
|
|
13066
14304
|
"index",
|
|
@@ -13075,8 +14313,14 @@ const COMMAND_DEFINITIONS = [
|
|
|
13075
14313
|
}
|
|
13076
14314
|
}
|
|
13077
14315
|
|
|
13078
|
-
if (!isNonEmptyString(payload.
|
|
13079
|
-
return invalidPayload("payload.
|
|
14316
|
+
if (!isNonEmptyString(payload.sectionId)) {
|
|
14317
|
+
return invalidPayload("payload.sectionId must be a non-empty string");
|
|
14318
|
+
}
|
|
14319
|
+
|
|
14320
|
+
if (payload.sceneId !== undefined && !isNonEmptyString(payload.sceneId)) {
|
|
14321
|
+
return invalidPayload(
|
|
14322
|
+
"payload.sceneId must be a non-empty string when provided",
|
|
14323
|
+
);
|
|
13080
14324
|
}
|
|
13081
14325
|
|
|
13082
14326
|
if (
|
|
@@ -13100,60 +14344,108 @@ const COMMAND_DEFINITIONS = [
|
|
|
13100
14344
|
}
|
|
13101
14345
|
},
|
|
13102
14346
|
validateAgainstState: ({ state, payload }) => {
|
|
13103
|
-
const
|
|
13104
|
-
|
|
14347
|
+
const location = findSectionLocation({
|
|
14348
|
+
state,
|
|
14349
|
+
sectionId: payload.sectionId,
|
|
14350
|
+
});
|
|
14351
|
+
if (!location) {
|
|
14352
|
+
return invalidPrecondition(
|
|
14353
|
+
"payload.sectionId must reference an existing section",
|
|
14354
|
+
);
|
|
14355
|
+
}
|
|
14356
|
+
|
|
14357
|
+
const targetSceneId = payload.sceneId ?? location.sceneId;
|
|
14358
|
+
const targetScene = state.scenes.items[targetSceneId];
|
|
14359
|
+
if (!isPlainObject(targetScene)) {
|
|
13105
14360
|
return invalidPrecondition(
|
|
13106
14361
|
"payload.sceneId must reference an existing scene",
|
|
13107
14362
|
);
|
|
13108
14363
|
}
|
|
13109
14364
|
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
14365
|
+
if (targetScene.type !== "scene") {
|
|
14366
|
+
return invalidPrecondition(
|
|
14367
|
+
"payload.sceneId must reference a non-folder scene",
|
|
14368
|
+
);
|
|
14369
|
+
}
|
|
14370
|
+
|
|
14371
|
+
const targetSections =
|
|
14372
|
+
targetScene.sections ?? createEmptyNestedCollection();
|
|
14373
|
+
const sectionNode = findTreeNode({
|
|
14374
|
+
nodes: location.sections.tree,
|
|
14375
|
+
nodeId: payload.sectionId,
|
|
13113
14376
|
});
|
|
14377
|
+
const isCrossSceneMove = targetSceneId !== location.sceneId;
|
|
14378
|
+
|
|
14379
|
+
if (isCrossSceneMove) {
|
|
14380
|
+
const sourceSectionCount = Object.keys(
|
|
14381
|
+
location.sections?.items ?? {},
|
|
14382
|
+
).length;
|
|
14383
|
+
const movedSectionCount = collectTreeDescendantIds({
|
|
14384
|
+
node: sectionNode,
|
|
14385
|
+
}).length;
|
|
14386
|
+
|
|
14387
|
+
if (sourceSectionCount <= movedSectionCount) {
|
|
14388
|
+
return invalidPrecondition(
|
|
14389
|
+
"payload.sectionId must not move the last section out of a scene",
|
|
14390
|
+
);
|
|
14391
|
+
}
|
|
14392
|
+
}
|
|
13114
14393
|
|
|
13115
14394
|
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
13116
|
-
const
|
|
13117
|
-
|
|
14395
|
+
const parentLocation = findSectionLocation({
|
|
14396
|
+
state,
|
|
14397
|
+
sectionId: payload.parentId,
|
|
14398
|
+
});
|
|
14399
|
+
if (!parentLocation) {
|
|
13118
14400
|
return invalidPrecondition(
|
|
13119
|
-
"payload.parentId must reference an existing
|
|
14401
|
+
"payload.parentId must reference an existing section",
|
|
13120
14402
|
);
|
|
13121
14403
|
}
|
|
13122
14404
|
|
|
13123
|
-
if (
|
|
14405
|
+
if (parentLocation.sceneId !== targetSceneId) {
|
|
13124
14406
|
return invalidPrecondition(
|
|
13125
|
-
"payload.parentId must reference a
|
|
14407
|
+
"payload.parentId must reference a section in the target scene",
|
|
13126
14408
|
);
|
|
13127
14409
|
}
|
|
13128
14410
|
|
|
13129
14411
|
const descendantIds = new Set(
|
|
13130
14412
|
collectTreeDescendantIds({
|
|
13131
|
-
node:
|
|
14413
|
+
node: sectionNode,
|
|
13132
14414
|
}),
|
|
13133
14415
|
);
|
|
13134
14416
|
|
|
13135
14417
|
if (descendantIds.has(payload.parentId)) {
|
|
13136
14418
|
return invalidPrecondition(
|
|
13137
|
-
"payload.parentId must not target the moved
|
|
14419
|
+
"payload.parentId must not target the moved section or its descendants",
|
|
13138
14420
|
);
|
|
13139
14421
|
}
|
|
13140
14422
|
}
|
|
13141
14423
|
|
|
13142
14424
|
if (payload.positionTargetId !== undefined) {
|
|
13143
|
-
if (payload.positionTargetId === payload.
|
|
14425
|
+
if (payload.positionTargetId === payload.sectionId) {
|
|
13144
14426
|
return invalidPrecondition(
|
|
13145
|
-
"payload.positionTargetId must not reference the moved
|
|
14427
|
+
"payload.positionTargetId must not reference the moved section",
|
|
13146
14428
|
);
|
|
13147
14429
|
}
|
|
13148
14430
|
|
|
13149
|
-
|
|
14431
|
+
const targetLocation = findSectionLocation({
|
|
14432
|
+
state,
|
|
14433
|
+
sectionId: payload.positionTargetId,
|
|
14434
|
+
});
|
|
14435
|
+
if (!targetLocation) {
|
|
13150
14436
|
return invalidPrecondition(
|
|
13151
|
-
"payload.positionTargetId must reference an existing
|
|
14437
|
+
"payload.positionTargetId must reference an existing section",
|
|
14438
|
+
);
|
|
14439
|
+
}
|
|
14440
|
+
|
|
14441
|
+
if (targetLocation.sceneId !== targetSceneId) {
|
|
14442
|
+
return invalidPrecondition(
|
|
14443
|
+
"payload.positionTargetId must reference a section in the target scene",
|
|
13152
14444
|
);
|
|
13153
14445
|
}
|
|
13154
14446
|
|
|
13155
14447
|
const targetParentId = getNodeParentId({
|
|
13156
|
-
tree:
|
|
14448
|
+
tree: targetSections.tree,
|
|
13157
14449
|
nodeId: payload.positionTargetId,
|
|
13158
14450
|
});
|
|
13159
14451
|
|
|
@@ -13165,18 +14457,38 @@ const COMMAND_DEFINITIONS = [
|
|
|
13165
14457
|
}
|
|
13166
14458
|
},
|
|
13167
14459
|
reduce: ({ state, payload }) => {
|
|
13168
|
-
const
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
errorMessage: "scene move target missing from tree",
|
|
14460
|
+
const location = findSectionLocation({
|
|
14461
|
+
state,
|
|
14462
|
+
sectionId: payload.sectionId,
|
|
13172
14463
|
});
|
|
13173
|
-
|
|
13174
|
-
|
|
14464
|
+
const targetSceneId = payload.sceneId ?? location.sceneId;
|
|
14465
|
+
const targetScene = state.scenes.items[targetSceneId];
|
|
14466
|
+
targetScene.sections ??= createEmptyNestedCollection();
|
|
14467
|
+
const targetSections = targetScene.sections;
|
|
14468
|
+
const sectionNodeResult = removeNodeOrResult({
|
|
14469
|
+
tree: location.sections.tree,
|
|
14470
|
+
nodeId: payload.sectionId,
|
|
14471
|
+
errorMessage: "section move target missing from tree",
|
|
14472
|
+
});
|
|
14473
|
+
if (!sectionNodeResult.valid) {
|
|
14474
|
+
return sectionNodeResult;
|
|
14475
|
+
}
|
|
14476
|
+
|
|
14477
|
+
if (targetSections !== location.sections) {
|
|
14478
|
+
const movedSectionIds = collectTreeDescendantIds({
|
|
14479
|
+
node: sectionNodeResult.node,
|
|
14480
|
+
});
|
|
14481
|
+
|
|
14482
|
+
for (const movedSectionId of movedSectionIds) {
|
|
14483
|
+
targetSections.items[movedSectionId] =
|
|
14484
|
+
location.sections.items[movedSectionId];
|
|
14485
|
+
delete location.sections.items[movedSectionId];
|
|
14486
|
+
}
|
|
13175
14487
|
}
|
|
13176
14488
|
|
|
13177
14489
|
insertTreeNode({
|
|
13178
|
-
tree:
|
|
13179
|
-
node:
|
|
14490
|
+
tree: targetSections.tree,
|
|
14491
|
+
node: sectionNodeResult.node,
|
|
13180
14492
|
parentId: payload.parentId ?? null,
|
|
13181
14493
|
index: payload.index,
|
|
13182
14494
|
position: payload.position,
|
|
@@ -13187,16 +14499,14 @@ const COMMAND_DEFINITIONS = [
|
|
|
13187
14499
|
},
|
|
13188
14500
|
},
|
|
13189
14501
|
{
|
|
13190
|
-
type: "
|
|
14502
|
+
type: "line.create",
|
|
13191
14503
|
validatePayload: ({ payload }) => {
|
|
13192
14504
|
{
|
|
13193
14505
|
const result = validateAllowedKeys({
|
|
13194
14506
|
value: payload,
|
|
13195
14507
|
allowedKeys: [
|
|
13196
14508
|
"sectionId",
|
|
13197
|
-
"
|
|
13198
|
-
"parentId",
|
|
13199
|
-
"data",
|
|
14509
|
+
"lines",
|
|
13200
14510
|
"index",
|
|
13201
14511
|
"position",
|
|
13202
14512
|
"positionTargetId",
|
|
@@ -13213,23 +14523,9 @@ const COMMAND_DEFINITIONS = [
|
|
|
13213
14523
|
return invalidPayload("payload.sectionId must be a non-empty string");
|
|
13214
14524
|
}
|
|
13215
14525
|
|
|
13216
|
-
if (!isNonEmptyString(payload.sceneId)) {
|
|
13217
|
-
return invalidPayload("payload.sceneId must be a non-empty string");
|
|
13218
|
-
}
|
|
13219
|
-
|
|
13220
|
-
if (
|
|
13221
|
-
payload.parentId !== undefined &&
|
|
13222
|
-
payload.parentId !== null &&
|
|
13223
|
-
!isNonEmptyString(payload.parentId)
|
|
13224
|
-
) {
|
|
13225
|
-
return invalidPayload(
|
|
13226
|
-
"payload.parentId must be a non-empty string when provided",
|
|
13227
|
-
);
|
|
13228
|
-
}
|
|
13229
|
-
|
|
13230
14526
|
{
|
|
13231
|
-
const result =
|
|
13232
|
-
|
|
14527
|
+
const result = validateLineCreatePayload({
|
|
14528
|
+
payload,
|
|
13233
14529
|
errorFactory: createPayloadValidationError,
|
|
13234
14530
|
});
|
|
13235
14531
|
if (result?.valid === false) {
|
|
@@ -13248,103 +14544,82 @@ const COMMAND_DEFINITIONS = [
|
|
|
13248
14544
|
}
|
|
13249
14545
|
},
|
|
13250
14546
|
validateAgainstState: ({ state, payload }) => {
|
|
13251
|
-
const
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
}
|
|
13257
|
-
|
|
13258
|
-
if (scene.type === "folder") {
|
|
14547
|
+
const sectionLocation = findSectionLocation({
|
|
14548
|
+
state,
|
|
14549
|
+
sectionId: payload.sectionId,
|
|
14550
|
+
});
|
|
14551
|
+
if (!sectionLocation) {
|
|
13259
14552
|
return invalidPrecondition(
|
|
13260
|
-
"payload.
|
|
14553
|
+
"payload.sectionId must reference an existing section",
|
|
13261
14554
|
);
|
|
13262
14555
|
}
|
|
13263
14556
|
|
|
13264
|
-
|
|
13265
|
-
|
|
13266
|
-
}
|
|
13267
|
-
|
|
13268
|
-
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
13269
|
-
const parentLocation = findSectionLocation({
|
|
13270
|
-
state,
|
|
13271
|
-
sectionId: payload.parentId,
|
|
13272
|
-
});
|
|
13273
|
-
if (!parentLocation) {
|
|
13274
|
-
return invalidPrecondition(
|
|
13275
|
-
"payload.parentId must reference an existing section",
|
|
13276
|
-
);
|
|
13277
|
-
}
|
|
13278
|
-
|
|
13279
|
-
if (parentLocation.sceneId !== payload.sceneId) {
|
|
14557
|
+
for (const item of payload.lines) {
|
|
14558
|
+
if (findLineLocation({ state, lineId: item.lineId })) {
|
|
13280
14559
|
return invalidPrecondition(
|
|
13281
|
-
"payload.
|
|
14560
|
+
"payload.lines.lineId must not already exist",
|
|
14561
|
+
{ lineId: item.lineId },
|
|
13282
14562
|
);
|
|
13283
14563
|
}
|
|
13284
14564
|
}
|
|
13285
14565
|
|
|
13286
14566
|
if (payload.positionTargetId !== undefined) {
|
|
13287
|
-
const targetLocation =
|
|
14567
|
+
const targetLocation = findLineLocation({
|
|
13288
14568
|
state,
|
|
13289
|
-
|
|
14569
|
+
lineId: payload.positionTargetId,
|
|
13290
14570
|
});
|
|
13291
14571
|
if (!targetLocation) {
|
|
13292
14572
|
return invalidPrecondition(
|
|
13293
|
-
"payload.positionTargetId must reference an existing
|
|
13294
|
-
);
|
|
13295
|
-
}
|
|
13296
|
-
|
|
13297
|
-
if (targetLocation.sceneId !== payload.sceneId) {
|
|
13298
|
-
return invalidPrecondition(
|
|
13299
|
-
"payload.positionTargetId must reference a section in the same scene",
|
|
14573
|
+
"payload.positionTargetId must reference an existing line",
|
|
13300
14574
|
);
|
|
13301
14575
|
}
|
|
13302
14576
|
|
|
13303
|
-
|
|
13304
|
-
const targetParentId = getNodeParentId({
|
|
13305
|
-
tree: sections.tree,
|
|
13306
|
-
nodeId: payload.positionTargetId,
|
|
13307
|
-
});
|
|
13308
|
-
|
|
13309
|
-
if (targetParentId !== (payload.parentId ?? null)) {
|
|
14577
|
+
if (targetLocation.sectionId !== payload.sectionId) {
|
|
13310
14578
|
return invalidPrecondition(
|
|
13311
|
-
"payload.positionTargetId must reference a
|
|
14579
|
+
"payload.positionTargetId must reference a line in the target section",
|
|
13312
14580
|
);
|
|
13313
14581
|
}
|
|
13314
14582
|
}
|
|
13315
14583
|
},
|
|
13316
14584
|
reduce: ({ state, payload }) => {
|
|
13317
|
-
const
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
};
|
|
14585
|
+
const sectionLocation = findSectionLocation({
|
|
14586
|
+
state,
|
|
14587
|
+
sectionId: payload.sectionId,
|
|
14588
|
+
});
|
|
14589
|
+
sectionLocation.section.lines ??= createEmptyNestedCollection();
|
|
14590
|
+
const lines = sectionLocation.section.lines;
|
|
14591
|
+
let previousLineId = payload.positionTargetId;
|
|
13325
14592
|
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
14593
|
+
payload.lines.forEach((item, index) => {
|
|
14594
|
+
lines.items[item.lineId] = {
|
|
14595
|
+
id: item.lineId,
|
|
14596
|
+
actions: structuredClone(item.data.actions || {}),
|
|
14597
|
+
};
|
|
14598
|
+
|
|
14599
|
+
insertTreeNode({
|
|
14600
|
+
tree: lines.tree,
|
|
14601
|
+
node: { id: item.lineId },
|
|
14602
|
+
index: Number.isInteger(payload.index)
|
|
14603
|
+
? payload.index + index
|
|
14604
|
+
: undefined,
|
|
14605
|
+
position: index === 0 ? payload.position : "after",
|
|
14606
|
+
positionTargetId:
|
|
14607
|
+
index === 0 ? payload.positionTargetId : previousLineId,
|
|
14608
|
+
});
|
|
14609
|
+
|
|
14610
|
+
previousLineId = item.lineId;
|
|
13336
14611
|
});
|
|
13337
14612
|
|
|
13338
14613
|
return state;
|
|
13339
14614
|
},
|
|
13340
14615
|
},
|
|
13341
14616
|
{
|
|
13342
|
-
type: "
|
|
14617
|
+
type: "line.update_actions",
|
|
13343
14618
|
validatePayload: ({ payload }) => {
|
|
13344
14619
|
{
|
|
13345
|
-
const result =
|
|
14620
|
+
const result = validateAllowedKeys({
|
|
13346
14621
|
value: payload,
|
|
13347
|
-
|
|
14622
|
+
allowedKeys: ["lineId", "data", "replace", "preserve"],
|
|
13348
14623
|
path: "payload",
|
|
13349
14624
|
errorFactory: createPayloadValidationError,
|
|
13350
14625
|
});
|
|
@@ -13353,13 +14628,34 @@ const COMMAND_DEFINITIONS = [
|
|
|
13353
14628
|
}
|
|
13354
14629
|
}
|
|
13355
14630
|
|
|
13356
|
-
if (!isNonEmptyString(payload.
|
|
13357
|
-
return invalidPayload("payload.
|
|
14631
|
+
if (!isNonEmptyString(payload.lineId)) {
|
|
14632
|
+
return invalidPayload("payload.lineId must be a non-empty string");
|
|
14633
|
+
}
|
|
14634
|
+
|
|
14635
|
+
{
|
|
14636
|
+
const result = validateLineUpdateActionsData({
|
|
14637
|
+
data: payload.data,
|
|
14638
|
+
errorFactory: createPayloadValidationError,
|
|
14639
|
+
});
|
|
14640
|
+
if (result?.valid === false) {
|
|
14641
|
+
return result;
|
|
14642
|
+
}
|
|
14643
|
+
}
|
|
14644
|
+
|
|
14645
|
+
if (
|
|
14646
|
+
payload.replace !== undefined &&
|
|
14647
|
+
typeof payload.replace !== "boolean"
|
|
14648
|
+
) {
|
|
14649
|
+
return invalidPayload(
|
|
14650
|
+
"payload.replace must be a boolean when provided",
|
|
14651
|
+
);
|
|
13358
14652
|
}
|
|
13359
14653
|
|
|
13360
14654
|
{
|
|
13361
|
-
const result =
|
|
14655
|
+
const result = validateLineUpdateActionsPreserve({
|
|
14656
|
+
preserve: payload.preserve,
|
|
13362
14657
|
data: payload.data,
|
|
14658
|
+
replace: payload.replace,
|
|
13363
14659
|
errorFactory: createPayloadValidationError,
|
|
13364
14660
|
});
|
|
13365
14661
|
if (result?.valid === false) {
|
|
@@ -13368,32 +14664,37 @@ const COMMAND_DEFINITIONS = [
|
|
|
13368
14664
|
}
|
|
13369
14665
|
},
|
|
13370
14666
|
validateAgainstState: ({ state, payload }) => {
|
|
13371
|
-
if (!
|
|
14667
|
+
if (!findLineLocation({ state, lineId: payload.lineId })) {
|
|
13372
14668
|
return invalidPrecondition(
|
|
13373
|
-
"payload.
|
|
14669
|
+
"payload.lineId must reference an existing line",
|
|
13374
14670
|
);
|
|
13375
14671
|
}
|
|
13376
14672
|
},
|
|
13377
14673
|
reduce: ({ state, payload }) => {
|
|
13378
|
-
const location =
|
|
13379
|
-
|
|
13380
|
-
|
|
14674
|
+
const location = findLineLocation({ state, lineId: payload.lineId });
|
|
14675
|
+
const line = location.line;
|
|
14676
|
+
const nextData = applyLineUpdateActionsPreserve({
|
|
14677
|
+
currentActions: line.actions,
|
|
14678
|
+
data: payload.data,
|
|
14679
|
+
preserve: payload.preserve,
|
|
13381
14680
|
});
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
14681
|
+
line.actions =
|
|
14682
|
+
payload.replace === true
|
|
14683
|
+
? structuredClone(nextData)
|
|
14684
|
+
: {
|
|
14685
|
+
...structuredClone(line.actions),
|
|
14686
|
+
...structuredClone(nextData),
|
|
14687
|
+
};
|
|
13387
14688
|
return state;
|
|
13388
14689
|
},
|
|
13389
14690
|
},
|
|
13390
14691
|
{
|
|
13391
|
-
type: "
|
|
14692
|
+
type: "line.delete",
|
|
13392
14693
|
validatePayload: ({ payload }) => {
|
|
13393
14694
|
{
|
|
13394
14695
|
const result = validateExactKeys({
|
|
13395
14696
|
value: payload,
|
|
13396
|
-
expectedKeys: ["
|
|
14697
|
+
expectedKeys: ["lineIds"],
|
|
13397
14698
|
path: "payload",
|
|
13398
14699
|
errorFactory: createPayloadValidationError,
|
|
13399
14700
|
});
|
|
@@ -13404,8 +14705,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
13404
14705
|
|
|
13405
14706
|
{
|
|
13406
14707
|
const result = validateRequiredUniqueIdArray({
|
|
13407
|
-
value: payload.
|
|
13408
|
-
path: "payload.
|
|
14708
|
+
value: payload.lineIds,
|
|
14709
|
+
path: "payload.lineIds",
|
|
13409
14710
|
errorFactory: createPayloadValidationError,
|
|
13410
14711
|
});
|
|
13411
14712
|
if (result?.valid === false) {
|
|
@@ -13414,68 +14715,38 @@ const COMMAND_DEFINITIONS = [
|
|
|
13414
14715
|
}
|
|
13415
14716
|
},
|
|
13416
14717
|
validateAgainstState: ({ state, payload }) => {
|
|
13417
|
-
for (const
|
|
13418
|
-
if (!
|
|
14718
|
+
for (const lineId of payload.lineIds) {
|
|
14719
|
+
if (!findLineLocation({ state, lineId })) {
|
|
13419
14720
|
return invalidPrecondition(
|
|
13420
|
-
"payload.
|
|
13421
|
-
{
|
|
14721
|
+
"payload.lineIds must reference existing lines",
|
|
14722
|
+
{ lineId },
|
|
13422
14723
|
);
|
|
13423
14724
|
}
|
|
13424
14725
|
}
|
|
13425
14726
|
},
|
|
13426
14727
|
reduce: ({ state, payload }) => {
|
|
13427
|
-
const
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
const removedNode = removeTreeNode({
|
|
13436
|
-
nodes: location.sections.tree,
|
|
13437
|
-
nodeId: sectionId,
|
|
14728
|
+
for (const lineId of payload.lineIds) {
|
|
14729
|
+
const location = findLineLocation({ state, lineId });
|
|
14730
|
+
location.section.lines ??= createEmptyNestedCollection();
|
|
14731
|
+
delete location.section.lines.items[lineId];
|
|
14732
|
+
removeTreeNode({
|
|
14733
|
+
nodes: location.section.lines.tree,
|
|
14734
|
+
nodeId: lineId,
|
|
13438
14735
|
});
|
|
13439
|
-
|
|
13440
|
-
if (!removedNode) {
|
|
13441
|
-
continue;
|
|
13442
|
-
}
|
|
13443
|
-
|
|
13444
|
-
let deletedSectionIds = deletedSectionIdsByScene.get(location.sceneId);
|
|
13445
|
-
if (!deletedSectionIds) {
|
|
13446
|
-
deletedSectionIds = new Set();
|
|
13447
|
-
deletedSectionIdsByScene.set(location.sceneId, deletedSectionIds);
|
|
13448
|
-
}
|
|
13449
|
-
|
|
13450
|
-
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
13451
|
-
deletedSectionIds.add(id);
|
|
13452
|
-
}
|
|
13453
|
-
}
|
|
13454
|
-
|
|
13455
|
-
for (const [sceneId, deletedSectionIds] of deletedSectionIdsByScene) {
|
|
13456
|
-
const sections = state.scenes.items[sceneId]?.sections;
|
|
13457
|
-
if (!sections) {
|
|
13458
|
-
continue;
|
|
13459
|
-
}
|
|
13460
|
-
|
|
13461
|
-
for (const sectionId of deletedSectionIds) {
|
|
13462
|
-
delete sections.items[sectionId];
|
|
13463
|
-
}
|
|
13464
14736
|
}
|
|
13465
14737
|
|
|
13466
14738
|
return state;
|
|
13467
14739
|
},
|
|
13468
14740
|
},
|
|
13469
14741
|
{
|
|
13470
|
-
type: "
|
|
14742
|
+
type: "line.move",
|
|
13471
14743
|
validatePayload: ({ payload }) => {
|
|
13472
14744
|
{
|
|
13473
14745
|
const result = validateAllowedKeys({
|
|
13474
14746
|
value: payload,
|
|
13475
14747
|
allowedKeys: [
|
|
13476
|
-
"
|
|
13477
|
-
"
|
|
13478
|
-
"parentId",
|
|
14748
|
+
"lineId",
|
|
14749
|
+
"toSectionId",
|
|
13479
14750
|
"index",
|
|
13480
14751
|
"position",
|
|
13481
14752
|
"positionTargetId",
|
|
@@ -13488,24 +14759,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
13488
14759
|
}
|
|
13489
14760
|
}
|
|
13490
14761
|
|
|
13491
|
-
if (!isNonEmptyString(payload.
|
|
13492
|
-
return invalidPayload("payload.
|
|
13493
|
-
}
|
|
13494
|
-
|
|
13495
|
-
if (payload.sceneId !== undefined && !isNonEmptyString(payload.sceneId)) {
|
|
13496
|
-
return invalidPayload(
|
|
13497
|
-
"payload.sceneId must be a non-empty string when provided",
|
|
13498
|
-
);
|
|
14762
|
+
if (!isNonEmptyString(payload.lineId)) {
|
|
14763
|
+
return invalidPayload("payload.lineId must be a non-empty string");
|
|
13499
14764
|
}
|
|
13500
14765
|
|
|
13501
|
-
if (
|
|
13502
|
-
payload.
|
|
13503
|
-
payload.parentId !== null &&
|
|
13504
|
-
!isNonEmptyString(payload.parentId)
|
|
13505
|
-
) {
|
|
13506
|
-
return invalidPayload(
|
|
13507
|
-
"payload.parentId must be a non-empty string when provided",
|
|
13508
|
-
);
|
|
14766
|
+
if (!isNonEmptyString(payload.toSectionId)) {
|
|
14767
|
+
return invalidPayload("payload.toSectionId must be a non-empty string");
|
|
13509
14768
|
}
|
|
13510
14769
|
|
|
13511
14770
|
{
|
|
@@ -13519,152 +14778,78 @@ const COMMAND_DEFINITIONS = [
|
|
|
13519
14778
|
}
|
|
13520
14779
|
},
|
|
13521
14780
|
validateAgainstState: ({ state, payload }) => {
|
|
13522
|
-
const
|
|
14781
|
+
const lineLocation = findLineLocation({
|
|
13523
14782
|
state,
|
|
13524
|
-
|
|
14783
|
+
lineId: payload.lineId,
|
|
13525
14784
|
});
|
|
13526
|
-
if (!
|
|
13527
|
-
return invalidPrecondition(
|
|
13528
|
-
"payload.sectionId must reference an existing section",
|
|
13529
|
-
);
|
|
13530
|
-
}
|
|
13531
|
-
|
|
13532
|
-
const targetSceneId = payload.sceneId ?? location.sceneId;
|
|
13533
|
-
const targetScene = state.scenes.items[targetSceneId];
|
|
13534
|
-
if (!isPlainObject(targetScene)) {
|
|
13535
|
-
return invalidPrecondition(
|
|
13536
|
-
"payload.sceneId must reference an existing scene",
|
|
13537
|
-
);
|
|
13538
|
-
}
|
|
13539
|
-
|
|
13540
|
-
if (targetScene.type !== "scene") {
|
|
14785
|
+
if (!lineLocation) {
|
|
13541
14786
|
return invalidPrecondition(
|
|
13542
|
-
"payload.
|
|
14787
|
+
"payload.lineId must reference an existing line",
|
|
13543
14788
|
);
|
|
13544
14789
|
}
|
|
13545
14790
|
|
|
13546
|
-
const
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
nodes: location.sections.tree,
|
|
13550
|
-
nodeId: payload.sectionId,
|
|
14791
|
+
const targetSectionLocation = findSectionLocation({
|
|
14792
|
+
state,
|
|
14793
|
+
sectionId: payload.toSectionId,
|
|
13551
14794
|
});
|
|
13552
|
-
|
|
13553
|
-
|
|
13554
|
-
|
|
13555
|
-
const sourceSectionCount = Object.keys(
|
|
13556
|
-
location.sections?.items ?? {},
|
|
13557
|
-
).length;
|
|
13558
|
-
const movedSectionCount = collectTreeDescendantIds({
|
|
13559
|
-
node: sectionNode,
|
|
13560
|
-
}).length;
|
|
13561
|
-
|
|
13562
|
-
if (sourceSectionCount <= movedSectionCount) {
|
|
13563
|
-
return invalidPrecondition(
|
|
13564
|
-
"payload.sectionId must not move the last section out of a scene",
|
|
13565
|
-
);
|
|
13566
|
-
}
|
|
13567
|
-
}
|
|
13568
|
-
|
|
13569
|
-
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
13570
|
-
const parentLocation = findSectionLocation({
|
|
13571
|
-
state,
|
|
13572
|
-
sectionId: payload.parentId,
|
|
13573
|
-
});
|
|
13574
|
-
if (!parentLocation) {
|
|
13575
|
-
return invalidPrecondition(
|
|
13576
|
-
"payload.parentId must reference an existing section",
|
|
13577
|
-
);
|
|
13578
|
-
}
|
|
13579
|
-
|
|
13580
|
-
if (parentLocation.sceneId !== targetSceneId) {
|
|
13581
|
-
return invalidPrecondition(
|
|
13582
|
-
"payload.parentId must reference a section in the target scene",
|
|
13583
|
-
);
|
|
13584
|
-
}
|
|
13585
|
-
|
|
13586
|
-
const descendantIds = new Set(
|
|
13587
|
-
collectTreeDescendantIds({
|
|
13588
|
-
node: sectionNode,
|
|
13589
|
-
}),
|
|
14795
|
+
if (!targetSectionLocation) {
|
|
14796
|
+
return invalidPrecondition(
|
|
14797
|
+
"payload.toSectionId must reference an existing section",
|
|
13590
14798
|
);
|
|
13591
|
-
|
|
13592
|
-
if (descendantIds.has(payload.parentId)) {
|
|
13593
|
-
return invalidPrecondition(
|
|
13594
|
-
"payload.parentId must not target the moved section or its descendants",
|
|
13595
|
-
);
|
|
13596
|
-
}
|
|
13597
14799
|
}
|
|
13598
14800
|
|
|
13599
14801
|
if (payload.positionTargetId !== undefined) {
|
|
13600
|
-
if (payload.positionTargetId === payload.
|
|
14802
|
+
if (payload.positionTargetId === payload.lineId) {
|
|
13601
14803
|
return invalidPrecondition(
|
|
13602
|
-
"payload.positionTargetId must not reference the moved
|
|
14804
|
+
"payload.positionTargetId must not reference the moved line",
|
|
13603
14805
|
);
|
|
13604
14806
|
}
|
|
13605
14807
|
|
|
13606
|
-
const targetLocation =
|
|
14808
|
+
const targetLocation = findLineLocation({
|
|
13607
14809
|
state,
|
|
13608
|
-
|
|
14810
|
+
lineId: payload.positionTargetId,
|
|
13609
14811
|
});
|
|
13610
14812
|
if (!targetLocation) {
|
|
13611
14813
|
return invalidPrecondition(
|
|
13612
|
-
"payload.positionTargetId must reference an existing
|
|
13613
|
-
);
|
|
13614
|
-
}
|
|
13615
|
-
|
|
13616
|
-
if (targetLocation.sceneId !== targetSceneId) {
|
|
13617
|
-
return invalidPrecondition(
|
|
13618
|
-
"payload.positionTargetId must reference a section in the target scene",
|
|
14814
|
+
"payload.positionTargetId must reference an existing line",
|
|
13619
14815
|
);
|
|
13620
14816
|
}
|
|
13621
14817
|
|
|
13622
|
-
|
|
13623
|
-
tree: targetSections.tree,
|
|
13624
|
-
nodeId: payload.positionTargetId,
|
|
13625
|
-
});
|
|
13626
|
-
|
|
13627
|
-
if (targetParentId !== (payload.parentId ?? null)) {
|
|
14818
|
+
if (targetLocation.sectionId !== payload.toSectionId) {
|
|
13628
14819
|
return invalidPrecondition(
|
|
13629
|
-
"payload.positionTargetId must reference a
|
|
14820
|
+
"payload.positionTargetId must reference a line in the target section",
|
|
13630
14821
|
);
|
|
13631
14822
|
}
|
|
13632
14823
|
}
|
|
13633
14824
|
},
|
|
13634
14825
|
reduce: ({ state, payload }) => {
|
|
13635
|
-
const
|
|
14826
|
+
const lineLocation = findLineLocation({
|
|
13636
14827
|
state,
|
|
13637
|
-
|
|
14828
|
+
lineId: payload.lineId,
|
|
13638
14829
|
});
|
|
13639
|
-
|
|
13640
|
-
const
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
tree: location.sections.tree,
|
|
13645
|
-
nodeId: payload.sectionId,
|
|
13646
|
-
errorMessage: "section move target missing from tree",
|
|
14830
|
+
lineLocation.section.lines ??= createEmptyNestedCollection();
|
|
14831
|
+
const lineNodeResult = removeNodeOrResult({
|
|
14832
|
+
tree: lineLocation.section.lines.tree,
|
|
14833
|
+
nodeId: payload.lineId,
|
|
14834
|
+
errorMessage: "line move target missing from tree",
|
|
13647
14835
|
});
|
|
13648
|
-
if (!
|
|
13649
|
-
return
|
|
13650
|
-
}
|
|
13651
|
-
|
|
13652
|
-
if (targetSections !== location.sections) {
|
|
13653
|
-
const movedSectionIds = collectTreeDescendantIds({
|
|
13654
|
-
node: sectionNodeResult.node,
|
|
13655
|
-
});
|
|
13656
|
-
|
|
13657
|
-
for (const movedSectionId of movedSectionIds) {
|
|
13658
|
-
targetSections.items[movedSectionId] =
|
|
13659
|
-
location.sections.items[movedSectionId];
|
|
13660
|
-
delete location.sections.items[movedSectionId];
|
|
13661
|
-
}
|
|
14836
|
+
if (!lineNodeResult.valid) {
|
|
14837
|
+
return lineNodeResult;
|
|
13662
14838
|
}
|
|
14839
|
+
const lineValue = lineLocation.line;
|
|
14840
|
+
|
|
14841
|
+
delete lineLocation.section.lines.items[payload.lineId];
|
|
14842
|
+
|
|
14843
|
+
const targetSectionLocation = findSectionLocation({
|
|
14844
|
+
state,
|
|
14845
|
+
sectionId: payload.toSectionId,
|
|
14846
|
+
});
|
|
14847
|
+
targetSectionLocation.section.lines ??= createEmptyNestedCollection();
|
|
14848
|
+
targetSectionLocation.section.lines.items[payload.lineId] = lineValue;
|
|
13663
14849
|
|
|
13664
14850
|
insertTreeNode({
|
|
13665
|
-
tree:
|
|
13666
|
-
node:
|
|
13667
|
-
parentId: payload.parentId ?? null,
|
|
14851
|
+
tree: targetSectionLocation.section.lines.tree,
|
|
14852
|
+
node: lineNodeResult.node,
|
|
13668
14853
|
index: payload.index,
|
|
13669
14854
|
position: payload.position,
|
|
13670
14855
|
positionTargetId: payload.positionTargetId,
|
|
@@ -13674,14 +14859,15 @@ const COMMAND_DEFINITIONS = [
|
|
|
13674
14859
|
},
|
|
13675
14860
|
},
|
|
13676
14861
|
{
|
|
13677
|
-
type: "
|
|
14862
|
+
type: "image.create",
|
|
13678
14863
|
validatePayload: ({ payload }) => {
|
|
13679
14864
|
{
|
|
13680
14865
|
const result = validateAllowedKeys({
|
|
13681
14866
|
value: payload,
|
|
13682
14867
|
allowedKeys: [
|
|
13683
|
-
"
|
|
13684
|
-
"
|
|
14868
|
+
"imageId",
|
|
14869
|
+
"parentId",
|
|
14870
|
+
"data",
|
|
13685
14871
|
"index",
|
|
13686
14872
|
"position",
|
|
13687
14873
|
"positionTargetId",
|
|
@@ -13694,13 +14880,23 @@ const COMMAND_DEFINITIONS = [
|
|
|
13694
14880
|
}
|
|
13695
14881
|
}
|
|
13696
14882
|
|
|
13697
|
-
if (!isNonEmptyString(payload.
|
|
13698
|
-
return invalidPayload("payload.
|
|
14883
|
+
if (!isNonEmptyString(payload.imageId)) {
|
|
14884
|
+
return invalidPayload("payload.imageId must be a non-empty string");
|
|
14885
|
+
}
|
|
14886
|
+
|
|
14887
|
+
if (
|
|
14888
|
+
payload.parentId !== undefined &&
|
|
14889
|
+
payload.parentId !== null &&
|
|
14890
|
+
!isNonEmptyString(payload.parentId)
|
|
14891
|
+
) {
|
|
14892
|
+
return invalidPayload(
|
|
14893
|
+
"payload.parentId must be a non-empty string when provided",
|
|
14894
|
+
);
|
|
13699
14895
|
}
|
|
13700
14896
|
|
|
13701
14897
|
{
|
|
13702
|
-
const result =
|
|
13703
|
-
payload,
|
|
14898
|
+
const result = validateImageCreateData({
|
|
14899
|
+
data: payload.data,
|
|
13704
14900
|
errorFactory: createPayloadValidationError,
|
|
13705
14901
|
});
|
|
13706
14902
|
if (result?.valid === false) {
|
|
@@ -13719,82 +14915,122 @@ const COMMAND_DEFINITIONS = [
|
|
|
13719
14915
|
}
|
|
13720
14916
|
},
|
|
13721
14917
|
validateAgainstState: ({ state, payload }) => {
|
|
13722
|
-
|
|
13723
|
-
|
|
13724
|
-
sectionId: payload.sectionId,
|
|
13725
|
-
});
|
|
13726
|
-
if (!sectionLocation) {
|
|
13727
|
-
return invalidPrecondition(
|
|
13728
|
-
"payload.sectionId must reference an existing section",
|
|
13729
|
-
);
|
|
14918
|
+
if (isPlainObject(state.images.items[payload.imageId])) {
|
|
14919
|
+
return invalidPrecondition("payload.imageId must not already exist");
|
|
13730
14920
|
}
|
|
13731
14921
|
|
|
13732
|
-
|
|
13733
|
-
|
|
14922
|
+
const parentId = payload.parentId ?? null;
|
|
14923
|
+
if (parentId !== null) {
|
|
14924
|
+
const parentImage = state.images.items[parentId];
|
|
14925
|
+
if (!isPlainObject(parentImage)) {
|
|
13734
14926
|
return invalidPrecondition(
|
|
13735
|
-
"payload.
|
|
13736
|
-
|
|
14927
|
+
"payload.parentId must reference an existing image item",
|
|
14928
|
+
);
|
|
14929
|
+
}
|
|
14930
|
+
|
|
14931
|
+
if (parentImage.type !== "folder") {
|
|
14932
|
+
return invalidPrecondition(
|
|
14933
|
+
"payload.parentId must reference a folder image item",
|
|
13737
14934
|
);
|
|
13738
14935
|
}
|
|
13739
14936
|
}
|
|
13740
14937
|
|
|
13741
14938
|
if (payload.positionTargetId !== undefined) {
|
|
13742
|
-
|
|
13743
|
-
state,
|
|
13744
|
-
lineId: payload.positionTargetId,
|
|
13745
|
-
});
|
|
13746
|
-
if (!targetLocation) {
|
|
14939
|
+
if (!isPlainObject(state.images.items[payload.positionTargetId])) {
|
|
13747
14940
|
return invalidPrecondition(
|
|
13748
|
-
"payload.positionTargetId must reference an existing
|
|
14941
|
+
"payload.positionTargetId must reference an existing image item",
|
|
13749
14942
|
);
|
|
13750
14943
|
}
|
|
13751
14944
|
|
|
13752
|
-
|
|
14945
|
+
const targetParentId = getNodeParentId({
|
|
14946
|
+
tree: state.images.tree,
|
|
14947
|
+
nodeId: payload.positionTargetId,
|
|
14948
|
+
});
|
|
14949
|
+
|
|
14950
|
+
if (targetParentId !== parentId) {
|
|
13753
14951
|
return invalidPrecondition(
|
|
13754
|
-
"payload.positionTargetId must reference a
|
|
14952
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
13755
14953
|
);
|
|
13756
14954
|
}
|
|
13757
14955
|
}
|
|
14956
|
+
|
|
14957
|
+
if (payload.data.type === "image") {
|
|
14958
|
+
const result = validateReferencedFilesInData({
|
|
14959
|
+
state,
|
|
14960
|
+
data: payload.data,
|
|
14961
|
+
fields: ["fileId", "thumbnailFileId"],
|
|
14962
|
+
details: {
|
|
14963
|
+
imageId: payload.imageId,
|
|
14964
|
+
},
|
|
14965
|
+
});
|
|
14966
|
+
if (!result.valid) {
|
|
14967
|
+
return result;
|
|
14968
|
+
}
|
|
14969
|
+
|
|
14970
|
+
return validateTagIdsAgainstScope({
|
|
14971
|
+
state,
|
|
14972
|
+
tagIds: payload.data.tagIds,
|
|
14973
|
+
scopeKey: "images",
|
|
14974
|
+
path: "payload.data.tagIds",
|
|
14975
|
+
details: {
|
|
14976
|
+
imageId: payload.imageId,
|
|
14977
|
+
},
|
|
14978
|
+
});
|
|
14979
|
+
}
|
|
13758
14980
|
},
|
|
13759
14981
|
reduce: ({ state, payload }) => {
|
|
13760
|
-
const
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
const lines = sectionLocation.section.lines;
|
|
13766
|
-
let previousLineId = payload.positionTargetId;
|
|
14982
|
+
const nextImage = {
|
|
14983
|
+
id: payload.imageId,
|
|
14984
|
+
type: payload.data.type,
|
|
14985
|
+
name: payload.data.name,
|
|
14986
|
+
};
|
|
13767
14987
|
|
|
13768
|
-
payload.
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
actions: structuredClone(item.data.actions || {}),
|
|
13772
|
-
};
|
|
14988
|
+
if (payload.data.description !== undefined) {
|
|
14989
|
+
nextImage.description = payload.data.description;
|
|
14990
|
+
}
|
|
13773
14991
|
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
|
|
14992
|
+
if (payload.data.type === "image") {
|
|
14993
|
+
nextImage.fileId = payload.data.fileId;
|
|
14994
|
+
if (payload.data.thumbnailFileId !== undefined) {
|
|
14995
|
+
nextImage.thumbnailFileId = payload.data.thumbnailFileId;
|
|
14996
|
+
}
|
|
14997
|
+
if (payload.data.width !== undefined) {
|
|
14998
|
+
nextImage.width = payload.data.width;
|
|
14999
|
+
}
|
|
15000
|
+
if (payload.data.height !== undefined) {
|
|
15001
|
+
nextImage.height = payload.data.height;
|
|
15002
|
+
}
|
|
15003
|
+
|
|
15004
|
+
assignOptionalTagIds({
|
|
15005
|
+
target: nextImage,
|
|
15006
|
+
tagIds: payload.data.tagIds,
|
|
13783
15007
|
});
|
|
15008
|
+
}
|
|
13784
15009
|
|
|
13785
|
-
|
|
15010
|
+
state.images.items[payload.imageId] = nextImage;
|
|
15011
|
+
|
|
15012
|
+
insertTreeNode({
|
|
15013
|
+
tree: state.images.tree,
|
|
15014
|
+
node: {
|
|
15015
|
+
id: payload.imageId,
|
|
15016
|
+
children: [],
|
|
15017
|
+
},
|
|
15018
|
+
parentId: payload.parentId ?? null,
|
|
15019
|
+
index: payload.index,
|
|
15020
|
+
position: payload.position,
|
|
15021
|
+
positionTargetId: payload.positionTargetId,
|
|
13786
15022
|
});
|
|
13787
15023
|
|
|
13788
15024
|
return state;
|
|
13789
15025
|
},
|
|
13790
15026
|
},
|
|
13791
15027
|
{
|
|
13792
|
-
type: "
|
|
15028
|
+
type: "image.update",
|
|
13793
15029
|
validatePayload: ({ payload }) => {
|
|
13794
15030
|
{
|
|
13795
|
-
const result =
|
|
15031
|
+
const result = validateExactKeys({
|
|
13796
15032
|
value: payload,
|
|
13797
|
-
|
|
15033
|
+
expectedKeys: ["imageId", "data"],
|
|
13798
15034
|
path: "payload",
|
|
13799
15035
|
errorFactory: createPayloadValidationError,
|
|
13800
15036
|
});
|
|
@@ -13803,12 +15039,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
13803
15039
|
}
|
|
13804
15040
|
}
|
|
13805
15041
|
|
|
13806
|
-
if (!isNonEmptyString(payload.
|
|
13807
|
-
return invalidPayload("payload.
|
|
15042
|
+
if (!isNonEmptyString(payload.imageId)) {
|
|
15043
|
+
return invalidPayload("payload.imageId must be a non-empty string");
|
|
13808
15044
|
}
|
|
13809
15045
|
|
|
13810
15046
|
{
|
|
13811
|
-
const result =
|
|
15047
|
+
const result = validateImageUpdateData({
|
|
13812
15048
|
data: payload.data,
|
|
13813
15049
|
errorFactory: createPayloadValidationError,
|
|
13814
15050
|
});
|
|
@@ -13816,60 +15052,68 @@ const COMMAND_DEFINITIONS = [
|
|
|
13816
15052
|
return result;
|
|
13817
15053
|
}
|
|
13818
15054
|
}
|
|
15055
|
+
},
|
|
15056
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15057
|
+
const currentImage = state.images.items[payload.imageId];
|
|
15058
|
+
if (!isPlainObject(currentImage)) {
|
|
15059
|
+
return invalidPrecondition(
|
|
15060
|
+
"payload.imageId must reference an existing image item",
|
|
15061
|
+
);
|
|
15062
|
+
}
|
|
13819
15063
|
|
|
13820
15064
|
if (
|
|
13821
|
-
|
|
13822
|
-
|
|
15065
|
+
currentImage.type === "folder" &&
|
|
15066
|
+
(payload.data.fileId !== undefined ||
|
|
15067
|
+
payload.data.thumbnailFileId !== undefined ||
|
|
15068
|
+
payload.data.width !== undefined ||
|
|
15069
|
+
payload.data.height !== undefined ||
|
|
15070
|
+
payload.data.tagIds !== undefined)
|
|
13823
15071
|
) {
|
|
13824
|
-
return
|
|
13825
|
-
"
|
|
15072
|
+
return invalidPrecondition(
|
|
15073
|
+
"folder image items cannot update file fields",
|
|
13826
15074
|
);
|
|
13827
15075
|
}
|
|
13828
15076
|
|
|
13829
|
-
{
|
|
13830
|
-
const result =
|
|
13831
|
-
|
|
15077
|
+
if (currentImage.type === "image") {
|
|
15078
|
+
const result = validateReferencedFilesInData({
|
|
15079
|
+
state,
|
|
13832
15080
|
data: payload.data,
|
|
13833
|
-
|
|
13834
|
-
|
|
15081
|
+
fields: ["fileId", "thumbnailFileId"],
|
|
15082
|
+
details: {
|
|
15083
|
+
imageId: payload.imageId,
|
|
15084
|
+
},
|
|
13835
15085
|
});
|
|
13836
|
-
if (result
|
|
15086
|
+
if (!result.valid) {
|
|
13837
15087
|
return result;
|
|
13838
15088
|
}
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
"payload.
|
|
13845
|
-
|
|
15089
|
+
|
|
15090
|
+
return validateTagIdsAgainstScope({
|
|
15091
|
+
state,
|
|
15092
|
+
tagIds: payload.data.tagIds,
|
|
15093
|
+
scopeKey: "images",
|
|
15094
|
+
path: "payload.data.tagIds",
|
|
15095
|
+
details: {
|
|
15096
|
+
imageId: payload.imageId,
|
|
15097
|
+
},
|
|
15098
|
+
});
|
|
13846
15099
|
}
|
|
13847
15100
|
},
|
|
13848
15101
|
reduce: ({ state, payload }) => {
|
|
13849
|
-
const
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
currentActions: line.actions,
|
|
15102
|
+
const currentImage = state.images.items[payload.imageId];
|
|
15103
|
+
state.images.items[payload.imageId] = applyTagIdsUpdate({
|
|
15104
|
+
currentItem: currentImage,
|
|
13853
15105
|
data: payload.data,
|
|
13854
|
-
preserve: payload.preserve,
|
|
13855
15106
|
});
|
|
13856
|
-
line.actions =
|
|
13857
|
-
payload.replace === true
|
|
13858
|
-
? structuredClone(nextData)
|
|
13859
|
-
: {
|
|
13860
|
-
...structuredClone(line.actions),
|
|
13861
|
-
...structuredClone(nextData),
|
|
13862
|
-
};
|
|
13863
15107
|
return state;
|
|
13864
15108
|
},
|
|
13865
15109
|
},
|
|
13866
15110
|
{
|
|
13867
|
-
type: "
|
|
15111
|
+
type: "image.delete",
|
|
13868
15112
|
validatePayload: ({ payload }) => {
|
|
13869
15113
|
{
|
|
13870
15114
|
const result = validateExactKeys({
|
|
13871
15115
|
value: payload,
|
|
13872
|
-
expectedKeys: ["
|
|
15116
|
+
expectedKeys: ["imageIds"],
|
|
13873
15117
|
path: "payload",
|
|
13874
15118
|
errorFactory: createPayloadValidationError,
|
|
13875
15119
|
});
|
|
@@ -13880,8 +15124,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
13880
15124
|
|
|
13881
15125
|
{
|
|
13882
15126
|
const result = validateRequiredUniqueIdArray({
|
|
13883
|
-
value: payload.
|
|
13884
|
-
path: "payload.
|
|
15127
|
+
value: payload.imageIds,
|
|
15128
|
+
path: "payload.imageIds",
|
|
13885
15129
|
errorFactory: createPayloadValidationError,
|
|
13886
15130
|
});
|
|
13887
15131
|
if (result?.valid === false) {
|
|
@@ -13890,38 +15134,49 @@ const COMMAND_DEFINITIONS = [
|
|
|
13890
15134
|
}
|
|
13891
15135
|
},
|
|
13892
15136
|
validateAgainstState: ({ state, payload }) => {
|
|
13893
|
-
for (const
|
|
13894
|
-
if (!
|
|
15137
|
+
for (const imageId of payload.imageIds) {
|
|
15138
|
+
if (!isPlainObject(state.images.items[imageId])) {
|
|
13895
15139
|
return invalidPrecondition(
|
|
13896
|
-
"payload.
|
|
13897
|
-
{
|
|
15140
|
+
"payload.imageIds must reference existing image items",
|
|
15141
|
+
{ imageId },
|
|
13898
15142
|
);
|
|
13899
15143
|
}
|
|
13900
15144
|
}
|
|
13901
15145
|
},
|
|
13902
15146
|
reduce: ({ state, payload }) => {
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
nodeId: lineId,
|
|
15147
|
+
const deletedImageIds = new Set();
|
|
15148
|
+
|
|
15149
|
+
for (const imageId of payload.imageIds) {
|
|
15150
|
+
const removedNode = removeTreeNode({
|
|
15151
|
+
nodes: state.images.tree,
|
|
15152
|
+
nodeId: imageId,
|
|
13910
15153
|
});
|
|
15154
|
+
|
|
15155
|
+
if (!removedNode) {
|
|
15156
|
+
continue;
|
|
15157
|
+
}
|
|
15158
|
+
|
|
15159
|
+
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
15160
|
+
deletedImageIds.add(id);
|
|
15161
|
+
}
|
|
15162
|
+
}
|
|
15163
|
+
|
|
15164
|
+
for (const imageId of deletedImageIds) {
|
|
15165
|
+
delete state.images.items[imageId];
|
|
13911
15166
|
}
|
|
13912
15167
|
|
|
13913
15168
|
return state;
|
|
13914
15169
|
},
|
|
13915
15170
|
},
|
|
13916
15171
|
{
|
|
13917
|
-
type: "
|
|
15172
|
+
type: "image.move",
|
|
13918
15173
|
validatePayload: ({ payload }) => {
|
|
13919
15174
|
{
|
|
13920
15175
|
const result = validateAllowedKeys({
|
|
13921
15176
|
value: payload,
|
|
13922
15177
|
allowedKeys: [
|
|
13923
|
-
"
|
|
13924
|
-
"
|
|
15178
|
+
"imageId",
|
|
15179
|
+
"parentId",
|
|
13925
15180
|
"index",
|
|
13926
15181
|
"position",
|
|
13927
15182
|
"positionTargetId",
|
|
@@ -13934,12 +15189,18 @@ const COMMAND_DEFINITIONS = [
|
|
|
13934
15189
|
}
|
|
13935
15190
|
}
|
|
13936
15191
|
|
|
13937
|
-
if (!isNonEmptyString(payload.
|
|
13938
|
-
return invalidPayload("payload.
|
|
15192
|
+
if (!isNonEmptyString(payload.imageId)) {
|
|
15193
|
+
return invalidPayload("payload.imageId must be a non-empty string");
|
|
13939
15194
|
}
|
|
13940
15195
|
|
|
13941
|
-
if (
|
|
13942
|
-
|
|
15196
|
+
if (
|
|
15197
|
+
payload.parentId !== undefined &&
|
|
15198
|
+
payload.parentId !== null &&
|
|
15199
|
+
!isNonEmptyString(payload.parentId)
|
|
15200
|
+
) {
|
|
15201
|
+
return invalidPayload(
|
|
15202
|
+
"payload.parentId must be a non-empty string when provided",
|
|
15203
|
+
);
|
|
13943
15204
|
}
|
|
13944
15205
|
|
|
13945
15206
|
{
|
|
@@ -13953,78 +15214,84 @@ const COMMAND_DEFINITIONS = [
|
|
|
13953
15214
|
}
|
|
13954
15215
|
},
|
|
13955
15216
|
validateAgainstState: ({ state, payload }) => {
|
|
13956
|
-
const
|
|
13957
|
-
|
|
13958
|
-
lineId: payload.lineId,
|
|
13959
|
-
});
|
|
13960
|
-
if (!lineLocation) {
|
|
15217
|
+
const image = state.images.items[payload.imageId];
|
|
15218
|
+
if (!isPlainObject(image)) {
|
|
13961
15219
|
return invalidPrecondition(
|
|
13962
|
-
"payload.
|
|
15220
|
+
"payload.imageId must reference an existing image item",
|
|
13963
15221
|
);
|
|
13964
15222
|
}
|
|
13965
15223
|
|
|
13966
|
-
const
|
|
13967
|
-
state,
|
|
13968
|
-
|
|
15224
|
+
const imageNode = findTreeNode({
|
|
15225
|
+
nodes: state.images.tree,
|
|
15226
|
+
nodeId: payload.imageId,
|
|
13969
15227
|
});
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
15228
|
+
|
|
15229
|
+
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
15230
|
+
const parentImage = state.images.items[payload.parentId];
|
|
15231
|
+
if (!isPlainObject(parentImage)) {
|
|
15232
|
+
return invalidPrecondition(
|
|
15233
|
+
"payload.parentId must reference an existing image item",
|
|
15234
|
+
);
|
|
15235
|
+
}
|
|
15236
|
+
|
|
15237
|
+
if (parentImage.type !== "folder") {
|
|
15238
|
+
return invalidPrecondition(
|
|
15239
|
+
"payload.parentId must reference a folder image item",
|
|
15240
|
+
);
|
|
15241
|
+
}
|
|
15242
|
+
|
|
15243
|
+
const descendantIds = new Set(
|
|
15244
|
+
collectTreeDescendantIds({
|
|
15245
|
+
node: imageNode,
|
|
15246
|
+
}),
|
|
13973
15247
|
);
|
|
15248
|
+
|
|
15249
|
+
if (descendantIds.has(payload.parentId)) {
|
|
15250
|
+
return invalidPrecondition(
|
|
15251
|
+
"payload.parentId must not target the moved image item or its descendants",
|
|
15252
|
+
);
|
|
15253
|
+
}
|
|
13974
15254
|
}
|
|
13975
15255
|
|
|
13976
15256
|
if (payload.positionTargetId !== undefined) {
|
|
13977
|
-
if (payload.positionTargetId === payload.
|
|
15257
|
+
if (payload.positionTargetId === payload.imageId) {
|
|
13978
15258
|
return invalidPrecondition(
|
|
13979
|
-
"payload.positionTargetId must not reference the moved
|
|
15259
|
+
"payload.positionTargetId must not reference the moved image item",
|
|
13980
15260
|
);
|
|
13981
15261
|
}
|
|
13982
15262
|
|
|
13983
|
-
|
|
13984
|
-
state,
|
|
13985
|
-
lineId: payload.positionTargetId,
|
|
13986
|
-
});
|
|
13987
|
-
if (!targetLocation) {
|
|
15263
|
+
if (!isPlainObject(state.images.items[payload.positionTargetId])) {
|
|
13988
15264
|
return invalidPrecondition(
|
|
13989
|
-
"payload.positionTargetId must reference an existing
|
|
15265
|
+
"payload.positionTargetId must reference an existing image item",
|
|
13990
15266
|
);
|
|
13991
15267
|
}
|
|
13992
15268
|
|
|
13993
|
-
|
|
15269
|
+
const targetParentId = getNodeParentId({
|
|
15270
|
+
tree: state.images.tree,
|
|
15271
|
+
nodeId: payload.positionTargetId,
|
|
15272
|
+
});
|
|
15273
|
+
|
|
15274
|
+
if (targetParentId !== (payload.parentId ?? null)) {
|
|
13994
15275
|
return invalidPrecondition(
|
|
13995
|
-
"payload.positionTargetId must reference a
|
|
15276
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
13996
15277
|
);
|
|
13997
15278
|
}
|
|
13998
15279
|
}
|
|
13999
15280
|
},
|
|
14000
15281
|
reduce: ({ state, payload }) => {
|
|
14001
|
-
const
|
|
14002
|
-
state,
|
|
14003
|
-
|
|
14004
|
-
|
|
14005
|
-
lineLocation.section.lines ??= createEmptyNestedCollection();
|
|
14006
|
-
const lineNodeResult = removeNodeOrResult({
|
|
14007
|
-
tree: lineLocation.section.lines.tree,
|
|
14008
|
-
nodeId: payload.lineId,
|
|
14009
|
-
errorMessage: "line move target missing from tree",
|
|
15282
|
+
const imageNodeResult = removeNodeOrResult({
|
|
15283
|
+
tree: state.images.tree,
|
|
15284
|
+
nodeId: payload.imageId,
|
|
15285
|
+
errorMessage: "image move target missing from tree",
|
|
14010
15286
|
});
|
|
14011
|
-
if (!
|
|
14012
|
-
return
|
|
15287
|
+
if (!imageNodeResult.valid) {
|
|
15288
|
+
return imageNodeResult;
|
|
14013
15289
|
}
|
|
14014
|
-
const lineValue = lineLocation.line;
|
|
14015
|
-
|
|
14016
|
-
delete lineLocation.section.lines.items[payload.lineId];
|
|
14017
|
-
|
|
14018
|
-
const targetSectionLocation = findSectionLocation({
|
|
14019
|
-
state,
|
|
14020
|
-
sectionId: payload.toSectionId,
|
|
14021
|
-
});
|
|
14022
|
-
targetSectionLocation.section.lines ??= createEmptyNestedCollection();
|
|
14023
|
-
targetSectionLocation.section.lines.items[payload.lineId] = lineValue;
|
|
14024
15290
|
|
|
14025
15291
|
insertTreeNode({
|
|
14026
|
-
tree:
|
|
14027
|
-
node:
|
|
15292
|
+
tree: state.images.tree,
|
|
15293
|
+
node: imageNodeResult.node,
|
|
15294
|
+
parentId: payload.parentId ?? null,
|
|
14028
15295
|
index: payload.index,
|
|
14029
15296
|
position: payload.position,
|
|
14030
15297
|
positionTargetId: payload.positionTargetId,
|
|
@@ -14034,13 +15301,13 @@ const COMMAND_DEFINITIONS = [
|
|
|
14034
15301
|
},
|
|
14035
15302
|
},
|
|
14036
15303
|
{
|
|
14037
|
-
type: "
|
|
15304
|
+
type: "sound.create",
|
|
14038
15305
|
validatePayload: ({ payload }) => {
|
|
14039
15306
|
{
|
|
14040
15307
|
const result = validateAllowedKeys({
|
|
14041
15308
|
value: payload,
|
|
14042
15309
|
allowedKeys: [
|
|
14043
|
-
"
|
|
15310
|
+
"soundId",
|
|
14044
15311
|
"parentId",
|
|
14045
15312
|
"data",
|
|
14046
15313
|
"index",
|
|
@@ -14055,8 +15322,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14055
15322
|
}
|
|
14056
15323
|
}
|
|
14057
15324
|
|
|
14058
|
-
if (!isNonEmptyString(payload.
|
|
14059
|
-
return invalidPayload("payload.
|
|
15325
|
+
if (!isNonEmptyString(payload.soundId)) {
|
|
15326
|
+
return invalidPayload("payload.soundId must be a non-empty string");
|
|
14060
15327
|
}
|
|
14061
15328
|
|
|
14062
15329
|
if (
|
|
@@ -14070,7 +15337,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
14070
15337
|
}
|
|
14071
15338
|
|
|
14072
15339
|
{
|
|
14073
|
-
const result =
|
|
15340
|
+
const result = validateSoundCreateData({
|
|
14074
15341
|
data: payload.data,
|
|
14075
15342
|
errorFactory: createPayloadValidationError,
|
|
14076
15343
|
});
|
|
@@ -14090,35 +15357,35 @@ const COMMAND_DEFINITIONS = [
|
|
|
14090
15357
|
}
|
|
14091
15358
|
},
|
|
14092
15359
|
validateAgainstState: ({ state, payload }) => {
|
|
14093
|
-
if (isPlainObject(state.
|
|
14094
|
-
return invalidPrecondition("payload.
|
|
15360
|
+
if (isPlainObject(state.sounds.items[payload.soundId])) {
|
|
15361
|
+
return invalidPrecondition("payload.soundId must not already exist");
|
|
14095
15362
|
}
|
|
14096
15363
|
|
|
14097
15364
|
const parentId = payload.parentId ?? null;
|
|
14098
15365
|
if (parentId !== null) {
|
|
14099
|
-
const
|
|
14100
|
-
if (!isPlainObject(
|
|
15366
|
+
const parentSound = state.sounds.items[parentId];
|
|
15367
|
+
if (!isPlainObject(parentSound)) {
|
|
14101
15368
|
return invalidPrecondition(
|
|
14102
|
-
"payload.parentId must reference an existing
|
|
15369
|
+
"payload.parentId must reference an existing sound item",
|
|
14103
15370
|
);
|
|
14104
15371
|
}
|
|
14105
15372
|
|
|
14106
|
-
if (
|
|
15373
|
+
if (parentSound.type !== "folder") {
|
|
14107
15374
|
return invalidPrecondition(
|
|
14108
|
-
"payload.parentId must reference a folder
|
|
15375
|
+
"payload.parentId must reference a folder sound item",
|
|
14109
15376
|
);
|
|
14110
15377
|
}
|
|
14111
15378
|
}
|
|
14112
15379
|
|
|
14113
15380
|
if (payload.positionTargetId !== undefined) {
|
|
14114
|
-
if (!isPlainObject(state.
|
|
15381
|
+
if (!isPlainObject(state.sounds.items[payload.positionTargetId])) {
|
|
14115
15382
|
return invalidPrecondition(
|
|
14116
|
-
"payload.positionTargetId must reference an existing
|
|
15383
|
+
"payload.positionTargetId must reference an existing sound item",
|
|
14117
15384
|
);
|
|
14118
15385
|
}
|
|
14119
15386
|
|
|
14120
15387
|
const targetParentId = getNodeParentId({
|
|
14121
|
-
tree: state.
|
|
15388
|
+
tree: state.sounds.tree,
|
|
14122
15389
|
nodeId: payload.positionTargetId,
|
|
14123
15390
|
});
|
|
14124
15391
|
|
|
@@ -14129,13 +15396,14 @@ const COMMAND_DEFINITIONS = [
|
|
|
14129
15396
|
}
|
|
14130
15397
|
}
|
|
14131
15398
|
|
|
14132
|
-
if (payload.data.type === "
|
|
15399
|
+
if (payload.data.type === "sound") {
|
|
14133
15400
|
const result = validateReferencedFilesInData({
|
|
14134
15401
|
state,
|
|
14135
15402
|
data: payload.data,
|
|
14136
|
-
fields: ["fileId", "
|
|
15403
|
+
fields: ["fileId", "waveformDataFileId"],
|
|
15404
|
+
nullableFields: ["waveformDataFileId"],
|
|
14137
15405
|
details: {
|
|
14138
|
-
|
|
15406
|
+
soundId: payload.soundId,
|
|
14139
15407
|
},
|
|
14140
15408
|
});
|
|
14141
15409
|
if (!result.valid) {
|
|
@@ -14145,49 +15413,46 @@ const COMMAND_DEFINITIONS = [
|
|
|
14145
15413
|
return validateTagIdsAgainstScope({
|
|
14146
15414
|
state,
|
|
14147
15415
|
tagIds: payload.data.tagIds,
|
|
14148
|
-
scopeKey: "
|
|
15416
|
+
scopeKey: "sounds",
|
|
14149
15417
|
path: "payload.data.tagIds",
|
|
14150
15418
|
details: {
|
|
14151
|
-
|
|
15419
|
+
soundId: payload.soundId,
|
|
14152
15420
|
},
|
|
14153
15421
|
});
|
|
14154
15422
|
}
|
|
14155
15423
|
},
|
|
14156
15424
|
reduce: ({ state, payload }) => {
|
|
14157
|
-
const
|
|
14158
|
-
id: payload.
|
|
15425
|
+
const nextSound = {
|
|
15426
|
+
id: payload.soundId,
|
|
14159
15427
|
type: payload.data.type,
|
|
14160
15428
|
name: payload.data.name,
|
|
14161
15429
|
};
|
|
14162
15430
|
|
|
14163
15431
|
if (payload.data.description !== undefined) {
|
|
14164
|
-
|
|
15432
|
+
nextSound.description = payload.data.description;
|
|
14165
15433
|
}
|
|
14166
15434
|
|
|
14167
|
-
if (payload.data.type === "
|
|
14168
|
-
|
|
14169
|
-
if (payload.data.
|
|
14170
|
-
|
|
14171
|
-
}
|
|
14172
|
-
if (payload.data.width !== undefined) {
|
|
14173
|
-
nextImage.width = payload.data.width;
|
|
15435
|
+
if (payload.data.type === "sound") {
|
|
15436
|
+
nextSound.fileId = payload.data.fileId;
|
|
15437
|
+
if (payload.data.waveformDataFileId !== undefined) {
|
|
15438
|
+
nextSound.waveformDataFileId = payload.data.waveformDataFileId;
|
|
14174
15439
|
}
|
|
14175
|
-
if (payload.data.
|
|
14176
|
-
|
|
15440
|
+
if (payload.data.duration !== undefined) {
|
|
15441
|
+
nextSound.duration = payload.data.duration;
|
|
14177
15442
|
}
|
|
14178
15443
|
|
|
14179
15444
|
assignOptionalTagIds({
|
|
14180
|
-
target:
|
|
15445
|
+
target: nextSound,
|
|
14181
15446
|
tagIds: payload.data.tagIds,
|
|
14182
15447
|
});
|
|
14183
15448
|
}
|
|
14184
15449
|
|
|
14185
|
-
state.
|
|
15450
|
+
state.sounds.items[payload.soundId] = nextSound;
|
|
14186
15451
|
|
|
14187
15452
|
insertTreeNode({
|
|
14188
|
-
tree: state.
|
|
15453
|
+
tree: state.sounds.tree,
|
|
14189
15454
|
node: {
|
|
14190
|
-
id: payload.
|
|
15455
|
+
id: payload.soundId,
|
|
14191
15456
|
children: [],
|
|
14192
15457
|
},
|
|
14193
15458
|
parentId: payload.parentId ?? null,
|
|
@@ -14200,12 +15465,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
14200
15465
|
},
|
|
14201
15466
|
},
|
|
14202
15467
|
{
|
|
14203
|
-
type: "
|
|
15468
|
+
type: "sound.update",
|
|
14204
15469
|
validatePayload: ({ payload }) => {
|
|
14205
15470
|
{
|
|
14206
15471
|
const result = validateExactKeys({
|
|
14207
15472
|
value: payload,
|
|
14208
|
-
expectedKeys: ["
|
|
15473
|
+
expectedKeys: ["soundId", "data"],
|
|
14209
15474
|
path: "payload",
|
|
14210
15475
|
errorFactory: createPayloadValidationError,
|
|
14211
15476
|
});
|
|
@@ -14214,12 +15479,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
14214
15479
|
}
|
|
14215
15480
|
}
|
|
14216
15481
|
|
|
14217
|
-
if (!isNonEmptyString(payload.
|
|
14218
|
-
return invalidPayload("payload.
|
|
15482
|
+
if (!isNonEmptyString(payload.soundId)) {
|
|
15483
|
+
return invalidPayload("payload.soundId must be a non-empty string");
|
|
14219
15484
|
}
|
|
14220
15485
|
|
|
14221
15486
|
{
|
|
14222
|
-
const result =
|
|
15487
|
+
const result = validateSoundUpdateData({
|
|
14223
15488
|
data: payload.data,
|
|
14224
15489
|
errorFactory: createPayloadValidationError,
|
|
14225
15490
|
});
|
|
@@ -14229,33 +15494,33 @@ const COMMAND_DEFINITIONS = [
|
|
|
14229
15494
|
}
|
|
14230
15495
|
},
|
|
14231
15496
|
validateAgainstState: ({ state, payload }) => {
|
|
14232
|
-
const
|
|
14233
|
-
if (!isPlainObject(
|
|
15497
|
+
const currentSound = state.sounds.items[payload.soundId];
|
|
15498
|
+
if (!isPlainObject(currentSound)) {
|
|
14234
15499
|
return invalidPrecondition(
|
|
14235
|
-
"payload.
|
|
15500
|
+
"payload.soundId must reference an existing sound item",
|
|
14236
15501
|
);
|
|
14237
15502
|
}
|
|
14238
15503
|
|
|
14239
15504
|
if (
|
|
14240
|
-
|
|
15505
|
+
currentSound.type === "folder" &&
|
|
14241
15506
|
(payload.data.fileId !== undefined ||
|
|
14242
|
-
payload.data.
|
|
14243
|
-
payload.data.
|
|
14244
|
-
payload.data.height !== undefined ||
|
|
15507
|
+
payload.data.waveformDataFileId !== undefined ||
|
|
15508
|
+
payload.data.duration !== undefined ||
|
|
14245
15509
|
payload.data.tagIds !== undefined)
|
|
14246
15510
|
) {
|
|
14247
15511
|
return invalidPrecondition(
|
|
14248
|
-
"folder
|
|
15512
|
+
"folder sound items cannot update file fields",
|
|
14249
15513
|
);
|
|
14250
15514
|
}
|
|
14251
15515
|
|
|
14252
|
-
if (
|
|
15516
|
+
if (currentSound.type === "sound") {
|
|
14253
15517
|
const result = validateReferencedFilesInData({
|
|
14254
15518
|
state,
|
|
14255
15519
|
data: payload.data,
|
|
14256
|
-
fields: ["fileId", "
|
|
15520
|
+
fields: ["fileId", "waveformDataFileId"],
|
|
15521
|
+
nullableFields: ["waveformDataFileId"],
|
|
14257
15522
|
details: {
|
|
14258
|
-
|
|
15523
|
+
soundId: payload.soundId,
|
|
14259
15524
|
},
|
|
14260
15525
|
});
|
|
14261
15526
|
if (!result.valid) {
|
|
@@ -14265,30 +15530,30 @@ const COMMAND_DEFINITIONS = [
|
|
|
14265
15530
|
return validateTagIdsAgainstScope({
|
|
14266
15531
|
state,
|
|
14267
15532
|
tagIds: payload.data.tagIds,
|
|
14268
|
-
scopeKey: "
|
|
15533
|
+
scopeKey: "sounds",
|
|
14269
15534
|
path: "payload.data.tagIds",
|
|
14270
15535
|
details: {
|
|
14271
|
-
|
|
15536
|
+
soundId: payload.soundId,
|
|
14272
15537
|
},
|
|
14273
15538
|
});
|
|
14274
15539
|
}
|
|
14275
15540
|
},
|
|
14276
15541
|
reduce: ({ state, payload }) => {
|
|
14277
|
-
const
|
|
14278
|
-
state.
|
|
14279
|
-
currentItem:
|
|
15542
|
+
const currentSound = state.sounds.items[payload.soundId];
|
|
15543
|
+
state.sounds.items[payload.soundId] = applyTagIdsUpdate({
|
|
15544
|
+
currentItem: currentSound,
|
|
14280
15545
|
data: payload.data,
|
|
14281
15546
|
});
|
|
14282
15547
|
return state;
|
|
14283
15548
|
},
|
|
14284
15549
|
},
|
|
14285
15550
|
{
|
|
14286
|
-
type: "
|
|
15551
|
+
type: "sound.delete",
|
|
14287
15552
|
validatePayload: ({ payload }) => {
|
|
14288
15553
|
{
|
|
14289
15554
|
const result = validateExactKeys({
|
|
14290
15555
|
value: payload,
|
|
14291
|
-
expectedKeys: ["
|
|
15556
|
+
expectedKeys: ["soundIds"],
|
|
14292
15557
|
path: "payload",
|
|
14293
15558
|
errorFactory: createPayloadValidationError,
|
|
14294
15559
|
});
|
|
@@ -14299,8 +15564,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14299
15564
|
|
|
14300
15565
|
{
|
|
14301
15566
|
const result = validateRequiredUniqueIdArray({
|
|
14302
|
-
value: payload.
|
|
14303
|
-
path: "payload.
|
|
15567
|
+
value: payload.soundIds,
|
|
15568
|
+
path: "payload.soundIds",
|
|
14304
15569
|
errorFactory: createPayloadValidationError,
|
|
14305
15570
|
});
|
|
14306
15571
|
if (result?.valid === false) {
|
|
@@ -14309,22 +15574,22 @@ const COMMAND_DEFINITIONS = [
|
|
|
14309
15574
|
}
|
|
14310
15575
|
},
|
|
14311
15576
|
validateAgainstState: ({ state, payload }) => {
|
|
14312
|
-
for (const
|
|
14313
|
-
if (!isPlainObject(state.
|
|
15577
|
+
for (const soundId of payload.soundIds) {
|
|
15578
|
+
if (!isPlainObject(state.sounds.items[soundId])) {
|
|
14314
15579
|
return invalidPrecondition(
|
|
14315
|
-
"payload.
|
|
14316
|
-
{
|
|
15580
|
+
"payload.soundIds must reference existing sound items",
|
|
15581
|
+
{ soundId },
|
|
14317
15582
|
);
|
|
14318
15583
|
}
|
|
14319
15584
|
}
|
|
14320
15585
|
},
|
|
14321
15586
|
reduce: ({ state, payload }) => {
|
|
14322
|
-
const
|
|
15587
|
+
const deletedSoundIds = new Set();
|
|
14323
15588
|
|
|
14324
|
-
for (const
|
|
15589
|
+
for (const soundId of payload.soundIds) {
|
|
14325
15590
|
const removedNode = removeTreeNode({
|
|
14326
|
-
nodes: state.
|
|
14327
|
-
nodeId:
|
|
15591
|
+
nodes: state.sounds.tree,
|
|
15592
|
+
nodeId: soundId,
|
|
14328
15593
|
});
|
|
14329
15594
|
|
|
14330
15595
|
if (!removedNode) {
|
|
@@ -14332,25 +15597,25 @@ const COMMAND_DEFINITIONS = [
|
|
|
14332
15597
|
}
|
|
14333
15598
|
|
|
14334
15599
|
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
14335
|
-
|
|
15600
|
+
deletedSoundIds.add(id);
|
|
14336
15601
|
}
|
|
14337
15602
|
}
|
|
14338
15603
|
|
|
14339
|
-
for (const
|
|
14340
|
-
delete state.
|
|
15604
|
+
for (const soundId of deletedSoundIds) {
|
|
15605
|
+
delete state.sounds.items[soundId];
|
|
14341
15606
|
}
|
|
14342
15607
|
|
|
14343
15608
|
return state;
|
|
14344
15609
|
},
|
|
14345
15610
|
},
|
|
14346
15611
|
{
|
|
14347
|
-
type: "
|
|
15612
|
+
type: "sound.move",
|
|
14348
15613
|
validatePayload: ({ payload }) => {
|
|
14349
15614
|
{
|
|
14350
15615
|
const result = validateAllowedKeys({
|
|
14351
15616
|
value: payload,
|
|
14352
15617
|
allowedKeys: [
|
|
14353
|
-
"
|
|
15618
|
+
"soundId",
|
|
14354
15619
|
"parentId",
|
|
14355
15620
|
"index",
|
|
14356
15621
|
"position",
|
|
@@ -14364,8 +15629,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14364
15629
|
}
|
|
14365
15630
|
}
|
|
14366
15631
|
|
|
14367
|
-
if (!isNonEmptyString(payload.
|
|
14368
|
-
return invalidPayload("payload.
|
|
15632
|
+
if (!isNonEmptyString(payload.soundId)) {
|
|
15633
|
+
return invalidPayload("payload.soundId must be a non-empty string");
|
|
14369
15634
|
}
|
|
14370
15635
|
|
|
14371
15636
|
if (
|
|
@@ -14389,60 +15654,60 @@ const COMMAND_DEFINITIONS = [
|
|
|
14389
15654
|
}
|
|
14390
15655
|
},
|
|
14391
15656
|
validateAgainstState: ({ state, payload }) => {
|
|
14392
|
-
const
|
|
14393
|
-
if (!isPlainObject(
|
|
15657
|
+
const sound = state.sounds.items[payload.soundId];
|
|
15658
|
+
if (!isPlainObject(sound)) {
|
|
14394
15659
|
return invalidPrecondition(
|
|
14395
|
-
"payload.
|
|
15660
|
+
"payload.soundId must reference an existing sound item",
|
|
14396
15661
|
);
|
|
14397
15662
|
}
|
|
14398
15663
|
|
|
14399
|
-
const
|
|
14400
|
-
nodes: state.
|
|
14401
|
-
nodeId: payload.
|
|
15664
|
+
const soundNode = findTreeNode({
|
|
15665
|
+
nodes: state.sounds.tree,
|
|
15666
|
+
nodeId: payload.soundId,
|
|
14402
15667
|
});
|
|
14403
15668
|
|
|
14404
15669
|
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
14405
|
-
const
|
|
14406
|
-
if (!isPlainObject(
|
|
15670
|
+
const parentSound = state.sounds.items[payload.parentId];
|
|
15671
|
+
if (!isPlainObject(parentSound)) {
|
|
14407
15672
|
return invalidPrecondition(
|
|
14408
|
-
"payload.parentId must reference an existing
|
|
15673
|
+
"payload.parentId must reference an existing sound item",
|
|
14409
15674
|
);
|
|
14410
15675
|
}
|
|
14411
15676
|
|
|
14412
|
-
if (
|
|
15677
|
+
if (parentSound.type !== "folder") {
|
|
14413
15678
|
return invalidPrecondition(
|
|
14414
|
-
"payload.parentId must reference a folder
|
|
15679
|
+
"payload.parentId must reference a folder sound item",
|
|
14415
15680
|
);
|
|
14416
15681
|
}
|
|
14417
15682
|
|
|
14418
15683
|
const descendantIds = new Set(
|
|
14419
15684
|
collectTreeDescendantIds({
|
|
14420
|
-
node:
|
|
15685
|
+
node: soundNode,
|
|
14421
15686
|
}),
|
|
14422
15687
|
);
|
|
14423
15688
|
|
|
14424
15689
|
if (descendantIds.has(payload.parentId)) {
|
|
14425
15690
|
return invalidPrecondition(
|
|
14426
|
-
"payload.parentId must not target the moved
|
|
15691
|
+
"payload.parentId must not target the moved sound item or its descendants",
|
|
14427
15692
|
);
|
|
14428
15693
|
}
|
|
14429
15694
|
}
|
|
14430
15695
|
|
|
14431
15696
|
if (payload.positionTargetId !== undefined) {
|
|
14432
|
-
if (payload.positionTargetId === payload.
|
|
15697
|
+
if (payload.positionTargetId === payload.soundId) {
|
|
14433
15698
|
return invalidPrecondition(
|
|
14434
|
-
"payload.positionTargetId must not reference the moved
|
|
15699
|
+
"payload.positionTargetId must not reference the moved sound item",
|
|
14435
15700
|
);
|
|
14436
15701
|
}
|
|
14437
15702
|
|
|
14438
|
-
if (!isPlainObject(state.
|
|
15703
|
+
if (!isPlainObject(state.sounds.items[payload.positionTargetId])) {
|
|
14439
15704
|
return invalidPrecondition(
|
|
14440
|
-
"payload.positionTargetId must reference an existing
|
|
15705
|
+
"payload.positionTargetId must reference an existing sound item",
|
|
14441
15706
|
);
|
|
14442
15707
|
}
|
|
14443
15708
|
|
|
14444
15709
|
const targetParentId = getNodeParentId({
|
|
14445
|
-
tree: state.
|
|
15710
|
+
tree: state.sounds.tree,
|
|
14446
15711
|
nodeId: payload.positionTargetId,
|
|
14447
15712
|
});
|
|
14448
15713
|
|
|
@@ -14454,18 +15719,18 @@ const COMMAND_DEFINITIONS = [
|
|
|
14454
15719
|
}
|
|
14455
15720
|
},
|
|
14456
15721
|
reduce: ({ state, payload }) => {
|
|
14457
|
-
const
|
|
14458
|
-
tree: state.
|
|
14459
|
-
nodeId: payload.
|
|
14460
|
-
errorMessage: "
|
|
15722
|
+
const soundNodeResult = removeNodeOrResult({
|
|
15723
|
+
tree: state.sounds.tree,
|
|
15724
|
+
nodeId: payload.soundId,
|
|
15725
|
+
errorMessage: "sound move target missing from tree",
|
|
14461
15726
|
});
|
|
14462
|
-
if (!
|
|
14463
|
-
return
|
|
15727
|
+
if (!soundNodeResult.valid) {
|
|
15728
|
+
return soundNodeResult;
|
|
14464
15729
|
}
|
|
14465
15730
|
|
|
14466
15731
|
insertTreeNode({
|
|
14467
|
-
tree: state.
|
|
14468
|
-
node:
|
|
15732
|
+
tree: state.sounds.tree,
|
|
15733
|
+
node: soundNodeResult.node,
|
|
14469
15734
|
parentId: payload.parentId ?? null,
|
|
14470
15735
|
index: payload.index,
|
|
14471
15736
|
position: payload.position,
|
|
@@ -14476,13 +15741,13 @@ const COMMAND_DEFINITIONS = [
|
|
|
14476
15741
|
},
|
|
14477
15742
|
},
|
|
14478
15743
|
{
|
|
14479
|
-
type: "
|
|
15744
|
+
type: "voice.create",
|
|
14480
15745
|
validatePayload: ({ payload }) => {
|
|
14481
15746
|
{
|
|
14482
15747
|
const result = validateAllowedKeys({
|
|
14483
15748
|
value: payload,
|
|
14484
15749
|
allowedKeys: [
|
|
14485
|
-
"
|
|
15750
|
+
"voiceId",
|
|
14486
15751
|
"parentId",
|
|
14487
15752
|
"data",
|
|
14488
15753
|
"index",
|
|
@@ -14497,8 +15762,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14497
15762
|
}
|
|
14498
15763
|
}
|
|
14499
15764
|
|
|
14500
|
-
if (!isNonEmptyString(payload.
|
|
14501
|
-
return invalidPayload("payload.
|
|
15765
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
15766
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
14502
15767
|
}
|
|
14503
15768
|
|
|
14504
15769
|
if (
|
|
@@ -14512,7 +15777,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
14512
15777
|
}
|
|
14513
15778
|
|
|
14514
15779
|
{
|
|
14515
|
-
const result =
|
|
15780
|
+
const result = validateVoiceCreateData({
|
|
14516
15781
|
data: payload.data,
|
|
14517
15782
|
errorFactory: createPayloadValidationError,
|
|
14518
15783
|
});
|
|
@@ -14532,35 +15797,35 @@ const COMMAND_DEFINITIONS = [
|
|
|
14532
15797
|
}
|
|
14533
15798
|
},
|
|
14534
15799
|
validateAgainstState: ({ state, payload }) => {
|
|
14535
|
-
if (isPlainObject(state.
|
|
14536
|
-
return invalidPrecondition("payload.
|
|
15800
|
+
if (isPlainObject(state.voices.items[payload.voiceId])) {
|
|
15801
|
+
return invalidPrecondition("payload.voiceId must not already exist");
|
|
14537
15802
|
}
|
|
14538
15803
|
|
|
14539
15804
|
const parentId = payload.parentId ?? null;
|
|
14540
15805
|
if (parentId !== null) {
|
|
14541
|
-
const
|
|
14542
|
-
if (!isPlainObject(
|
|
15806
|
+
const parentVoice = state.voices.items[parentId];
|
|
15807
|
+
if (!isPlainObject(parentVoice)) {
|
|
14543
15808
|
return invalidPrecondition(
|
|
14544
|
-
"payload.parentId must reference an existing
|
|
15809
|
+
"payload.parentId must reference an existing voice item",
|
|
14545
15810
|
);
|
|
14546
15811
|
}
|
|
14547
15812
|
|
|
14548
|
-
if (
|
|
15813
|
+
if (parentVoice.type !== "folder") {
|
|
14549
15814
|
return invalidPrecondition(
|
|
14550
|
-
"payload.parentId must reference a folder
|
|
15815
|
+
"payload.parentId must reference a folder voice item",
|
|
14551
15816
|
);
|
|
14552
15817
|
}
|
|
14553
15818
|
}
|
|
14554
15819
|
|
|
14555
15820
|
if (payload.positionTargetId !== undefined) {
|
|
14556
|
-
if (!isPlainObject(state.
|
|
15821
|
+
if (!isPlainObject(state.voices.items[payload.positionTargetId])) {
|
|
14557
15822
|
return invalidPrecondition(
|
|
14558
|
-
"payload.positionTargetId must reference an existing
|
|
15823
|
+
"payload.positionTargetId must reference an existing voice item",
|
|
14559
15824
|
);
|
|
14560
15825
|
}
|
|
14561
15826
|
|
|
14562
15827
|
const targetParentId = getNodeParentId({
|
|
14563
|
-
tree: state.
|
|
15828
|
+
tree: state.voices.tree,
|
|
14564
15829
|
nodeId: payload.positionTargetId,
|
|
14565
15830
|
});
|
|
14566
15831
|
|
|
@@ -14571,63 +15836,53 @@ const COMMAND_DEFINITIONS = [
|
|
|
14571
15836
|
}
|
|
14572
15837
|
}
|
|
14573
15838
|
|
|
14574
|
-
if (payload.data.type === "
|
|
14575
|
-
const
|
|
15839
|
+
if (payload.data.type === "voice") {
|
|
15840
|
+
const scene = state.scenes.items[payload.data.sceneId];
|
|
15841
|
+
if (!isPlainObject(scene) || scene.type === "folder") {
|
|
15842
|
+
return invalidPrecondition(
|
|
15843
|
+
"payload.data.sceneId must reference an existing non-folder scene",
|
|
15844
|
+
);
|
|
15845
|
+
}
|
|
15846
|
+
|
|
15847
|
+
return validateReferencedFilesInData({
|
|
14576
15848
|
state,
|
|
14577
15849
|
data: payload.data,
|
|
14578
15850
|
fields: ["fileId", "waveformDataFileId"],
|
|
14579
15851
|
nullableFields: ["waveformDataFileId"],
|
|
14580
15852
|
details: {
|
|
14581
|
-
|
|
14582
|
-
},
|
|
14583
|
-
});
|
|
14584
|
-
if (!result.valid) {
|
|
14585
|
-
return result;
|
|
14586
|
-
}
|
|
14587
|
-
|
|
14588
|
-
return validateTagIdsAgainstScope({
|
|
14589
|
-
state,
|
|
14590
|
-
tagIds: payload.data.tagIds,
|
|
14591
|
-
scopeKey: "sounds",
|
|
14592
|
-
path: "payload.data.tagIds",
|
|
14593
|
-
details: {
|
|
14594
|
-
soundId: payload.soundId,
|
|
15853
|
+
voiceId: payload.voiceId,
|
|
14595
15854
|
},
|
|
14596
15855
|
});
|
|
14597
15856
|
}
|
|
14598
15857
|
},
|
|
14599
15858
|
reduce: ({ state, payload }) => {
|
|
14600
|
-
const
|
|
14601
|
-
id: payload.
|
|
15859
|
+
const nextVoice = {
|
|
15860
|
+
id: payload.voiceId,
|
|
14602
15861
|
type: payload.data.type,
|
|
14603
15862
|
name: payload.data.name,
|
|
14604
15863
|
};
|
|
14605
15864
|
|
|
14606
15865
|
if (payload.data.description !== undefined) {
|
|
14607
|
-
|
|
15866
|
+
nextVoice.description = payload.data.description;
|
|
14608
15867
|
}
|
|
14609
15868
|
|
|
14610
|
-
if (payload.data.type === "
|
|
14611
|
-
|
|
15869
|
+
if (payload.data.type === "voice") {
|
|
15870
|
+
nextVoice.sceneId = payload.data.sceneId;
|
|
15871
|
+
nextVoice.fileId = payload.data.fileId;
|
|
14612
15872
|
if (payload.data.waveformDataFileId !== undefined) {
|
|
14613
|
-
|
|
15873
|
+
nextVoice.waveformDataFileId = payload.data.waveformDataFileId;
|
|
14614
15874
|
}
|
|
14615
15875
|
if (payload.data.duration !== undefined) {
|
|
14616
|
-
|
|
15876
|
+
nextVoice.duration = payload.data.duration;
|
|
14617
15877
|
}
|
|
14618
|
-
|
|
14619
|
-
assignOptionalTagIds({
|
|
14620
|
-
target: nextSound,
|
|
14621
|
-
tagIds: payload.data.tagIds,
|
|
14622
|
-
});
|
|
14623
15878
|
}
|
|
14624
15879
|
|
|
14625
|
-
state.
|
|
15880
|
+
state.voices.items[payload.voiceId] = nextVoice;
|
|
14626
15881
|
|
|
14627
15882
|
insertTreeNode({
|
|
14628
|
-
tree: state.
|
|
15883
|
+
tree: state.voices.tree,
|
|
14629
15884
|
node: {
|
|
14630
|
-
id: payload.
|
|
15885
|
+
id: payload.voiceId,
|
|
14631
15886
|
children: [],
|
|
14632
15887
|
},
|
|
14633
15888
|
parentId: payload.parentId ?? null,
|
|
@@ -14640,12 +15895,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
14640
15895
|
},
|
|
14641
15896
|
},
|
|
14642
15897
|
{
|
|
14643
|
-
type: "
|
|
15898
|
+
type: "voice.update",
|
|
14644
15899
|
validatePayload: ({ payload }) => {
|
|
14645
15900
|
{
|
|
14646
15901
|
const result = validateExactKeys({
|
|
14647
15902
|
value: payload,
|
|
14648
|
-
expectedKeys: ["
|
|
15903
|
+
expectedKeys: ["voiceId", "data"],
|
|
14649
15904
|
path: "payload",
|
|
14650
15905
|
errorFactory: createPayloadValidationError,
|
|
14651
15906
|
});
|
|
@@ -14654,12 +15909,12 @@ const COMMAND_DEFINITIONS = [
|
|
|
14654
15909
|
}
|
|
14655
15910
|
}
|
|
14656
15911
|
|
|
14657
|
-
if (!isNonEmptyString(payload.
|
|
14658
|
-
return invalidPayload("payload.
|
|
15912
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
15913
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
14659
15914
|
}
|
|
14660
15915
|
|
|
14661
15916
|
{
|
|
14662
|
-
const result =
|
|
15917
|
+
const result = validateVoiceUpdateData({
|
|
14663
15918
|
data: payload.data,
|
|
14664
15919
|
errorFactory: createPayloadValidationError,
|
|
14665
15920
|
});
|
|
@@ -14669,66 +15924,61 @@ const COMMAND_DEFINITIONS = [
|
|
|
14669
15924
|
}
|
|
14670
15925
|
},
|
|
14671
15926
|
validateAgainstState: ({ state, payload }) => {
|
|
14672
|
-
const
|
|
14673
|
-
if (!isPlainObject(
|
|
15927
|
+
const currentVoice = state.voices.items[payload.voiceId];
|
|
15928
|
+
if (!isPlainObject(currentVoice)) {
|
|
14674
15929
|
return invalidPrecondition(
|
|
14675
|
-
"payload.
|
|
15930
|
+
"payload.voiceId must reference an existing voice item",
|
|
14676
15931
|
);
|
|
14677
15932
|
}
|
|
14678
15933
|
|
|
14679
15934
|
if (
|
|
14680
|
-
|
|
14681
|
-
(payload.data.
|
|
15935
|
+
currentVoice.type === "folder" &&
|
|
15936
|
+
(payload.data.sceneId !== undefined ||
|
|
15937
|
+
payload.data.fileId !== undefined ||
|
|
14682
15938
|
payload.data.waveformDataFileId !== undefined ||
|
|
14683
|
-
payload.data.duration !== undefined
|
|
14684
|
-
payload.data.tagIds !== undefined)
|
|
15939
|
+
payload.data.duration !== undefined)
|
|
14685
15940
|
) {
|
|
14686
15941
|
return invalidPrecondition(
|
|
14687
|
-
"folder
|
|
15942
|
+
"folder voice items cannot update file fields",
|
|
14688
15943
|
);
|
|
14689
15944
|
}
|
|
14690
15945
|
|
|
14691
|
-
if (
|
|
14692
|
-
|
|
15946
|
+
if (currentVoice.type === "voice") {
|
|
15947
|
+
if (payload.data.sceneId !== undefined) {
|
|
15948
|
+
const scene = state.scenes.items[payload.data.sceneId];
|
|
15949
|
+
if (!isPlainObject(scene) || scene.type === "folder") {
|
|
15950
|
+
return invalidPrecondition(
|
|
15951
|
+
"payload.data.sceneId must reference an existing non-folder scene",
|
|
15952
|
+
);
|
|
15953
|
+
}
|
|
15954
|
+
}
|
|
15955
|
+
|
|
15956
|
+
return validateReferencedFilesInData({
|
|
14693
15957
|
state,
|
|
14694
15958
|
data: payload.data,
|
|
14695
15959
|
fields: ["fileId", "waveformDataFileId"],
|
|
14696
15960
|
nullableFields: ["waveformDataFileId"],
|
|
14697
15961
|
details: {
|
|
14698
|
-
|
|
14699
|
-
},
|
|
14700
|
-
});
|
|
14701
|
-
if (!result.valid) {
|
|
14702
|
-
return result;
|
|
14703
|
-
}
|
|
14704
|
-
|
|
14705
|
-
return validateTagIdsAgainstScope({
|
|
14706
|
-
state,
|
|
14707
|
-
tagIds: payload.data.tagIds,
|
|
14708
|
-
scopeKey: "sounds",
|
|
14709
|
-
path: "payload.data.tagIds",
|
|
14710
|
-
details: {
|
|
14711
|
-
soundId: payload.soundId,
|
|
15962
|
+
voiceId: payload.voiceId,
|
|
14712
15963
|
},
|
|
14713
15964
|
});
|
|
14714
15965
|
}
|
|
14715
15966
|
},
|
|
14716
15967
|
reduce: ({ state, payload }) => {
|
|
14717
|
-
|
|
14718
|
-
|
|
14719
|
-
|
|
14720
|
-
|
|
14721
|
-
});
|
|
15968
|
+
state.voices.items[payload.voiceId] = {
|
|
15969
|
+
...structuredClone(state.voices.items[payload.voiceId]),
|
|
15970
|
+
...structuredClone(payload.data),
|
|
15971
|
+
};
|
|
14722
15972
|
return state;
|
|
14723
15973
|
},
|
|
14724
15974
|
},
|
|
14725
15975
|
{
|
|
14726
|
-
type: "
|
|
15976
|
+
type: "voice.delete",
|
|
14727
15977
|
validatePayload: ({ payload }) => {
|
|
14728
15978
|
{
|
|
14729
15979
|
const result = validateExactKeys({
|
|
14730
15980
|
value: payload,
|
|
14731
|
-
expectedKeys: ["
|
|
15981
|
+
expectedKeys: ["voiceIds"],
|
|
14732
15982
|
path: "payload",
|
|
14733
15983
|
errorFactory: createPayloadValidationError,
|
|
14734
15984
|
});
|
|
@@ -14739,8 +15989,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14739
15989
|
|
|
14740
15990
|
{
|
|
14741
15991
|
const result = validateRequiredUniqueIdArray({
|
|
14742
|
-
value: payload.
|
|
14743
|
-
path: "payload.
|
|
15992
|
+
value: payload.voiceIds,
|
|
15993
|
+
path: "payload.voiceIds",
|
|
14744
15994
|
errorFactory: createPayloadValidationError,
|
|
14745
15995
|
});
|
|
14746
15996
|
if (result?.valid === false) {
|
|
@@ -14749,22 +15999,22 @@ const COMMAND_DEFINITIONS = [
|
|
|
14749
15999
|
}
|
|
14750
16000
|
},
|
|
14751
16001
|
validateAgainstState: ({ state, payload }) => {
|
|
14752
|
-
for (const
|
|
14753
|
-
if (!isPlainObject(state.
|
|
16002
|
+
for (const voiceId of payload.voiceIds) {
|
|
16003
|
+
if (!isPlainObject(state.voices.items[voiceId])) {
|
|
14754
16004
|
return invalidPrecondition(
|
|
14755
|
-
"payload.
|
|
14756
|
-
{
|
|
16005
|
+
"payload.voiceIds must reference existing voice items",
|
|
16006
|
+
{ voiceId },
|
|
14757
16007
|
);
|
|
14758
16008
|
}
|
|
14759
16009
|
}
|
|
14760
16010
|
},
|
|
14761
16011
|
reduce: ({ state, payload }) => {
|
|
14762
|
-
const
|
|
16012
|
+
const deletedVoiceIds = new Set();
|
|
14763
16013
|
|
|
14764
|
-
for (const
|
|
16014
|
+
for (const voiceId of payload.voiceIds) {
|
|
14765
16015
|
const removedNode = removeTreeNode({
|
|
14766
|
-
nodes: state.
|
|
14767
|
-
nodeId:
|
|
16016
|
+
nodes: state.voices.tree,
|
|
16017
|
+
nodeId: voiceId,
|
|
14768
16018
|
});
|
|
14769
16019
|
|
|
14770
16020
|
if (!removedNode) {
|
|
@@ -14772,25 +16022,25 @@ const COMMAND_DEFINITIONS = [
|
|
|
14772
16022
|
}
|
|
14773
16023
|
|
|
14774
16024
|
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
14775
|
-
|
|
16025
|
+
deletedVoiceIds.add(id);
|
|
14776
16026
|
}
|
|
14777
16027
|
}
|
|
14778
16028
|
|
|
14779
|
-
for (const
|
|
14780
|
-
delete state.
|
|
16029
|
+
for (const voiceId of deletedVoiceIds) {
|
|
16030
|
+
delete state.voices.items[voiceId];
|
|
14781
16031
|
}
|
|
14782
16032
|
|
|
14783
16033
|
return state;
|
|
14784
16034
|
},
|
|
14785
16035
|
},
|
|
14786
16036
|
{
|
|
14787
|
-
type: "
|
|
16037
|
+
type: "voice.move",
|
|
14788
16038
|
validatePayload: ({ payload }) => {
|
|
14789
16039
|
{
|
|
14790
16040
|
const result = validateAllowedKeys({
|
|
14791
16041
|
value: payload,
|
|
14792
16042
|
allowedKeys: [
|
|
14793
|
-
"
|
|
16043
|
+
"voiceId",
|
|
14794
16044
|
"parentId",
|
|
14795
16045
|
"index",
|
|
14796
16046
|
"position",
|
|
@@ -14804,8 +16054,8 @@ const COMMAND_DEFINITIONS = [
|
|
|
14804
16054
|
}
|
|
14805
16055
|
}
|
|
14806
16056
|
|
|
14807
|
-
if (!isNonEmptyString(payload.
|
|
14808
|
-
return invalidPayload("payload.
|
|
16057
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
16058
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
14809
16059
|
}
|
|
14810
16060
|
|
|
14811
16061
|
if (
|
|
@@ -14829,60 +16079,60 @@ const COMMAND_DEFINITIONS = [
|
|
|
14829
16079
|
}
|
|
14830
16080
|
},
|
|
14831
16081
|
validateAgainstState: ({ state, payload }) => {
|
|
14832
|
-
const
|
|
14833
|
-
if (!isPlainObject(
|
|
16082
|
+
const voice = state.voices.items[payload.voiceId];
|
|
16083
|
+
if (!isPlainObject(voice)) {
|
|
14834
16084
|
return invalidPrecondition(
|
|
14835
|
-
"payload.
|
|
16085
|
+
"payload.voiceId must reference an existing voice item",
|
|
14836
16086
|
);
|
|
14837
16087
|
}
|
|
14838
16088
|
|
|
14839
|
-
const
|
|
14840
|
-
nodes: state.
|
|
14841
|
-
nodeId: payload.
|
|
16089
|
+
const voiceNode = findTreeNode({
|
|
16090
|
+
nodes: state.voices.tree,
|
|
16091
|
+
nodeId: payload.voiceId,
|
|
14842
16092
|
});
|
|
14843
16093
|
|
|
14844
16094
|
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
14845
|
-
const
|
|
14846
|
-
if (!isPlainObject(
|
|
16095
|
+
const parentVoice = state.voices.items[payload.parentId];
|
|
16096
|
+
if (!isPlainObject(parentVoice)) {
|
|
14847
16097
|
return invalidPrecondition(
|
|
14848
|
-
"payload.parentId must reference an existing
|
|
16098
|
+
"payload.parentId must reference an existing voice item",
|
|
14849
16099
|
);
|
|
14850
16100
|
}
|
|
14851
16101
|
|
|
14852
|
-
if (
|
|
16102
|
+
if (parentVoice.type !== "folder") {
|
|
14853
16103
|
return invalidPrecondition(
|
|
14854
|
-
"payload.parentId must reference a folder
|
|
16104
|
+
"payload.parentId must reference a folder voice item",
|
|
14855
16105
|
);
|
|
14856
16106
|
}
|
|
14857
16107
|
|
|
14858
16108
|
const descendantIds = new Set(
|
|
14859
16109
|
collectTreeDescendantIds({
|
|
14860
|
-
node:
|
|
16110
|
+
node: voiceNode,
|
|
14861
16111
|
}),
|
|
14862
16112
|
);
|
|
14863
16113
|
|
|
14864
16114
|
if (descendantIds.has(payload.parentId)) {
|
|
14865
16115
|
return invalidPrecondition(
|
|
14866
|
-
"payload.parentId must not target the moved
|
|
16116
|
+
"payload.parentId must not target the moved voice item or its descendants",
|
|
14867
16117
|
);
|
|
14868
16118
|
}
|
|
14869
16119
|
}
|
|
14870
16120
|
|
|
14871
16121
|
if (payload.positionTargetId !== undefined) {
|
|
14872
|
-
if (payload.positionTargetId === payload.
|
|
16122
|
+
if (payload.positionTargetId === payload.voiceId) {
|
|
14873
16123
|
return invalidPrecondition(
|
|
14874
|
-
"payload.positionTargetId must not reference the moved
|
|
16124
|
+
"payload.positionTargetId must not reference the moved voice item",
|
|
14875
16125
|
);
|
|
14876
16126
|
}
|
|
14877
16127
|
|
|
14878
|
-
if (!isPlainObject(state.
|
|
16128
|
+
if (!isPlainObject(state.voices.items[payload.positionTargetId])) {
|
|
14879
16129
|
return invalidPrecondition(
|
|
14880
|
-
"payload.positionTargetId must reference an existing
|
|
16130
|
+
"payload.positionTargetId must reference an existing voice item",
|
|
14881
16131
|
);
|
|
14882
16132
|
}
|
|
14883
16133
|
|
|
14884
16134
|
const targetParentId = getNodeParentId({
|
|
14885
|
-
tree: state.
|
|
16135
|
+
tree: state.voices.tree,
|
|
14886
16136
|
nodeId: payload.positionTargetId,
|
|
14887
16137
|
});
|
|
14888
16138
|
|
|
@@ -14894,18 +16144,18 @@ const COMMAND_DEFINITIONS = [
|
|
|
14894
16144
|
}
|
|
14895
16145
|
},
|
|
14896
16146
|
reduce: ({ state, payload }) => {
|
|
14897
|
-
const
|
|
14898
|
-
tree: state.
|
|
14899
|
-
nodeId: payload.
|
|
14900
|
-
errorMessage: "
|
|
16147
|
+
const voiceNodeResult = removeNodeOrResult({
|
|
16148
|
+
tree: state.voices.tree,
|
|
16149
|
+
nodeId: payload.voiceId,
|
|
16150
|
+
errorMessage: "voice move target missing from tree",
|
|
14901
16151
|
});
|
|
14902
|
-
if (!
|
|
14903
|
-
return
|
|
16152
|
+
if (!voiceNodeResult.valid) {
|
|
16153
|
+
return voiceNodeResult;
|
|
14904
16154
|
}
|
|
14905
16155
|
|
|
14906
16156
|
insertTreeNode({
|
|
14907
|
-
tree: state.
|
|
14908
|
-
node:
|
|
16157
|
+
tree: state.voices.tree,
|
|
16158
|
+
node: voiceNodeResult.node,
|
|
14909
16159
|
parentId: payload.parentId ?? null,
|
|
14910
16160
|
index: payload.index,
|
|
14911
16161
|
position: payload.position,
|
|
@@ -17736,6 +18986,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
17736
18986
|
"payload.parentId must reference a folder sprite item",
|
|
17737
18987
|
);
|
|
17738
18988
|
}
|
|
18989
|
+
|
|
18990
|
+
const parentNode = findTreeNode({
|
|
18991
|
+
nodes: collection.tree,
|
|
18992
|
+
nodeId: parentId,
|
|
18993
|
+
});
|
|
18994
|
+
if (!parentNode) {
|
|
18995
|
+
return invalidPrecondition(
|
|
18996
|
+
"payload.parentId must reference a folder sprite item in the tree",
|
|
18997
|
+
);
|
|
18998
|
+
}
|
|
17739
18999
|
}
|
|
17740
19000
|
|
|
17741
19001
|
if (payload.positionTargetId !== undefined) {
|
|
@@ -17745,6 +19005,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
17745
19005
|
);
|
|
17746
19006
|
}
|
|
17747
19007
|
|
|
19008
|
+
const targetNode = findTreeNode({
|
|
19009
|
+
nodes: collection.tree,
|
|
19010
|
+
nodeId: payload.positionTargetId,
|
|
19011
|
+
});
|
|
19012
|
+
if (!targetNode) {
|
|
19013
|
+
return invalidPrecondition(
|
|
19014
|
+
"payload.positionTargetId must reference an existing sprite item in the tree",
|
|
19015
|
+
);
|
|
19016
|
+
}
|
|
19017
|
+
|
|
17748
19018
|
const targetParentId = getNodeParentId({
|
|
17749
19019
|
tree: collection.tree,
|
|
17750
19020
|
nodeId: payload.positionTargetId,
|
|
@@ -18076,6 +19346,11 @@ const COMMAND_DEFINITIONS = [
|
|
|
18076
19346
|
nodes: collection.tree,
|
|
18077
19347
|
nodeId: payload.spriteId,
|
|
18078
19348
|
});
|
|
19349
|
+
if (!currentNode) {
|
|
19350
|
+
return invalidPrecondition(
|
|
19351
|
+
"payload.spriteId must reference an existing sprite item in the tree",
|
|
19352
|
+
);
|
|
19353
|
+
}
|
|
18079
19354
|
|
|
18080
19355
|
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
18081
19356
|
const parentItem = collection.items[payload.parentId];
|
|
@@ -18085,6 +19360,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
18085
19360
|
);
|
|
18086
19361
|
}
|
|
18087
19362
|
|
|
19363
|
+
const parentNode = findTreeNode({
|
|
19364
|
+
nodes: collection.tree,
|
|
19365
|
+
nodeId: payload.parentId,
|
|
19366
|
+
});
|
|
19367
|
+
if (!parentNode) {
|
|
19368
|
+
return invalidPrecondition(
|
|
19369
|
+
"payload.parentId must reference a folder sprite item in the tree",
|
|
19370
|
+
);
|
|
19371
|
+
}
|
|
19372
|
+
|
|
18088
19373
|
const descendantIds = new Set(
|
|
18089
19374
|
collectTreeDescendantIds({
|
|
18090
19375
|
node: currentNode,
|
|
@@ -18111,6 +19396,16 @@ const COMMAND_DEFINITIONS = [
|
|
|
18111
19396
|
);
|
|
18112
19397
|
}
|
|
18113
19398
|
|
|
19399
|
+
const targetNode = findTreeNode({
|
|
19400
|
+
nodes: collection.tree,
|
|
19401
|
+
nodeId: payload.positionTargetId,
|
|
19402
|
+
});
|
|
19403
|
+
if (!targetNode) {
|
|
19404
|
+
return invalidPrecondition(
|
|
19405
|
+
"payload.positionTargetId must reference an existing sprite item in the tree",
|
|
19406
|
+
);
|
|
19407
|
+
}
|
|
19408
|
+
|
|
18114
19409
|
const targetParentId = getNodeParentId({
|
|
18115
19410
|
tree: collection.tree,
|
|
18116
19411
|
nodeId: payload.positionTargetId,
|