@routevn/creator-model 1.8.0 → 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 +860 -79
package/package.json
CHANGED
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;
|
|
@@ -1443,6 +1445,105 @@ const validateSoundItems = ({ items, path, errorFactory }) => {
|
|
|
1443
1445
|
}
|
|
1444
1446
|
};
|
|
1445
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
|
+
|
|
1446
1547
|
const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
1447
1548
|
for (const [itemId, item] of Object.entries(items)) {
|
|
1448
1549
|
const itemPath = `${path}.${itemId}`;
|
|
@@ -6411,6 +6512,17 @@ const validateCollection = ({ collection, path }) => {
|
|
|
6411
6512
|
return result;
|
|
6412
6513
|
}
|
|
6413
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
|
+
}
|
|
6414
6526
|
} else if (path === "state.videos") {
|
|
6415
6527
|
{
|
|
6416
6528
|
const result = validateVideoItems({
|
|
@@ -6596,6 +6708,18 @@ const validateCollection = ({ collection, path }) => {
|
|
|
6596
6708
|
return result;
|
|
6597
6709
|
}
|
|
6598
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
|
+
}
|
|
6599
6723
|
} else if (path === "state.videos") {
|
|
6600
6724
|
{
|
|
6601
6725
|
const result = validateVideoTreeFolderOwnership({
|
|
@@ -7116,6 +7240,55 @@ export const assertInvariants = ({ state }) => {
|
|
|
7116
7240
|
}
|
|
7117
7241
|
}
|
|
7118
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
|
+
|
|
7119
7292
|
for (const [videoId, video] of Object.entries(state.videos.items)) {
|
|
7120
7293
|
if (video.type !== "video") {
|
|
7121
7294
|
continue;
|
|
@@ -9274,7 +9447,7 @@ const validateSoundUpdateData = ({ data, errorFactory }) => {
|
|
|
9274
9447
|
}
|
|
9275
9448
|
};
|
|
9276
9449
|
|
|
9277
|
-
const
|
|
9450
|
+
const validateVoiceCreateData = ({ data, errorFactory }) => {
|
|
9278
9451
|
if (!isPlainObject(data)) {
|
|
9279
9452
|
return invalidFromErrorFactory(
|
|
9280
9453
|
errorFactory,
|
|
@@ -9282,10 +9455,10 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
9282
9455
|
);
|
|
9283
9456
|
}
|
|
9284
9457
|
|
|
9285
|
-
if (data.type !== "folder" && data.type !== "
|
|
9458
|
+
if (data.type !== "folder" && data.type !== "voice") {
|
|
9286
9459
|
return invalidFromErrorFactory(
|
|
9287
9460
|
errorFactory,
|
|
9288
|
-
"payload.data.type must be 'folder' or '
|
|
9461
|
+
"payload.data.type must be 'folder' or 'voice'",
|
|
9289
9462
|
);
|
|
9290
9463
|
}
|
|
9291
9464
|
|
|
@@ -9299,12 +9472,10 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
9299
9472
|
"type",
|
|
9300
9473
|
"name",
|
|
9301
9474
|
"description",
|
|
9302
|
-
"
|
|
9475
|
+
"sceneId",
|
|
9303
9476
|
"fileId",
|
|
9304
|
-
"
|
|
9477
|
+
"waveformDataFileId",
|
|
9305
9478
|
"duration",
|
|
9306
|
-
"width",
|
|
9307
|
-
"height",
|
|
9308
9479
|
],
|
|
9309
9480
|
path: "payload.data",
|
|
9310
9481
|
errorFactory,
|
|
@@ -9328,16 +9499,12 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
9328
9499
|
);
|
|
9329
9500
|
}
|
|
9330
9501
|
|
|
9331
|
-
if (data.type === "
|
|
9332
|
-
{
|
|
9333
|
-
|
|
9334
|
-
value: data.tagIds,
|
|
9335
|
-
path: "payload.data.tagIds",
|
|
9502
|
+
if (data.type === "voice") {
|
|
9503
|
+
if (!isNonEmptyString(data.sceneId)) {
|
|
9504
|
+
return invalidFromErrorFactory(
|
|
9336
9505
|
errorFactory,
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
return result;
|
|
9340
|
-
}
|
|
9506
|
+
"payload.data.sceneId must be a non-empty string",
|
|
9507
|
+
);
|
|
9341
9508
|
}
|
|
9342
9509
|
|
|
9343
9510
|
if (!isNonEmptyString(data.fileId)) {
|
|
@@ -9347,10 +9514,14 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
9347
9514
|
);
|
|
9348
9515
|
}
|
|
9349
9516
|
|
|
9350
|
-
if (
|
|
9517
|
+
if (
|
|
9518
|
+
data.waveformDataFileId !== undefined &&
|
|
9519
|
+
data.waveformDataFileId !== null &&
|
|
9520
|
+
!isNonEmptyString(data.waveformDataFileId)
|
|
9521
|
+
) {
|
|
9351
9522
|
return invalidFromErrorFactory(
|
|
9352
9523
|
errorFactory,
|
|
9353
|
-
"payload.data.
|
|
9524
|
+
"payload.data.waveformDataFileId must be a non-empty string or null when provided",
|
|
9354
9525
|
);
|
|
9355
9526
|
}
|
|
9356
9527
|
|
|
@@ -9360,36 +9531,20 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
9360
9531
|
"payload.data.duration must be a finite number",
|
|
9361
9532
|
);
|
|
9362
9533
|
}
|
|
9363
|
-
|
|
9364
|
-
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
9365
|
-
return invalidFromErrorFactory(
|
|
9366
|
-
errorFactory,
|
|
9367
|
-
"payload.data.width must be a finite number",
|
|
9368
|
-
);
|
|
9369
|
-
}
|
|
9370
|
-
|
|
9371
|
-
if (data.height !== undefined && !isFiniteNumber(data.height)) {
|
|
9372
|
-
return invalidFromErrorFactory(
|
|
9373
|
-
errorFactory,
|
|
9374
|
-
"payload.data.height must be a finite number",
|
|
9375
|
-
);
|
|
9376
|
-
}
|
|
9377
9534
|
}
|
|
9378
9535
|
};
|
|
9379
9536
|
|
|
9380
|
-
const
|
|
9537
|
+
const validateVoiceUpdateData = ({ data, errorFactory }) => {
|
|
9381
9538
|
{
|
|
9382
9539
|
const result = validateAllowedKeys({
|
|
9383
9540
|
value: data,
|
|
9384
9541
|
allowedKeys: [
|
|
9385
9542
|
"name",
|
|
9386
9543
|
"description",
|
|
9387
|
-
"
|
|
9544
|
+
"sceneId",
|
|
9388
9545
|
"fileId",
|
|
9389
|
-
"
|
|
9546
|
+
"waveformDataFileId",
|
|
9390
9547
|
"duration",
|
|
9391
|
-
"width",
|
|
9392
|
-
"height",
|
|
9393
9548
|
],
|
|
9394
9549
|
path: "payload.data",
|
|
9395
9550
|
errorFactory,
|
|
@@ -9420,15 +9575,11 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
9420
9575
|
);
|
|
9421
9576
|
}
|
|
9422
9577
|
|
|
9423
|
-
{
|
|
9424
|
-
|
|
9425
|
-
value: data.tagIds,
|
|
9426
|
-
path: "payload.data.tagIds",
|
|
9578
|
+
if (data.sceneId !== undefined && !isNonEmptyString(data.sceneId)) {
|
|
9579
|
+
return invalidFromErrorFactory(
|
|
9427
9580
|
errorFactory,
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
return result;
|
|
9431
|
-
}
|
|
9581
|
+
"payload.data.sceneId must be a non-empty string when provided",
|
|
9582
|
+
);
|
|
9432
9583
|
}
|
|
9433
9584
|
|
|
9434
9585
|
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
@@ -9439,12 +9590,13 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
9439
9590
|
}
|
|
9440
9591
|
|
|
9441
9592
|
if (
|
|
9442
|
-
data.
|
|
9443
|
-
|
|
9593
|
+
data.waveformDataFileId !== undefined &&
|
|
9594
|
+
data.waveformDataFileId !== null &&
|
|
9595
|
+
!isNonEmptyString(data.waveformDataFileId)
|
|
9444
9596
|
) {
|
|
9445
9597
|
return invalidFromErrorFactory(
|
|
9446
9598
|
errorFactory,
|
|
9447
|
-
"payload.data.
|
|
9599
|
+
"payload.data.waveformDataFileId must be a non-empty string or null when provided",
|
|
9448
9600
|
);
|
|
9449
9601
|
}
|
|
9450
9602
|
|
|
@@ -9454,23 +9606,9 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
9454
9606
|
"payload.data.duration must be a finite number",
|
|
9455
9607
|
);
|
|
9456
9608
|
}
|
|
9457
|
-
|
|
9458
|
-
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
9459
|
-
return invalidFromErrorFactory(
|
|
9460
|
-
errorFactory,
|
|
9461
|
-
"payload.data.width must be a finite number",
|
|
9462
|
-
);
|
|
9463
|
-
}
|
|
9464
|
-
|
|
9465
|
-
if (data.height !== undefined && !isFiniteNumber(data.height)) {
|
|
9466
|
-
return invalidFromErrorFactory(
|
|
9467
|
-
errorFactory,
|
|
9468
|
-
"payload.data.height must be a finite number",
|
|
9469
|
-
);
|
|
9470
|
-
}
|
|
9471
9609
|
};
|
|
9472
9610
|
|
|
9473
|
-
const
|
|
9611
|
+
const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
9474
9612
|
if (!isPlainObject(data)) {
|
|
9475
9613
|
return invalidFromErrorFactory(
|
|
9476
9614
|
errorFactory,
|
|
@@ -9478,10 +9616,10 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9478
9616
|
);
|
|
9479
9617
|
}
|
|
9480
9618
|
|
|
9481
|
-
if (data.type !== "folder" && data.type !== "
|
|
9619
|
+
if (data.type !== "folder" && data.type !== "video") {
|
|
9482
9620
|
return invalidFromErrorFactory(
|
|
9483
9621
|
errorFactory,
|
|
9484
|
-
"payload.data.type must be 'folder' or '
|
|
9622
|
+
"payload.data.type must be 'folder' or 'video'",
|
|
9485
9623
|
);
|
|
9486
9624
|
}
|
|
9487
9625
|
|
|
@@ -9491,7 +9629,17 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9491
9629
|
allowedKeys:
|
|
9492
9630
|
data.type === "folder"
|
|
9493
9631
|
? ["type", "name", "description"]
|
|
9494
|
-
: [
|
|
9632
|
+
: [
|
|
9633
|
+
"type",
|
|
9634
|
+
"name",
|
|
9635
|
+
"description",
|
|
9636
|
+
"tagIds",
|
|
9637
|
+
"fileId",
|
|
9638
|
+
"thumbnailFileId",
|
|
9639
|
+
"duration",
|
|
9640
|
+
"width",
|
|
9641
|
+
"height",
|
|
9642
|
+
],
|
|
9495
9643
|
path: "payload.data",
|
|
9496
9644
|
errorFactory,
|
|
9497
9645
|
});
|
|
@@ -9514,7 +9662,7 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9514
9662
|
);
|
|
9515
9663
|
}
|
|
9516
9664
|
|
|
9517
|
-
if (data.type === "
|
|
9665
|
+
if (data.type === "video") {
|
|
9518
9666
|
{
|
|
9519
9667
|
const result = validateOptionalUniqueIdArray({
|
|
9520
9668
|
value: data.tagIds,
|
|
@@ -9533,23 +9681,209 @@ const validateFontCreateData = ({ data, errorFactory }) => {
|
|
|
9533
9681
|
);
|
|
9534
9682
|
}
|
|
9535
9683
|
|
|
9536
|
-
if (!isNonEmptyString(data.
|
|
9684
|
+
if (!isNonEmptyString(data.thumbnailFileId)) {
|
|
9537
9685
|
return invalidFromErrorFactory(
|
|
9538
9686
|
errorFactory,
|
|
9539
|
-
"payload.data.
|
|
9687
|
+
"payload.data.thumbnailFileId must be a non-empty string",
|
|
9540
9688
|
);
|
|
9541
9689
|
}
|
|
9542
|
-
}
|
|
9543
|
-
};
|
|
9544
9690
|
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9691
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
9692
|
+
return invalidFromErrorFactory(
|
|
9693
|
+
errorFactory,
|
|
9694
|
+
"payload.data.duration must be a finite number",
|
|
9695
|
+
);
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9698
|
+
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
9699
|
+
return invalidFromErrorFactory(
|
|
9700
|
+
errorFactory,
|
|
9701
|
+
"payload.data.width must be a finite number",
|
|
9702
|
+
);
|
|
9703
|
+
}
|
|
9704
|
+
|
|
9705
|
+
if (data.height !== undefined && !isFiniteNumber(data.height)) {
|
|
9706
|
+
return invalidFromErrorFactory(
|
|
9707
|
+
errorFactory,
|
|
9708
|
+
"payload.data.height must be a finite number",
|
|
9709
|
+
);
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9712
|
+
};
|
|
9713
|
+
|
|
9714
|
+
const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
9715
|
+
{
|
|
9716
|
+
const result = validateAllowedKeys({
|
|
9717
|
+
value: data,
|
|
9718
|
+
allowedKeys: [
|
|
9719
|
+
"name",
|
|
9720
|
+
"description",
|
|
9721
|
+
"tagIds",
|
|
9722
|
+
"fileId",
|
|
9723
|
+
"thumbnailFileId",
|
|
9724
|
+
"duration",
|
|
9725
|
+
"width",
|
|
9726
|
+
"height",
|
|
9727
|
+
],
|
|
9728
|
+
path: "payload.data",
|
|
9729
|
+
errorFactory,
|
|
9730
|
+
});
|
|
9731
|
+
if (result?.valid === false) {
|
|
9732
|
+
return result;
|
|
9733
|
+
}
|
|
9734
|
+
}
|
|
9735
|
+
|
|
9736
|
+
if (Object.keys(data).length === 0) {
|
|
9737
|
+
return invalidFromErrorFactory(
|
|
9738
|
+
errorFactory,
|
|
9739
|
+
"payload.data must include at least one updatable field",
|
|
9740
|
+
);
|
|
9741
|
+
}
|
|
9742
|
+
|
|
9743
|
+
if (data.name !== undefined && !isNonEmptyString(data.name)) {
|
|
9744
|
+
return invalidFromErrorFactory(
|
|
9745
|
+
errorFactory,
|
|
9746
|
+
"payload.data.name must be a non-empty string when provided",
|
|
9747
|
+
);
|
|
9748
|
+
}
|
|
9749
|
+
|
|
9750
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
9751
|
+
return invalidFromErrorFactory(
|
|
9752
|
+
errorFactory,
|
|
9753
|
+
"payload.data.description must be a string when provided",
|
|
9754
|
+
);
|
|
9755
|
+
}
|
|
9756
|
+
|
|
9757
|
+
{
|
|
9758
|
+
const result = validateOptionalUniqueIdArray({
|
|
9759
|
+
value: data.tagIds,
|
|
9760
|
+
path: "payload.data.tagIds",
|
|
9761
|
+
errorFactory,
|
|
9762
|
+
});
|
|
9763
|
+
if (result?.valid === false) {
|
|
9764
|
+
return result;
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9767
|
+
|
|
9768
|
+
if (data.fileId !== undefined && !isNonEmptyString(data.fileId)) {
|
|
9769
|
+
return invalidFromErrorFactory(
|
|
9770
|
+
errorFactory,
|
|
9771
|
+
"payload.data.fileId must be a non-empty string when provided",
|
|
9772
|
+
);
|
|
9773
|
+
}
|
|
9774
|
+
|
|
9775
|
+
if (
|
|
9776
|
+
data.thumbnailFileId !== undefined &&
|
|
9777
|
+
!isNonEmptyString(data.thumbnailFileId)
|
|
9778
|
+
) {
|
|
9779
|
+
return invalidFromErrorFactory(
|
|
9780
|
+
errorFactory,
|
|
9781
|
+
"payload.data.thumbnailFileId must be a non-empty string when provided",
|
|
9782
|
+
);
|
|
9783
|
+
}
|
|
9784
|
+
|
|
9785
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
9786
|
+
return invalidFromErrorFactory(
|
|
9787
|
+
errorFactory,
|
|
9788
|
+
"payload.data.duration must be a finite number",
|
|
9789
|
+
);
|
|
9790
|
+
}
|
|
9791
|
+
|
|
9792
|
+
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
9793
|
+
return invalidFromErrorFactory(
|
|
9794
|
+
errorFactory,
|
|
9795
|
+
"payload.data.width must be a finite number",
|
|
9796
|
+
);
|
|
9797
|
+
}
|
|
9798
|
+
|
|
9799
|
+
if (data.height !== undefined && !isFiniteNumber(data.height)) {
|
|
9800
|
+
return invalidFromErrorFactory(
|
|
9801
|
+
errorFactory,
|
|
9802
|
+
"payload.data.height must be a finite number",
|
|
9803
|
+
);
|
|
9804
|
+
}
|
|
9805
|
+
};
|
|
9806
|
+
|
|
9807
|
+
const validateFontCreateData = ({ data, errorFactory }) => {
|
|
9808
|
+
if (!isPlainObject(data)) {
|
|
9809
|
+
return invalidFromErrorFactory(
|
|
9810
|
+
errorFactory,
|
|
9811
|
+
"payload.data must be an object",
|
|
9812
|
+
);
|
|
9813
|
+
}
|
|
9814
|
+
|
|
9815
|
+
if (data.type !== "folder" && data.type !== "font") {
|
|
9816
|
+
return invalidFromErrorFactory(
|
|
9817
|
+
errorFactory,
|
|
9818
|
+
"payload.data.type must be 'folder' or 'font'",
|
|
9819
|
+
);
|
|
9820
|
+
}
|
|
9821
|
+
|
|
9822
|
+
{
|
|
9823
|
+
const result = validateAllowedKeys({
|
|
9824
|
+
value: data,
|
|
9825
|
+
allowedKeys:
|
|
9826
|
+
data.type === "folder"
|
|
9827
|
+
? ["type", "name", "description"]
|
|
9828
|
+
: ["type", "name", "description", "tagIds", "fileId", "fontFamily"],
|
|
9829
|
+
path: "payload.data",
|
|
9830
|
+
errorFactory,
|
|
9831
|
+
});
|
|
9832
|
+
if (result?.valid === false) {
|
|
9833
|
+
return result;
|
|
9834
|
+
}
|
|
9835
|
+
}
|
|
9836
|
+
|
|
9837
|
+
if (!isNonEmptyString(data.name)) {
|
|
9838
|
+
return invalidFromErrorFactory(
|
|
9839
|
+
errorFactory,
|
|
9840
|
+
"payload.data.name must be a non-empty string",
|
|
9841
|
+
);
|
|
9842
|
+
}
|
|
9843
|
+
|
|
9844
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
9845
|
+
return invalidFromErrorFactory(
|
|
9846
|
+
errorFactory,
|
|
9847
|
+
"payload.data.description must be a string when provided",
|
|
9848
|
+
);
|
|
9849
|
+
}
|
|
9850
|
+
|
|
9851
|
+
if (data.type === "font") {
|
|
9852
|
+
{
|
|
9853
|
+
const result = validateOptionalUniqueIdArray({
|
|
9854
|
+
value: data.tagIds,
|
|
9855
|
+
path: "payload.data.tagIds",
|
|
9856
|
+
errorFactory,
|
|
9857
|
+
});
|
|
9858
|
+
if (result?.valid === false) {
|
|
9859
|
+
return result;
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
|
|
9863
|
+
if (!isNonEmptyString(data.fileId)) {
|
|
9864
|
+
return invalidFromErrorFactory(
|
|
9865
|
+
errorFactory,
|
|
9866
|
+
"payload.data.fileId must be a non-empty string",
|
|
9867
|
+
);
|
|
9868
|
+
}
|
|
9869
|
+
|
|
9870
|
+
if (!isNonEmptyString(data.fontFamily)) {
|
|
9871
|
+
return invalidFromErrorFactory(
|
|
9872
|
+
errorFactory,
|
|
9873
|
+
"payload.data.fontFamily must be a non-empty string",
|
|
9874
|
+
);
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9877
|
+
};
|
|
9878
|
+
|
|
9879
|
+
const validateFontUpdateData = ({ data, errorFactory }) => {
|
|
9880
|
+
{
|
|
9881
|
+
const result = validateAllowedKeys({
|
|
9882
|
+
value: data,
|
|
9883
|
+
allowedKeys: ["name", "description", "tagIds", "fileId", "fontFamily"],
|
|
9884
|
+
path: "payload.data",
|
|
9885
|
+
errorFactory,
|
|
9886
|
+
});
|
|
9553
9887
|
if (result?.valid === false) {
|
|
9554
9888
|
return result;
|
|
9555
9889
|
}
|
|
@@ -12854,6 +13188,28 @@ const findReferencedFileUsage = ({ state, fileId }) => {
|
|
|
12854
13188
|
}
|
|
12855
13189
|
}
|
|
12856
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
|
+
|
|
12857
13213
|
for (const [videoId, video] of Object.entries(state.videos.items)) {
|
|
12858
13214
|
if (video.type !== "video") {
|
|
12859
13215
|
continue;
|
|
@@ -15384,6 +15740,431 @@ const COMMAND_DEFINITIONS = [
|
|
|
15384
15740
|
return state;
|
|
15385
15741
|
},
|
|
15386
15742
|
},
|
|
15743
|
+
{
|
|
15744
|
+
type: "voice.create",
|
|
15745
|
+
validatePayload: ({ payload }) => {
|
|
15746
|
+
{
|
|
15747
|
+
const result = validateAllowedKeys({
|
|
15748
|
+
value: payload,
|
|
15749
|
+
allowedKeys: [
|
|
15750
|
+
"voiceId",
|
|
15751
|
+
"parentId",
|
|
15752
|
+
"data",
|
|
15753
|
+
"index",
|
|
15754
|
+
"position",
|
|
15755
|
+
"positionTargetId",
|
|
15756
|
+
],
|
|
15757
|
+
path: "payload",
|
|
15758
|
+
errorFactory: createPayloadValidationError,
|
|
15759
|
+
});
|
|
15760
|
+
if (result?.valid === false) {
|
|
15761
|
+
return result;
|
|
15762
|
+
}
|
|
15763
|
+
}
|
|
15764
|
+
|
|
15765
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
15766
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
15767
|
+
}
|
|
15768
|
+
|
|
15769
|
+
if (
|
|
15770
|
+
payload.parentId !== undefined &&
|
|
15771
|
+
payload.parentId !== null &&
|
|
15772
|
+
!isNonEmptyString(payload.parentId)
|
|
15773
|
+
) {
|
|
15774
|
+
return invalidPayload(
|
|
15775
|
+
"payload.parentId must be a non-empty string when provided",
|
|
15776
|
+
);
|
|
15777
|
+
}
|
|
15778
|
+
|
|
15779
|
+
{
|
|
15780
|
+
const result = validateVoiceCreateData({
|
|
15781
|
+
data: payload.data,
|
|
15782
|
+
errorFactory: createPayloadValidationError,
|
|
15783
|
+
});
|
|
15784
|
+
if (result?.valid === false) {
|
|
15785
|
+
return result;
|
|
15786
|
+
}
|
|
15787
|
+
}
|
|
15788
|
+
|
|
15789
|
+
{
|
|
15790
|
+
const result = validatePlacementFields({
|
|
15791
|
+
payload,
|
|
15792
|
+
errorFactory: createPayloadValidationError,
|
|
15793
|
+
});
|
|
15794
|
+
if (result?.valid === false) {
|
|
15795
|
+
return result;
|
|
15796
|
+
}
|
|
15797
|
+
}
|
|
15798
|
+
},
|
|
15799
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15800
|
+
if (isPlainObject(state.voices.items[payload.voiceId])) {
|
|
15801
|
+
return invalidPrecondition("payload.voiceId must not already exist");
|
|
15802
|
+
}
|
|
15803
|
+
|
|
15804
|
+
const parentId = payload.parentId ?? null;
|
|
15805
|
+
if (parentId !== null) {
|
|
15806
|
+
const parentVoice = state.voices.items[parentId];
|
|
15807
|
+
if (!isPlainObject(parentVoice)) {
|
|
15808
|
+
return invalidPrecondition(
|
|
15809
|
+
"payload.parentId must reference an existing voice item",
|
|
15810
|
+
);
|
|
15811
|
+
}
|
|
15812
|
+
|
|
15813
|
+
if (parentVoice.type !== "folder") {
|
|
15814
|
+
return invalidPrecondition(
|
|
15815
|
+
"payload.parentId must reference a folder voice item",
|
|
15816
|
+
);
|
|
15817
|
+
}
|
|
15818
|
+
}
|
|
15819
|
+
|
|
15820
|
+
if (payload.positionTargetId !== undefined) {
|
|
15821
|
+
if (!isPlainObject(state.voices.items[payload.positionTargetId])) {
|
|
15822
|
+
return invalidPrecondition(
|
|
15823
|
+
"payload.positionTargetId must reference an existing voice item",
|
|
15824
|
+
);
|
|
15825
|
+
}
|
|
15826
|
+
|
|
15827
|
+
const targetParentId = getNodeParentId({
|
|
15828
|
+
tree: state.voices.tree,
|
|
15829
|
+
nodeId: payload.positionTargetId,
|
|
15830
|
+
});
|
|
15831
|
+
|
|
15832
|
+
if (targetParentId !== parentId) {
|
|
15833
|
+
return invalidPrecondition(
|
|
15834
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
15835
|
+
);
|
|
15836
|
+
}
|
|
15837
|
+
}
|
|
15838
|
+
|
|
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({
|
|
15848
|
+
state,
|
|
15849
|
+
data: payload.data,
|
|
15850
|
+
fields: ["fileId", "waveformDataFileId"],
|
|
15851
|
+
nullableFields: ["waveformDataFileId"],
|
|
15852
|
+
details: {
|
|
15853
|
+
voiceId: payload.voiceId,
|
|
15854
|
+
},
|
|
15855
|
+
});
|
|
15856
|
+
}
|
|
15857
|
+
},
|
|
15858
|
+
reduce: ({ state, payload }) => {
|
|
15859
|
+
const nextVoice = {
|
|
15860
|
+
id: payload.voiceId,
|
|
15861
|
+
type: payload.data.type,
|
|
15862
|
+
name: payload.data.name,
|
|
15863
|
+
};
|
|
15864
|
+
|
|
15865
|
+
if (payload.data.description !== undefined) {
|
|
15866
|
+
nextVoice.description = payload.data.description;
|
|
15867
|
+
}
|
|
15868
|
+
|
|
15869
|
+
if (payload.data.type === "voice") {
|
|
15870
|
+
nextVoice.sceneId = payload.data.sceneId;
|
|
15871
|
+
nextVoice.fileId = payload.data.fileId;
|
|
15872
|
+
if (payload.data.waveformDataFileId !== undefined) {
|
|
15873
|
+
nextVoice.waveformDataFileId = payload.data.waveformDataFileId;
|
|
15874
|
+
}
|
|
15875
|
+
if (payload.data.duration !== undefined) {
|
|
15876
|
+
nextVoice.duration = payload.data.duration;
|
|
15877
|
+
}
|
|
15878
|
+
}
|
|
15879
|
+
|
|
15880
|
+
state.voices.items[payload.voiceId] = nextVoice;
|
|
15881
|
+
|
|
15882
|
+
insertTreeNode({
|
|
15883
|
+
tree: state.voices.tree,
|
|
15884
|
+
node: {
|
|
15885
|
+
id: payload.voiceId,
|
|
15886
|
+
children: [],
|
|
15887
|
+
},
|
|
15888
|
+
parentId: payload.parentId ?? null,
|
|
15889
|
+
index: payload.index,
|
|
15890
|
+
position: payload.position,
|
|
15891
|
+
positionTargetId: payload.positionTargetId,
|
|
15892
|
+
});
|
|
15893
|
+
|
|
15894
|
+
return state;
|
|
15895
|
+
},
|
|
15896
|
+
},
|
|
15897
|
+
{
|
|
15898
|
+
type: "voice.update",
|
|
15899
|
+
validatePayload: ({ payload }) => {
|
|
15900
|
+
{
|
|
15901
|
+
const result = validateExactKeys({
|
|
15902
|
+
value: payload,
|
|
15903
|
+
expectedKeys: ["voiceId", "data"],
|
|
15904
|
+
path: "payload",
|
|
15905
|
+
errorFactory: createPayloadValidationError,
|
|
15906
|
+
});
|
|
15907
|
+
if (result?.valid === false) {
|
|
15908
|
+
return result;
|
|
15909
|
+
}
|
|
15910
|
+
}
|
|
15911
|
+
|
|
15912
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
15913
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
15914
|
+
}
|
|
15915
|
+
|
|
15916
|
+
{
|
|
15917
|
+
const result = validateVoiceUpdateData({
|
|
15918
|
+
data: payload.data,
|
|
15919
|
+
errorFactory: createPayloadValidationError,
|
|
15920
|
+
});
|
|
15921
|
+
if (result?.valid === false) {
|
|
15922
|
+
return result;
|
|
15923
|
+
}
|
|
15924
|
+
}
|
|
15925
|
+
},
|
|
15926
|
+
validateAgainstState: ({ state, payload }) => {
|
|
15927
|
+
const currentVoice = state.voices.items[payload.voiceId];
|
|
15928
|
+
if (!isPlainObject(currentVoice)) {
|
|
15929
|
+
return invalidPrecondition(
|
|
15930
|
+
"payload.voiceId must reference an existing voice item",
|
|
15931
|
+
);
|
|
15932
|
+
}
|
|
15933
|
+
|
|
15934
|
+
if (
|
|
15935
|
+
currentVoice.type === "folder" &&
|
|
15936
|
+
(payload.data.sceneId !== undefined ||
|
|
15937
|
+
payload.data.fileId !== undefined ||
|
|
15938
|
+
payload.data.waveformDataFileId !== undefined ||
|
|
15939
|
+
payload.data.duration !== undefined)
|
|
15940
|
+
) {
|
|
15941
|
+
return invalidPrecondition(
|
|
15942
|
+
"folder voice items cannot update file fields",
|
|
15943
|
+
);
|
|
15944
|
+
}
|
|
15945
|
+
|
|
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({
|
|
15957
|
+
state,
|
|
15958
|
+
data: payload.data,
|
|
15959
|
+
fields: ["fileId", "waveformDataFileId"],
|
|
15960
|
+
nullableFields: ["waveformDataFileId"],
|
|
15961
|
+
details: {
|
|
15962
|
+
voiceId: payload.voiceId,
|
|
15963
|
+
},
|
|
15964
|
+
});
|
|
15965
|
+
}
|
|
15966
|
+
},
|
|
15967
|
+
reduce: ({ state, payload }) => {
|
|
15968
|
+
state.voices.items[payload.voiceId] = {
|
|
15969
|
+
...structuredClone(state.voices.items[payload.voiceId]),
|
|
15970
|
+
...structuredClone(payload.data),
|
|
15971
|
+
};
|
|
15972
|
+
return state;
|
|
15973
|
+
},
|
|
15974
|
+
},
|
|
15975
|
+
{
|
|
15976
|
+
type: "voice.delete",
|
|
15977
|
+
validatePayload: ({ payload }) => {
|
|
15978
|
+
{
|
|
15979
|
+
const result = validateExactKeys({
|
|
15980
|
+
value: payload,
|
|
15981
|
+
expectedKeys: ["voiceIds"],
|
|
15982
|
+
path: "payload",
|
|
15983
|
+
errorFactory: createPayloadValidationError,
|
|
15984
|
+
});
|
|
15985
|
+
if (result?.valid === false) {
|
|
15986
|
+
return result;
|
|
15987
|
+
}
|
|
15988
|
+
}
|
|
15989
|
+
|
|
15990
|
+
{
|
|
15991
|
+
const result = validateRequiredUniqueIdArray({
|
|
15992
|
+
value: payload.voiceIds,
|
|
15993
|
+
path: "payload.voiceIds",
|
|
15994
|
+
errorFactory: createPayloadValidationError,
|
|
15995
|
+
});
|
|
15996
|
+
if (result?.valid === false) {
|
|
15997
|
+
return result;
|
|
15998
|
+
}
|
|
15999
|
+
}
|
|
16000
|
+
},
|
|
16001
|
+
validateAgainstState: ({ state, payload }) => {
|
|
16002
|
+
for (const voiceId of payload.voiceIds) {
|
|
16003
|
+
if (!isPlainObject(state.voices.items[voiceId])) {
|
|
16004
|
+
return invalidPrecondition(
|
|
16005
|
+
"payload.voiceIds must reference existing voice items",
|
|
16006
|
+
{ voiceId },
|
|
16007
|
+
);
|
|
16008
|
+
}
|
|
16009
|
+
}
|
|
16010
|
+
},
|
|
16011
|
+
reduce: ({ state, payload }) => {
|
|
16012
|
+
const deletedVoiceIds = new Set();
|
|
16013
|
+
|
|
16014
|
+
for (const voiceId of payload.voiceIds) {
|
|
16015
|
+
const removedNode = removeTreeNode({
|
|
16016
|
+
nodes: state.voices.tree,
|
|
16017
|
+
nodeId: voiceId,
|
|
16018
|
+
});
|
|
16019
|
+
|
|
16020
|
+
if (!removedNode) {
|
|
16021
|
+
continue;
|
|
16022
|
+
}
|
|
16023
|
+
|
|
16024
|
+
for (const id of collectTreeDescendantIds({ node: removedNode })) {
|
|
16025
|
+
deletedVoiceIds.add(id);
|
|
16026
|
+
}
|
|
16027
|
+
}
|
|
16028
|
+
|
|
16029
|
+
for (const voiceId of deletedVoiceIds) {
|
|
16030
|
+
delete state.voices.items[voiceId];
|
|
16031
|
+
}
|
|
16032
|
+
|
|
16033
|
+
return state;
|
|
16034
|
+
},
|
|
16035
|
+
},
|
|
16036
|
+
{
|
|
16037
|
+
type: "voice.move",
|
|
16038
|
+
validatePayload: ({ payload }) => {
|
|
16039
|
+
{
|
|
16040
|
+
const result = validateAllowedKeys({
|
|
16041
|
+
value: payload,
|
|
16042
|
+
allowedKeys: [
|
|
16043
|
+
"voiceId",
|
|
16044
|
+
"parentId",
|
|
16045
|
+
"index",
|
|
16046
|
+
"position",
|
|
16047
|
+
"positionTargetId",
|
|
16048
|
+
],
|
|
16049
|
+
path: "payload",
|
|
16050
|
+
errorFactory: createPayloadValidationError,
|
|
16051
|
+
});
|
|
16052
|
+
if (result?.valid === false) {
|
|
16053
|
+
return result;
|
|
16054
|
+
}
|
|
16055
|
+
}
|
|
16056
|
+
|
|
16057
|
+
if (!isNonEmptyString(payload.voiceId)) {
|
|
16058
|
+
return invalidPayload("payload.voiceId must be a non-empty string");
|
|
16059
|
+
}
|
|
16060
|
+
|
|
16061
|
+
if (
|
|
16062
|
+
payload.parentId !== undefined &&
|
|
16063
|
+
payload.parentId !== null &&
|
|
16064
|
+
!isNonEmptyString(payload.parentId)
|
|
16065
|
+
) {
|
|
16066
|
+
return invalidPayload(
|
|
16067
|
+
"payload.parentId must be a non-empty string when provided",
|
|
16068
|
+
);
|
|
16069
|
+
}
|
|
16070
|
+
|
|
16071
|
+
{
|
|
16072
|
+
const result = validatePlacementFields({
|
|
16073
|
+
payload,
|
|
16074
|
+
errorFactory: createPayloadValidationError,
|
|
16075
|
+
});
|
|
16076
|
+
if (result?.valid === false) {
|
|
16077
|
+
return result;
|
|
16078
|
+
}
|
|
16079
|
+
}
|
|
16080
|
+
},
|
|
16081
|
+
validateAgainstState: ({ state, payload }) => {
|
|
16082
|
+
const voice = state.voices.items[payload.voiceId];
|
|
16083
|
+
if (!isPlainObject(voice)) {
|
|
16084
|
+
return invalidPrecondition(
|
|
16085
|
+
"payload.voiceId must reference an existing voice item",
|
|
16086
|
+
);
|
|
16087
|
+
}
|
|
16088
|
+
|
|
16089
|
+
const voiceNode = findTreeNode({
|
|
16090
|
+
nodes: state.voices.tree,
|
|
16091
|
+
nodeId: payload.voiceId,
|
|
16092
|
+
});
|
|
16093
|
+
|
|
16094
|
+
if (payload.parentId !== undefined && payload.parentId !== null) {
|
|
16095
|
+
const parentVoice = state.voices.items[payload.parentId];
|
|
16096
|
+
if (!isPlainObject(parentVoice)) {
|
|
16097
|
+
return invalidPrecondition(
|
|
16098
|
+
"payload.parentId must reference an existing voice item",
|
|
16099
|
+
);
|
|
16100
|
+
}
|
|
16101
|
+
|
|
16102
|
+
if (parentVoice.type !== "folder") {
|
|
16103
|
+
return invalidPrecondition(
|
|
16104
|
+
"payload.parentId must reference a folder voice item",
|
|
16105
|
+
);
|
|
16106
|
+
}
|
|
16107
|
+
|
|
16108
|
+
const descendantIds = new Set(
|
|
16109
|
+
collectTreeDescendantIds({
|
|
16110
|
+
node: voiceNode,
|
|
16111
|
+
}),
|
|
16112
|
+
);
|
|
16113
|
+
|
|
16114
|
+
if (descendantIds.has(payload.parentId)) {
|
|
16115
|
+
return invalidPrecondition(
|
|
16116
|
+
"payload.parentId must not target the moved voice item or its descendants",
|
|
16117
|
+
);
|
|
16118
|
+
}
|
|
16119
|
+
}
|
|
16120
|
+
|
|
16121
|
+
if (payload.positionTargetId !== undefined) {
|
|
16122
|
+
if (payload.positionTargetId === payload.voiceId) {
|
|
16123
|
+
return invalidPrecondition(
|
|
16124
|
+
"payload.positionTargetId must not reference the moved voice item",
|
|
16125
|
+
);
|
|
16126
|
+
}
|
|
16127
|
+
|
|
16128
|
+
if (!isPlainObject(state.voices.items[payload.positionTargetId])) {
|
|
16129
|
+
return invalidPrecondition(
|
|
16130
|
+
"payload.positionTargetId must reference an existing voice item",
|
|
16131
|
+
);
|
|
16132
|
+
}
|
|
16133
|
+
|
|
16134
|
+
const targetParentId = getNodeParentId({
|
|
16135
|
+
tree: state.voices.tree,
|
|
16136
|
+
nodeId: payload.positionTargetId,
|
|
16137
|
+
});
|
|
16138
|
+
|
|
16139
|
+
if (targetParentId !== (payload.parentId ?? null)) {
|
|
16140
|
+
return invalidPrecondition(
|
|
16141
|
+
"payload.positionTargetId must reference a sibling under payload.parentId",
|
|
16142
|
+
);
|
|
16143
|
+
}
|
|
16144
|
+
}
|
|
16145
|
+
},
|
|
16146
|
+
reduce: ({ state, payload }) => {
|
|
16147
|
+
const voiceNodeResult = removeNodeOrResult({
|
|
16148
|
+
tree: state.voices.tree,
|
|
16149
|
+
nodeId: payload.voiceId,
|
|
16150
|
+
errorMessage: "voice move target missing from tree",
|
|
16151
|
+
});
|
|
16152
|
+
if (!voiceNodeResult.valid) {
|
|
16153
|
+
return voiceNodeResult;
|
|
16154
|
+
}
|
|
16155
|
+
|
|
16156
|
+
insertTreeNode({
|
|
16157
|
+
tree: state.voices.tree,
|
|
16158
|
+
node: voiceNodeResult.node,
|
|
16159
|
+
parentId: payload.parentId ?? null,
|
|
16160
|
+
index: payload.index,
|
|
16161
|
+
position: payload.position,
|
|
16162
|
+
positionTargetId: payload.positionTargetId,
|
|
16163
|
+
});
|
|
16164
|
+
|
|
16165
|
+
return state;
|
|
16166
|
+
},
|
|
16167
|
+
},
|
|
15387
16168
|
{
|
|
15388
16169
|
type: "video.create",
|
|
15389
16170
|
validatePayload: ({ payload }) => {
|