@routevn/creator-model 1.2.0 → 1.2.3
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/index.js +1 -6
- package/src/model.js +504 -20
- package/src/runtimeFields.js +22 -0
- package/src/systemVariables.js +0 -69
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -5,9 +5,4 @@ export {
|
|
|
5
5
|
validatePayload,
|
|
6
6
|
validateState,
|
|
7
7
|
} from "./model.js";
|
|
8
|
-
export {
|
|
9
|
-
SYSTEM_VARIABLE_GROUPS,
|
|
10
|
-
SYSTEM_VARIABLE_IDS,
|
|
11
|
-
getSystemVariableDefinitions,
|
|
12
|
-
isSystemVariableId,
|
|
13
|
-
} from "./systemVariables.js";
|
|
8
|
+
export { RUNTIME_FIELD_IDS, isRuntimeFieldId } from "./runtimeFields.js";
|
package/src/model.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
isPlainObject,
|
|
16
16
|
removeTreeNode,
|
|
17
17
|
} from "./helpers.js";
|
|
18
|
-
import {
|
|
18
|
+
import { isRuntimeFieldId } from "./runtimeFields.js";
|
|
19
19
|
|
|
20
20
|
const COLLECTION_KEYS = [
|
|
21
21
|
"scenes",
|
|
@@ -25,6 +25,7 @@ const COLLECTION_KEYS = [
|
|
|
25
25
|
"sounds",
|
|
26
26
|
"videos",
|
|
27
27
|
"animations",
|
|
28
|
+
"particles",
|
|
28
29
|
"characters",
|
|
29
30
|
"fonts",
|
|
30
31
|
"transforms",
|
|
@@ -45,9 +46,11 @@ const normalizeStateCollections = (state) => {
|
|
|
45
46
|
return state;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
const missingCollectionKeys = [
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const missingCollectionKeys = [
|
|
50
|
+
"spritesheets",
|
|
51
|
+
"particles",
|
|
52
|
+
"controls",
|
|
53
|
+
].filter((key) => state[key] === undefined);
|
|
51
54
|
|
|
52
55
|
if (missingCollectionKeys.length === 0) {
|
|
53
56
|
return state;
|
|
@@ -117,7 +120,7 @@ const ANIMATION_EASING_KEYS = [
|
|
|
117
120
|
"easeOutElastic",
|
|
118
121
|
"easeInOutElastic",
|
|
119
122
|
];
|
|
120
|
-
const VARIABLE_SCOPE_KEYS = ["context", "
|
|
123
|
+
const VARIABLE_SCOPE_KEYS = ["context", "device", "account"];
|
|
121
124
|
const VARIABLE_TYPE_KEYS = ["string", "number", "boolean"];
|
|
122
125
|
const LAYOUT_TYPE_KEYS = [
|
|
123
126
|
"normal",
|
|
@@ -135,6 +138,7 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
135
138
|
"container",
|
|
136
139
|
"rect",
|
|
137
140
|
"sprite",
|
|
141
|
+
"particle",
|
|
138
142
|
"spritesheet-animation",
|
|
139
143
|
"text",
|
|
140
144
|
"text-revealing",
|
|
@@ -255,20 +259,12 @@ const invalidInvariant = (message, details = {}) =>
|
|
|
255
259
|
});
|
|
256
260
|
|
|
257
261
|
const isVariableReferenceTarget = (state, variableId) => {
|
|
258
|
-
if (isSystemVariableId(variableId)) {
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
262
|
const variable = state.variables.items[variableId];
|
|
263
263
|
return isPlainObject(variable) && variable.type !== "folder";
|
|
264
264
|
};
|
|
265
265
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
"isLineCompleted",
|
|
269
|
-
"autoMode",
|
|
270
|
-
"skipMode",
|
|
271
|
-
]);
|
|
266
|
+
const LAYOUT_ITEM_TARGET_SET = new Set(["item.savedAt"]);
|
|
267
|
+
const RUNTIME_TARGET_DOT_PATTERN = /^runtime\.([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
272
268
|
const VARIABLE_TARGET_DOT_PATTERN = /^variables\.([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
273
269
|
const VARIABLE_TARGET_BRACKET_PATTERN = /^variables\[(.+)\]$/;
|
|
274
270
|
|
|
@@ -277,10 +273,19 @@ const parseLayoutConditionTarget = (target) => {
|
|
|
277
273
|
return undefined;
|
|
278
274
|
}
|
|
279
275
|
|
|
280
|
-
if (
|
|
276
|
+
if (LAYOUT_ITEM_TARGET_SET.has(target)) {
|
|
277
|
+
return {
|
|
278
|
+
kind: "item",
|
|
279
|
+
target,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const runtimeMatch = target.match(RUNTIME_TARGET_DOT_PATTERN);
|
|
284
|
+
if (runtimeMatch && isRuntimeFieldId(runtimeMatch[1])) {
|
|
281
285
|
return {
|
|
282
286
|
kind: "runtime",
|
|
283
287
|
target,
|
|
288
|
+
runtimeId: runtimeMatch[1],
|
|
284
289
|
};
|
|
285
290
|
}
|
|
286
291
|
|
|
@@ -1198,6 +1203,7 @@ const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
|
1198
1203
|
"thumbnailFileId",
|
|
1199
1204
|
"fileType",
|
|
1200
1205
|
"fileSize",
|
|
1206
|
+
"duration",
|
|
1201
1207
|
"width",
|
|
1202
1208
|
"height",
|
|
1203
1209
|
],
|
|
@@ -1266,6 +1272,13 @@ const validateVideoItems = ({ items, path, errorFactory }) => {
|
|
|
1266
1272
|
);
|
|
1267
1273
|
}
|
|
1268
1274
|
|
|
1275
|
+
if (item.duration !== undefined && !isFiniteNumber(item.duration)) {
|
|
1276
|
+
return invalidFromErrorFactory(
|
|
1277
|
+
errorFactory,
|
|
1278
|
+
`${itemPath}.duration must be a finite number`,
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1269
1282
|
if (item.width !== undefined && !isFiniteNumber(item.width)) {
|
|
1270
1283
|
return invalidFromErrorFactory(
|
|
1271
1284
|
errorFactory,
|
|
@@ -2224,6 +2237,141 @@ const validateTransformItems = ({ items, path, errorFactory }) => {
|
|
|
2224
2237
|
}
|
|
2225
2238
|
};
|
|
2226
2239
|
|
|
2240
|
+
const validateParticleModules = ({ modules, path, errorFactory }) => {
|
|
2241
|
+
if (!isPlainObject(modules)) {
|
|
2242
|
+
return invalidFromErrorFactory(errorFactory, `${path} must be an object`);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
if (!isPlainObject(modules.emission)) {
|
|
2246
|
+
return invalidFromErrorFactory(
|
|
2247
|
+
errorFactory,
|
|
2248
|
+
`${path}.emission must be an object`,
|
|
2249
|
+
);
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
if (!isPlainObject(modules.appearance)) {
|
|
2253
|
+
return invalidFromErrorFactory(
|
|
2254
|
+
errorFactory,
|
|
2255
|
+
`${path}.appearance must be an object`,
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
if (modules.movement !== undefined && !isPlainObject(modules.movement)) {
|
|
2260
|
+
return invalidFromErrorFactory(
|
|
2261
|
+
errorFactory,
|
|
2262
|
+
`${path}.movement must be an object when provided`,
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
if (modules.bounds !== undefined && !isPlainObject(modules.bounds)) {
|
|
2267
|
+
return invalidFromErrorFactory(
|
|
2268
|
+
errorFactory,
|
|
2269
|
+
`${path}.bounds must be an object when provided`,
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
|
|
2274
|
+
const validateParticleItems = ({ items, path, errorFactory }) => {
|
|
2275
|
+
for (const [itemId, item] of Object.entries(items)) {
|
|
2276
|
+
const itemPath = `${path}.${itemId}`;
|
|
2277
|
+
|
|
2278
|
+
if (item?.type !== "folder" && item?.type !== "particle") {
|
|
2279
|
+
return invalidFromErrorFactory(
|
|
2280
|
+
errorFactory,
|
|
2281
|
+
`${itemPath}.type must be 'folder' or 'particle'`,
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
{
|
|
2286
|
+
const result = validateAllowedKeys({
|
|
2287
|
+
value: item,
|
|
2288
|
+
allowedKeys:
|
|
2289
|
+
item.type === "folder"
|
|
2290
|
+
? ["id", "type", "name", "description"]
|
|
2291
|
+
: [
|
|
2292
|
+
"id",
|
|
2293
|
+
"type",
|
|
2294
|
+
"name",
|
|
2295
|
+
"description",
|
|
2296
|
+
"width",
|
|
2297
|
+
"height",
|
|
2298
|
+
"seed",
|
|
2299
|
+
"modules",
|
|
2300
|
+
],
|
|
2301
|
+
path: itemPath,
|
|
2302
|
+
errorFactory,
|
|
2303
|
+
});
|
|
2304
|
+
if (result?.valid === false) {
|
|
2305
|
+
return result;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
if (!isNonEmptyString(item.id)) {
|
|
2310
|
+
return invalidFromErrorFactory(
|
|
2311
|
+
errorFactory,
|
|
2312
|
+
`${itemPath}.id must be a non-empty string`,
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
if (item.id !== itemId) {
|
|
2317
|
+
return invalidFromErrorFactory(
|
|
2318
|
+
errorFactory,
|
|
2319
|
+
`${itemPath}.id must match item key '${itemId}'`,
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
if (!isNonEmptyString(item.name)) {
|
|
2324
|
+
return invalidFromErrorFactory(
|
|
2325
|
+
errorFactory,
|
|
2326
|
+
`${itemPath}.name must be a non-empty string`,
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
if (item.description !== undefined && !isString(item.description)) {
|
|
2331
|
+
return invalidFromErrorFactory(
|
|
2332
|
+
errorFactory,
|
|
2333
|
+
`${itemPath}.description must be a string when provided`,
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
if (item.type !== "particle") {
|
|
2338
|
+
continue;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
if (!isFiniteNumber(item.width) || item.width <= 0) {
|
|
2342
|
+
return invalidFromErrorFactory(
|
|
2343
|
+
errorFactory,
|
|
2344
|
+
`${itemPath}.width must be a positive finite number`,
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
if (!isFiniteNumber(item.height) || item.height <= 0) {
|
|
2349
|
+
return invalidFromErrorFactory(
|
|
2350
|
+
errorFactory,
|
|
2351
|
+
`${itemPath}.height must be a positive finite number`,
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
if (item.seed !== undefined && !isFiniteNumber(item.seed)) {
|
|
2356
|
+
return invalidFromErrorFactory(
|
|
2357
|
+
errorFactory,
|
|
2358
|
+
`${itemPath}.seed must be a finite number when provided`,
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
{
|
|
2363
|
+
const result = validateParticleModules({
|
|
2364
|
+
modules: item.modules,
|
|
2365
|
+
path: `${itemPath}.modules`,
|
|
2366
|
+
errorFactory,
|
|
2367
|
+
});
|
|
2368
|
+
if (result?.valid === false) {
|
|
2369
|
+
return result;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2227
2375
|
const validateVariableTypedValue = ({
|
|
2228
2376
|
value,
|
|
2229
2377
|
variableType,
|
|
@@ -2316,7 +2464,7 @@ const validateVariableItems = ({ items, path, errorFactory }) => {
|
|
|
2316
2464
|
if (!VARIABLE_SCOPE_KEYS.includes(item.scope)) {
|
|
2317
2465
|
return invalidFromErrorFactory(
|
|
2318
2466
|
errorFactory,
|
|
2319
|
-
`${itemPath}.scope must be 'context', '
|
|
2467
|
+
`${itemPath}.scope must be 'context', 'device', or 'account'`,
|
|
2320
2468
|
);
|
|
2321
2469
|
}
|
|
2322
2470
|
|
|
@@ -2801,6 +2949,7 @@ const validateLayoutElementData = ({
|
|
|
2801
2949
|
"step",
|
|
2802
2950
|
"initialValue",
|
|
2803
2951
|
"variableId",
|
|
2952
|
+
"particleId",
|
|
2804
2953
|
"fragmentLayoutId",
|
|
2805
2954
|
"paginationMode",
|
|
2806
2955
|
"paginationVariableId",
|
|
@@ -2900,6 +3049,7 @@ const validateLayoutElementData = ({
|
|
|
2900
3049
|
"text",
|
|
2901
3050
|
"resourceId",
|
|
2902
3051
|
"animationName",
|
|
3052
|
+
"particleId",
|
|
2903
3053
|
"imageId",
|
|
2904
3054
|
"hoverImageId",
|
|
2905
3055
|
"clickImageId",
|
|
@@ -2948,6 +3098,18 @@ const validateLayoutElementData = ({
|
|
|
2948
3098
|
}
|
|
2949
3099
|
}
|
|
2950
3100
|
|
|
3101
|
+
if (data.type === "particle") {
|
|
3102
|
+
if (
|
|
3103
|
+
(!allowPartial || data.particleId !== undefined) &&
|
|
3104
|
+
!isNonEmptyString(data.particleId)
|
|
3105
|
+
) {
|
|
3106
|
+
return invalidFromErrorFactory(
|
|
3107
|
+
errorFactory,
|
|
3108
|
+
`${path}.particleId must be a non-empty string`,
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
|
|
2951
3113
|
if (data.conditionalOverrides !== undefined) {
|
|
2952
3114
|
if (!Array.isArray(data.conditionalOverrides)) {
|
|
2953
3115
|
return invalidFromErrorFactory(
|
|
@@ -3299,6 +3461,7 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
3299
3461
|
"revealEffect",
|
|
3300
3462
|
"resourceId",
|
|
3301
3463
|
"animationName",
|
|
3464
|
+
"particleId",
|
|
3302
3465
|
"imageId",
|
|
3303
3466
|
"hoverImageId",
|
|
3304
3467
|
"clickImageId",
|
|
@@ -4293,6 +4456,17 @@ const validateCollection = ({ collection, path }) => {
|
|
|
4293
4456
|
return result;
|
|
4294
4457
|
}
|
|
4295
4458
|
}
|
|
4459
|
+
} else if (path === "state.particles") {
|
|
4460
|
+
{
|
|
4461
|
+
const result = validateParticleItems({
|
|
4462
|
+
items: collection.items,
|
|
4463
|
+
path: `${path}.items`,
|
|
4464
|
+
errorFactory: createStateValidationError,
|
|
4465
|
+
});
|
|
4466
|
+
if (result?.valid === false) {
|
|
4467
|
+
return result;
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4296
4470
|
} else if (path === "state.variables") {
|
|
4297
4471
|
{
|
|
4298
4472
|
const result = validateVariableItems({
|
|
@@ -4458,6 +4632,7 @@ const validateCollection = ({ collection, path }) => {
|
|
|
4458
4632
|
}
|
|
4459
4633
|
} else if (
|
|
4460
4634
|
path === "state.files" ||
|
|
4635
|
+
path === "state.particles" ||
|
|
4461
4636
|
path === "state.transforms" ||
|
|
4462
4637
|
path === "state.variables" ||
|
|
4463
4638
|
path === "state.textStyles" ||
|
|
@@ -5124,6 +5299,29 @@ export const assertInvariants = ({ state }) => {
|
|
|
5124
5299
|
return VALID_RESULT;
|
|
5125
5300
|
};
|
|
5126
5301
|
|
|
5302
|
+
const assertParticleReference = ({
|
|
5303
|
+
ownerIdField,
|
|
5304
|
+
ownerId,
|
|
5305
|
+
ownerLabel,
|
|
5306
|
+
elementId,
|
|
5307
|
+
targetId,
|
|
5308
|
+
}) => {
|
|
5309
|
+
const particle = state.particles?.items?.[targetId];
|
|
5310
|
+
if (!isPlainObject(particle) || particle.type === "folder") {
|
|
5311
|
+
return invalidInvariant(
|
|
5312
|
+
`${ownerLabel} element particleId must reference an existing non-folder particle`,
|
|
5313
|
+
{
|
|
5314
|
+
[ownerIdField]: ownerId,
|
|
5315
|
+
elementId,
|
|
5316
|
+
field: "particleId",
|
|
5317
|
+
targetId,
|
|
5318
|
+
},
|
|
5319
|
+
);
|
|
5320
|
+
}
|
|
5321
|
+
|
|
5322
|
+
return VALID_RESULT;
|
|
5323
|
+
};
|
|
5324
|
+
|
|
5127
5325
|
const assertElementReferencesForCollection = ({
|
|
5128
5326
|
items,
|
|
5129
5327
|
ownerIdField,
|
|
@@ -5154,6 +5352,19 @@ export const assertInvariants = ({ state }) => {
|
|
|
5154
5352
|
}
|
|
5155
5353
|
}
|
|
5156
5354
|
|
|
5355
|
+
if (element.type === "particle" || element.particleId !== undefined) {
|
|
5356
|
+
const result = assertParticleReference({
|
|
5357
|
+
ownerIdField,
|
|
5358
|
+
ownerId,
|
|
5359
|
+
ownerLabel,
|
|
5360
|
+
elementId,
|
|
5361
|
+
targetId: element.particleId,
|
|
5362
|
+
});
|
|
5363
|
+
if (!result.valid) {
|
|
5364
|
+
return result;
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
|
|
5157
5368
|
for (const field of [
|
|
5158
5369
|
"imageId",
|
|
5159
5370
|
"hoverImageId",
|
|
@@ -6327,6 +6538,7 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
6327
6538
|
"thumbnailFileId",
|
|
6328
6539
|
"fileType",
|
|
6329
6540
|
"fileSize",
|
|
6541
|
+
"duration",
|
|
6330
6542
|
"width",
|
|
6331
6543
|
"height",
|
|
6332
6544
|
],
|
|
@@ -6381,6 +6593,13 @@ const validateVideoCreateData = ({ data, errorFactory }) => {
|
|
|
6381
6593
|
);
|
|
6382
6594
|
}
|
|
6383
6595
|
|
|
6596
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
6597
|
+
return invalidFromErrorFactory(
|
|
6598
|
+
errorFactory,
|
|
6599
|
+
"payload.data.duration must be a finite number",
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
|
|
6384
6603
|
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
6385
6604
|
return invalidFromErrorFactory(
|
|
6386
6605
|
errorFactory,
|
|
@@ -6408,6 +6627,7 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
6408
6627
|
"thumbnailFileId",
|
|
6409
6628
|
"fileType",
|
|
6410
6629
|
"fileSize",
|
|
6630
|
+
"duration",
|
|
6411
6631
|
"width",
|
|
6412
6632
|
"height",
|
|
6413
6633
|
],
|
|
@@ -6471,6 +6691,13 @@ const validateVideoUpdateData = ({ data, errorFactory }) => {
|
|
|
6471
6691
|
);
|
|
6472
6692
|
}
|
|
6473
6693
|
|
|
6694
|
+
if (data.duration !== undefined && !isFiniteNumber(data.duration)) {
|
|
6695
|
+
return invalidFromErrorFactory(
|
|
6696
|
+
errorFactory,
|
|
6697
|
+
"payload.data.duration must be a finite number",
|
|
6698
|
+
);
|
|
6699
|
+
}
|
|
6700
|
+
|
|
6474
6701
|
if (data.width !== undefined && !isFiniteNumber(data.width)) {
|
|
6475
6702
|
return invalidFromErrorFactory(
|
|
6476
6703
|
errorFactory,
|
|
@@ -7014,6 +7241,185 @@ const validateTransformCreateData = ({ data, errorFactory }) => {
|
|
|
7014
7241
|
}
|
|
7015
7242
|
};
|
|
7016
7243
|
|
|
7244
|
+
const validateParticleCreateData = ({ data, errorFactory }) => {
|
|
7245
|
+
if (!isPlainObject(data)) {
|
|
7246
|
+
return invalidFromErrorFactory(
|
|
7247
|
+
errorFactory,
|
|
7248
|
+
"payload.data must be an object",
|
|
7249
|
+
);
|
|
7250
|
+
}
|
|
7251
|
+
|
|
7252
|
+
if (data.type !== "folder" && data.type !== "particle") {
|
|
7253
|
+
return invalidFromErrorFactory(
|
|
7254
|
+
errorFactory,
|
|
7255
|
+
"payload.data.type must be 'folder' or 'particle'",
|
|
7256
|
+
);
|
|
7257
|
+
}
|
|
7258
|
+
|
|
7259
|
+
{
|
|
7260
|
+
const result = validateAllowedKeys({
|
|
7261
|
+
value: data,
|
|
7262
|
+
allowedKeys:
|
|
7263
|
+
data.type === "folder"
|
|
7264
|
+
? ["type", "name", "description"]
|
|
7265
|
+
: [
|
|
7266
|
+
"type",
|
|
7267
|
+
"name",
|
|
7268
|
+
"description",
|
|
7269
|
+
"width",
|
|
7270
|
+
"height",
|
|
7271
|
+
"seed",
|
|
7272
|
+
"modules",
|
|
7273
|
+
],
|
|
7274
|
+
path: "payload.data",
|
|
7275
|
+
errorFactory,
|
|
7276
|
+
});
|
|
7277
|
+
if (result?.valid === false) {
|
|
7278
|
+
return result;
|
|
7279
|
+
}
|
|
7280
|
+
}
|
|
7281
|
+
|
|
7282
|
+
if (!isNonEmptyString(data.name)) {
|
|
7283
|
+
return invalidFromErrorFactory(
|
|
7284
|
+
errorFactory,
|
|
7285
|
+
"payload.data.name must be a non-empty string",
|
|
7286
|
+
);
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7289
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
7290
|
+
return invalidFromErrorFactory(
|
|
7291
|
+
errorFactory,
|
|
7292
|
+
"payload.data.description must be a string when provided",
|
|
7293
|
+
);
|
|
7294
|
+
}
|
|
7295
|
+
|
|
7296
|
+
if (data.type !== "particle") {
|
|
7297
|
+
return;
|
|
7298
|
+
}
|
|
7299
|
+
|
|
7300
|
+
if (!isFiniteNumber(data.width) || data.width <= 0) {
|
|
7301
|
+
return invalidFromErrorFactory(
|
|
7302
|
+
errorFactory,
|
|
7303
|
+
"payload.data.width must be a positive finite number",
|
|
7304
|
+
);
|
|
7305
|
+
}
|
|
7306
|
+
|
|
7307
|
+
if (!isFiniteNumber(data.height) || data.height <= 0) {
|
|
7308
|
+
return invalidFromErrorFactory(
|
|
7309
|
+
errorFactory,
|
|
7310
|
+
"payload.data.height must be a positive finite number",
|
|
7311
|
+
);
|
|
7312
|
+
}
|
|
7313
|
+
|
|
7314
|
+
if (
|
|
7315
|
+
data.seed !== undefined &&
|
|
7316
|
+
data.seed !== null &&
|
|
7317
|
+
!isFiniteNumber(data.seed)
|
|
7318
|
+
) {
|
|
7319
|
+
return invalidFromErrorFactory(
|
|
7320
|
+
errorFactory,
|
|
7321
|
+
"payload.data.seed must be a finite number when provided",
|
|
7322
|
+
);
|
|
7323
|
+
}
|
|
7324
|
+
|
|
7325
|
+
{
|
|
7326
|
+
const result = validateParticleModules({
|
|
7327
|
+
modules: data.modules,
|
|
7328
|
+
path: "payload.data.modules",
|
|
7329
|
+
errorFactory,
|
|
7330
|
+
});
|
|
7331
|
+
if (result?.valid === false) {
|
|
7332
|
+
return result;
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
};
|
|
7336
|
+
|
|
7337
|
+
const validateParticleUpdateData = ({ data, errorFactory }) => {
|
|
7338
|
+
{
|
|
7339
|
+
const result = validateAllowedKeys({
|
|
7340
|
+
value: data,
|
|
7341
|
+
allowedKeys: [
|
|
7342
|
+
"name",
|
|
7343
|
+
"description",
|
|
7344
|
+
"width",
|
|
7345
|
+
"height",
|
|
7346
|
+
"seed",
|
|
7347
|
+
"modules",
|
|
7348
|
+
],
|
|
7349
|
+
path: "payload.data",
|
|
7350
|
+
errorFactory,
|
|
7351
|
+
});
|
|
7352
|
+
if (result?.valid === false) {
|
|
7353
|
+
return result;
|
|
7354
|
+
}
|
|
7355
|
+
}
|
|
7356
|
+
|
|
7357
|
+
if (Object.keys(data).length === 0) {
|
|
7358
|
+
return invalidFromErrorFactory(
|
|
7359
|
+
errorFactory,
|
|
7360
|
+
"payload.data must include at least one updatable field",
|
|
7361
|
+
);
|
|
7362
|
+
}
|
|
7363
|
+
|
|
7364
|
+
if (data.name !== undefined && !isNonEmptyString(data.name)) {
|
|
7365
|
+
return invalidFromErrorFactory(
|
|
7366
|
+
errorFactory,
|
|
7367
|
+
"payload.data.name must be a non-empty string when provided",
|
|
7368
|
+
);
|
|
7369
|
+
}
|
|
7370
|
+
|
|
7371
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
7372
|
+
return invalidFromErrorFactory(
|
|
7373
|
+
errorFactory,
|
|
7374
|
+
"payload.data.description must be a string when provided",
|
|
7375
|
+
);
|
|
7376
|
+
}
|
|
7377
|
+
|
|
7378
|
+
if (
|
|
7379
|
+
data.width !== undefined &&
|
|
7380
|
+
(!isFiniteNumber(data.width) || data.width <= 0)
|
|
7381
|
+
) {
|
|
7382
|
+
return invalidFromErrorFactory(
|
|
7383
|
+
errorFactory,
|
|
7384
|
+
"payload.data.width must be a positive finite number when provided",
|
|
7385
|
+
);
|
|
7386
|
+
}
|
|
7387
|
+
|
|
7388
|
+
if (
|
|
7389
|
+
data.height !== undefined &&
|
|
7390
|
+
(!isFiniteNumber(data.height) || data.height <= 0)
|
|
7391
|
+
) {
|
|
7392
|
+
return invalidFromErrorFactory(
|
|
7393
|
+
errorFactory,
|
|
7394
|
+
"payload.data.height must be a positive finite number when provided",
|
|
7395
|
+
);
|
|
7396
|
+
}
|
|
7397
|
+
|
|
7398
|
+
if (
|
|
7399
|
+
data.seed !== undefined &&
|
|
7400
|
+
data.seed !== null &&
|
|
7401
|
+
!isFiniteNumber(data.seed)
|
|
7402
|
+
) {
|
|
7403
|
+
return invalidFromErrorFactory(
|
|
7404
|
+
errorFactory,
|
|
7405
|
+
"payload.data.seed must be a finite number when provided",
|
|
7406
|
+
);
|
|
7407
|
+
}
|
|
7408
|
+
|
|
7409
|
+
if (data.modules !== undefined) {
|
|
7410
|
+
{
|
|
7411
|
+
const result = validateParticleModules({
|
|
7412
|
+
modules: data.modules,
|
|
7413
|
+
path: "payload.data.modules",
|
|
7414
|
+
errorFactory,
|
|
7415
|
+
});
|
|
7416
|
+
if (result?.valid === false) {
|
|
7417
|
+
return result;
|
|
7418
|
+
}
|
|
7419
|
+
}
|
|
7420
|
+
}
|
|
7421
|
+
};
|
|
7422
|
+
|
|
7017
7423
|
const validateTransformUpdateData = ({ data, errorFactory }) => {
|
|
7018
7424
|
{
|
|
7019
7425
|
const result = validateAllowedKeys({
|
|
@@ -7124,7 +7530,7 @@ const validateVariableCreateData = ({ data, errorFactory }) => {
|
|
|
7124
7530
|
if (!VARIABLE_SCOPE_KEYS.includes(data.scope)) {
|
|
7125
7531
|
return invalidFromErrorFactory(
|
|
7126
7532
|
errorFactory,
|
|
7127
|
-
"payload.data.scope must be 'context', '
|
|
7533
|
+
"payload.data.scope must be 'context', 'device', or 'account'",
|
|
7128
7534
|
);
|
|
7129
7535
|
}
|
|
7130
7536
|
|
|
@@ -7190,7 +7596,7 @@ const validateVariableUpdateData = ({ data, errorFactory }) => {
|
|
|
7190
7596
|
if (data.scope !== undefined && !VARIABLE_SCOPE_KEYS.includes(data.scope)) {
|
|
7191
7597
|
return invalidFromErrorFactory(
|
|
7192
7598
|
errorFactory,
|
|
7193
|
-
"payload.data.scope must be 'context', '
|
|
7599
|
+
"payload.data.scope must be 'context', 'device', or 'account' when provided",
|
|
7194
7600
|
);
|
|
7195
7601
|
}
|
|
7196
7602
|
};
|
|
@@ -8130,6 +8536,22 @@ const validateVisualElementReferenceTargets = ({
|
|
|
8130
8536
|
}
|
|
8131
8537
|
}
|
|
8132
8538
|
|
|
8539
|
+
if (data.type === "particle" || data.particleId !== undefined) {
|
|
8540
|
+
const particle = state.particles?.items?.[data.particleId];
|
|
8541
|
+
if (!isPlainObject(particle) || particle.type === "folder") {
|
|
8542
|
+
return invalidFromErrorFactory(
|
|
8543
|
+
errorFactory,
|
|
8544
|
+
`${ownerLabel} element particleId must reference an existing non-folder particle`,
|
|
8545
|
+
{
|
|
8546
|
+
[ownerIdField]: ownerId,
|
|
8547
|
+
elementId,
|
|
8548
|
+
field: "particleId",
|
|
8549
|
+
targetId: data.particleId,
|
|
8550
|
+
},
|
|
8551
|
+
);
|
|
8552
|
+
}
|
|
8553
|
+
}
|
|
8554
|
+
|
|
8133
8555
|
if (data.imageId !== undefined) {
|
|
8134
8556
|
const image = state.images.items[data.imageId];
|
|
8135
8557
|
if (!isPlainObject(image) || image.type === "folder") {
|
|
@@ -11467,6 +11889,9 @@ const COMMAND_DEFINITIONS = [
|
|
|
11467
11889
|
if (payload.data.fileSize !== undefined) {
|
|
11468
11890
|
nextVideo.fileSize = payload.data.fileSize;
|
|
11469
11891
|
}
|
|
11892
|
+
if (payload.data.duration !== undefined) {
|
|
11893
|
+
nextVideo.duration = payload.data.duration;
|
|
11894
|
+
}
|
|
11470
11895
|
if (payload.data.width !== undefined) {
|
|
11471
11896
|
nextVideo.width = payload.data.width;
|
|
11472
11897
|
}
|
|
@@ -11535,6 +11960,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
11535
11960
|
payload.data.thumbnailFileId !== undefined ||
|
|
11536
11961
|
payload.data.fileType !== undefined ||
|
|
11537
11962
|
payload.data.fileSize !== undefined ||
|
|
11963
|
+
payload.data.duration !== undefined ||
|
|
11538
11964
|
payload.data.width !== undefined ||
|
|
11539
11965
|
payload.data.height !== undefined)
|
|
11540
11966
|
) {
|
|
@@ -12950,6 +13376,63 @@ const COMMAND_DEFINITIONS = [
|
|
|
12950
13376
|
return state;
|
|
12951
13377
|
},
|
|
12952
13378
|
},
|
|
13379
|
+
...createFolderedCollectionCommandDefinitions({
|
|
13380
|
+
familyName: "particle",
|
|
13381
|
+
collectionKey: "particles",
|
|
13382
|
+
idField: "particleId",
|
|
13383
|
+
itemLabel: "particle item",
|
|
13384
|
+
createDataValidator: validateParticleCreateData,
|
|
13385
|
+
updateDataValidator: validateParticleUpdateData,
|
|
13386
|
+
createItem: ({ payload }) => {
|
|
13387
|
+
const item = {
|
|
13388
|
+
id: payload.particleId,
|
|
13389
|
+
type: payload.data.type,
|
|
13390
|
+
name: payload.data.name,
|
|
13391
|
+
};
|
|
13392
|
+
|
|
13393
|
+
if (payload.data.description !== undefined) {
|
|
13394
|
+
item.description = payload.data.description;
|
|
13395
|
+
}
|
|
13396
|
+
|
|
13397
|
+
if (payload.data.type !== "particle") {
|
|
13398
|
+
return item;
|
|
13399
|
+
}
|
|
13400
|
+
|
|
13401
|
+
item.width = payload.data.width;
|
|
13402
|
+
item.height = payload.data.height;
|
|
13403
|
+
item.modules = structuredClone(payload.data.modules);
|
|
13404
|
+
|
|
13405
|
+
if (payload.data.seed !== undefined && payload.data.seed !== null) {
|
|
13406
|
+
item.seed = payload.data.seed;
|
|
13407
|
+
}
|
|
13408
|
+
|
|
13409
|
+
return item;
|
|
13410
|
+
},
|
|
13411
|
+
updateItem: ({ currentItem, payload }) => {
|
|
13412
|
+
const nextItem = {
|
|
13413
|
+
...structuredClone(currentItem),
|
|
13414
|
+
...structuredClone(payload.data),
|
|
13415
|
+
};
|
|
13416
|
+
|
|
13417
|
+
if (payload.data.seed === null) {
|
|
13418
|
+
delete nextItem.seed;
|
|
13419
|
+
}
|
|
13420
|
+
|
|
13421
|
+
return nextItem;
|
|
13422
|
+
},
|
|
13423
|
+
validateUpdateState: ({ payload, currentItem }) => {
|
|
13424
|
+
if (
|
|
13425
|
+
currentItem.type === "folder" &&
|
|
13426
|
+
Object.keys(payload.data).some(
|
|
13427
|
+
(key) => key !== "name" && key !== "description",
|
|
13428
|
+
)
|
|
13429
|
+
) {
|
|
13430
|
+
return invalidPrecondition(
|
|
13431
|
+
"folder particle items cannot update particle fields",
|
|
13432
|
+
);
|
|
13433
|
+
}
|
|
13434
|
+
},
|
|
13435
|
+
}),
|
|
12953
13436
|
...createFolderedCollectionCommandDefinitions({
|
|
12954
13437
|
familyName: "transform",
|
|
12955
13438
|
collectionKey: "transforms",
|
|
@@ -14960,7 +15443,8 @@ export const processCommand = ({ state, command }) => {
|
|
|
14960
15443
|
const shouldMaterializeNormalizedState =
|
|
14961
15444
|
typeof command?.type === "string" &&
|
|
14962
15445
|
(command.type.startsWith("control.") ||
|
|
14963
|
-
command.type.startsWith("spritesheet.")
|
|
15446
|
+
command.type.startsWith("spritesheet.") ||
|
|
15447
|
+
command.type.startsWith("particle."));
|
|
14964
15448
|
|
|
14965
15449
|
if (!isPlainObject(command)) {
|
|
14966
15450
|
return invalidPrecondition("command must be an object");
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const RUNTIME_FIELD_IDS = Object.freeze([
|
|
2
|
+
"dialogueTextSpeed",
|
|
3
|
+
"autoForwardDelay",
|
|
4
|
+
"skipUnseenText",
|
|
5
|
+
"skipTransitionsAndAnimations",
|
|
6
|
+
"soundVolume",
|
|
7
|
+
"musicVolume",
|
|
8
|
+
"muteAll",
|
|
9
|
+
"saveLoadPagination",
|
|
10
|
+
"menuPage",
|
|
11
|
+
"menuEntryPoint",
|
|
12
|
+
"autoMode",
|
|
13
|
+
"skipMode",
|
|
14
|
+
"dialogueUIHidden",
|
|
15
|
+
"isLineCompleted",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const RUNTIME_FIELD_ID_SET = new Set(RUNTIME_FIELD_IDS);
|
|
19
|
+
|
|
20
|
+
export const isRuntimeFieldId = (value) => {
|
|
21
|
+
return typeof value === "string" && RUNTIME_FIELD_ID_SET.has(value);
|
|
22
|
+
};
|
package/src/systemVariables.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export const SYSTEM_VARIABLE_GROUPS = Object.freeze([
|
|
2
|
-
{
|
|
3
|
-
id: "routeEngine",
|
|
4
|
-
name: "Route Engine",
|
|
5
|
-
variables: Object.freeze([
|
|
6
|
-
{
|
|
7
|
-
id: "_skipUnseenText",
|
|
8
|
-
name: "Skip Unseen Text",
|
|
9
|
-
scope: "global-device",
|
|
10
|
-
type: "boolean",
|
|
11
|
-
default: false,
|
|
12
|
-
description:
|
|
13
|
-
"When enabled, skip mode can continue through lines the player has not viewed yet.",
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
id: "_dialogueTextSpeed",
|
|
17
|
-
name: "Dialogue Text Speed",
|
|
18
|
-
scope: "global-device",
|
|
19
|
-
type: "number",
|
|
20
|
-
default: 50,
|
|
21
|
-
description:
|
|
22
|
-
"Controls the default dialogue text speed stored for this device.",
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: "_currentSaveLoadPagination",
|
|
26
|
-
name: "Current Save/Load Pagination",
|
|
27
|
-
scope: "context",
|
|
28
|
-
type: "number",
|
|
29
|
-
default: 0,
|
|
30
|
-
description:
|
|
31
|
-
"The current save/load pagination index. Resolves to 0, 1, 2, 3, and so on for the active save/load page.",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
id: "_currentMenuPage",
|
|
35
|
-
name: "Current Menu Page",
|
|
36
|
-
scope: "context",
|
|
37
|
-
type: "string",
|
|
38
|
-
default: "",
|
|
39
|
-
description:
|
|
40
|
-
"The current menu page id for the active UI flow. Typical values include options, save, load, and history.",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "_menuEntryPoint",
|
|
44
|
-
name: "Menu Entry Point",
|
|
45
|
-
scope: "context",
|
|
46
|
-
type: "string",
|
|
47
|
-
default: "",
|
|
48
|
-
description:
|
|
49
|
-
"Indicates how the current menu flow was opened. Typical values include title and read.",
|
|
50
|
-
},
|
|
51
|
-
]),
|
|
52
|
-
},
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
|
-
export const SYSTEM_VARIABLE_IDS = Object.freeze(
|
|
56
|
-
SYSTEM_VARIABLE_GROUPS.flatMap((group) =>
|
|
57
|
-
(group.variables || []).map((variable) => variable.id),
|
|
58
|
-
),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const SYSTEM_VARIABLE_ID_SET = new Set(SYSTEM_VARIABLE_IDS);
|
|
62
|
-
|
|
63
|
-
export const isSystemVariableId = (value) => {
|
|
64
|
-
return typeof value === "string" && SYSTEM_VARIABLE_ID_SET.has(value);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const getSystemVariableDefinitions = () => {
|
|
68
|
-
return SYSTEM_VARIABLE_GROUPS.flatMap((group) => group.variables || []);
|
|
69
|
-
};
|