@routevn/creator-model 1.2.0 → 1.2.2
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 +476 -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
|
|
|
@@ -2224,6 +2229,141 @@ const validateTransformItems = ({ items, path, errorFactory }) => {
|
|
|
2224
2229
|
}
|
|
2225
2230
|
};
|
|
2226
2231
|
|
|
2232
|
+
const validateParticleModules = ({ modules, path, errorFactory }) => {
|
|
2233
|
+
if (!isPlainObject(modules)) {
|
|
2234
|
+
return invalidFromErrorFactory(errorFactory, `${path} must be an object`);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
if (!isPlainObject(modules.emission)) {
|
|
2238
|
+
return invalidFromErrorFactory(
|
|
2239
|
+
errorFactory,
|
|
2240
|
+
`${path}.emission must be an object`,
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
if (!isPlainObject(modules.appearance)) {
|
|
2245
|
+
return invalidFromErrorFactory(
|
|
2246
|
+
errorFactory,
|
|
2247
|
+
`${path}.appearance must be an object`,
|
|
2248
|
+
);
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
if (modules.movement !== undefined && !isPlainObject(modules.movement)) {
|
|
2252
|
+
return invalidFromErrorFactory(
|
|
2253
|
+
errorFactory,
|
|
2254
|
+
`${path}.movement must be an object when provided`,
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
if (modules.bounds !== undefined && !isPlainObject(modules.bounds)) {
|
|
2259
|
+
return invalidFromErrorFactory(
|
|
2260
|
+
errorFactory,
|
|
2261
|
+
`${path}.bounds must be an object when provided`,
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
|
|
2266
|
+
const validateParticleItems = ({ items, path, errorFactory }) => {
|
|
2267
|
+
for (const [itemId, item] of Object.entries(items)) {
|
|
2268
|
+
const itemPath = `${path}.${itemId}`;
|
|
2269
|
+
|
|
2270
|
+
if (item?.type !== "folder" && item?.type !== "particle") {
|
|
2271
|
+
return invalidFromErrorFactory(
|
|
2272
|
+
errorFactory,
|
|
2273
|
+
`${itemPath}.type must be 'folder' or 'particle'`,
|
|
2274
|
+
);
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
{
|
|
2278
|
+
const result = validateAllowedKeys({
|
|
2279
|
+
value: item,
|
|
2280
|
+
allowedKeys:
|
|
2281
|
+
item.type === "folder"
|
|
2282
|
+
? ["id", "type", "name", "description"]
|
|
2283
|
+
: [
|
|
2284
|
+
"id",
|
|
2285
|
+
"type",
|
|
2286
|
+
"name",
|
|
2287
|
+
"description",
|
|
2288
|
+
"width",
|
|
2289
|
+
"height",
|
|
2290
|
+
"seed",
|
|
2291
|
+
"modules",
|
|
2292
|
+
],
|
|
2293
|
+
path: itemPath,
|
|
2294
|
+
errorFactory,
|
|
2295
|
+
});
|
|
2296
|
+
if (result?.valid === false) {
|
|
2297
|
+
return result;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
if (!isNonEmptyString(item.id)) {
|
|
2302
|
+
return invalidFromErrorFactory(
|
|
2303
|
+
errorFactory,
|
|
2304
|
+
`${itemPath}.id must be a non-empty string`,
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
if (item.id !== itemId) {
|
|
2309
|
+
return invalidFromErrorFactory(
|
|
2310
|
+
errorFactory,
|
|
2311
|
+
`${itemPath}.id must match item key '${itemId}'`,
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
if (!isNonEmptyString(item.name)) {
|
|
2316
|
+
return invalidFromErrorFactory(
|
|
2317
|
+
errorFactory,
|
|
2318
|
+
`${itemPath}.name must be a non-empty string`,
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
if (item.description !== undefined && !isString(item.description)) {
|
|
2323
|
+
return invalidFromErrorFactory(
|
|
2324
|
+
errorFactory,
|
|
2325
|
+
`${itemPath}.description must be a string when provided`,
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
if (item.type !== "particle") {
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
if (!isFiniteNumber(item.width) || item.width <= 0) {
|
|
2334
|
+
return invalidFromErrorFactory(
|
|
2335
|
+
errorFactory,
|
|
2336
|
+
`${itemPath}.width must be a positive finite number`,
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
if (!isFiniteNumber(item.height) || item.height <= 0) {
|
|
2341
|
+
return invalidFromErrorFactory(
|
|
2342
|
+
errorFactory,
|
|
2343
|
+
`${itemPath}.height must be a positive finite number`,
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
if (item.seed !== undefined && !isFiniteNumber(item.seed)) {
|
|
2348
|
+
return invalidFromErrorFactory(
|
|
2349
|
+
errorFactory,
|
|
2350
|
+
`${itemPath}.seed must be a finite number when provided`,
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
{
|
|
2355
|
+
const result = validateParticleModules({
|
|
2356
|
+
modules: item.modules,
|
|
2357
|
+
path: `${itemPath}.modules`,
|
|
2358
|
+
errorFactory,
|
|
2359
|
+
});
|
|
2360
|
+
if (result?.valid === false) {
|
|
2361
|
+
return result;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
|
|
2227
2367
|
const validateVariableTypedValue = ({
|
|
2228
2368
|
value,
|
|
2229
2369
|
variableType,
|
|
@@ -2316,7 +2456,7 @@ const validateVariableItems = ({ items, path, errorFactory }) => {
|
|
|
2316
2456
|
if (!VARIABLE_SCOPE_KEYS.includes(item.scope)) {
|
|
2317
2457
|
return invalidFromErrorFactory(
|
|
2318
2458
|
errorFactory,
|
|
2319
|
-
`${itemPath}.scope must be 'context', '
|
|
2459
|
+
`${itemPath}.scope must be 'context', 'device', or 'account'`,
|
|
2320
2460
|
);
|
|
2321
2461
|
}
|
|
2322
2462
|
|
|
@@ -2801,6 +2941,7 @@ const validateLayoutElementData = ({
|
|
|
2801
2941
|
"step",
|
|
2802
2942
|
"initialValue",
|
|
2803
2943
|
"variableId",
|
|
2944
|
+
"particleId",
|
|
2804
2945
|
"fragmentLayoutId",
|
|
2805
2946
|
"paginationMode",
|
|
2806
2947
|
"paginationVariableId",
|
|
@@ -2900,6 +3041,7 @@ const validateLayoutElementData = ({
|
|
|
2900
3041
|
"text",
|
|
2901
3042
|
"resourceId",
|
|
2902
3043
|
"animationName",
|
|
3044
|
+
"particleId",
|
|
2903
3045
|
"imageId",
|
|
2904
3046
|
"hoverImageId",
|
|
2905
3047
|
"clickImageId",
|
|
@@ -2948,6 +3090,18 @@ const validateLayoutElementData = ({
|
|
|
2948
3090
|
}
|
|
2949
3091
|
}
|
|
2950
3092
|
|
|
3093
|
+
if (data.type === "particle") {
|
|
3094
|
+
if (
|
|
3095
|
+
(!allowPartial || data.particleId !== undefined) &&
|
|
3096
|
+
!isNonEmptyString(data.particleId)
|
|
3097
|
+
) {
|
|
3098
|
+
return invalidFromErrorFactory(
|
|
3099
|
+
errorFactory,
|
|
3100
|
+
`${path}.particleId must be a non-empty string`,
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
|
|
2951
3105
|
if (data.conditionalOverrides !== undefined) {
|
|
2952
3106
|
if (!Array.isArray(data.conditionalOverrides)) {
|
|
2953
3107
|
return invalidFromErrorFactory(
|
|
@@ -3299,6 +3453,7 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
3299
3453
|
"revealEffect",
|
|
3300
3454
|
"resourceId",
|
|
3301
3455
|
"animationName",
|
|
3456
|
+
"particleId",
|
|
3302
3457
|
"imageId",
|
|
3303
3458
|
"hoverImageId",
|
|
3304
3459
|
"clickImageId",
|
|
@@ -4293,6 +4448,17 @@ const validateCollection = ({ collection, path }) => {
|
|
|
4293
4448
|
return result;
|
|
4294
4449
|
}
|
|
4295
4450
|
}
|
|
4451
|
+
} else if (path === "state.particles") {
|
|
4452
|
+
{
|
|
4453
|
+
const result = validateParticleItems({
|
|
4454
|
+
items: collection.items,
|
|
4455
|
+
path: `${path}.items`,
|
|
4456
|
+
errorFactory: createStateValidationError,
|
|
4457
|
+
});
|
|
4458
|
+
if (result?.valid === false) {
|
|
4459
|
+
return result;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4296
4462
|
} else if (path === "state.variables") {
|
|
4297
4463
|
{
|
|
4298
4464
|
const result = validateVariableItems({
|
|
@@ -4458,6 +4624,7 @@ const validateCollection = ({ collection, path }) => {
|
|
|
4458
4624
|
}
|
|
4459
4625
|
} else if (
|
|
4460
4626
|
path === "state.files" ||
|
|
4627
|
+
path === "state.particles" ||
|
|
4461
4628
|
path === "state.transforms" ||
|
|
4462
4629
|
path === "state.variables" ||
|
|
4463
4630
|
path === "state.textStyles" ||
|
|
@@ -5124,6 +5291,29 @@ export const assertInvariants = ({ state }) => {
|
|
|
5124
5291
|
return VALID_RESULT;
|
|
5125
5292
|
};
|
|
5126
5293
|
|
|
5294
|
+
const assertParticleReference = ({
|
|
5295
|
+
ownerIdField,
|
|
5296
|
+
ownerId,
|
|
5297
|
+
ownerLabel,
|
|
5298
|
+
elementId,
|
|
5299
|
+
targetId,
|
|
5300
|
+
}) => {
|
|
5301
|
+
const particle = state.particles?.items?.[targetId];
|
|
5302
|
+
if (!isPlainObject(particle) || particle.type === "folder") {
|
|
5303
|
+
return invalidInvariant(
|
|
5304
|
+
`${ownerLabel} element particleId must reference an existing non-folder particle`,
|
|
5305
|
+
{
|
|
5306
|
+
[ownerIdField]: ownerId,
|
|
5307
|
+
elementId,
|
|
5308
|
+
field: "particleId",
|
|
5309
|
+
targetId,
|
|
5310
|
+
},
|
|
5311
|
+
);
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5314
|
+
return VALID_RESULT;
|
|
5315
|
+
};
|
|
5316
|
+
|
|
5127
5317
|
const assertElementReferencesForCollection = ({
|
|
5128
5318
|
items,
|
|
5129
5319
|
ownerIdField,
|
|
@@ -5154,6 +5344,19 @@ export const assertInvariants = ({ state }) => {
|
|
|
5154
5344
|
}
|
|
5155
5345
|
}
|
|
5156
5346
|
|
|
5347
|
+
if (element.type === "particle" || element.particleId !== undefined) {
|
|
5348
|
+
const result = assertParticleReference({
|
|
5349
|
+
ownerIdField,
|
|
5350
|
+
ownerId,
|
|
5351
|
+
ownerLabel,
|
|
5352
|
+
elementId,
|
|
5353
|
+
targetId: element.particleId,
|
|
5354
|
+
});
|
|
5355
|
+
if (!result.valid) {
|
|
5356
|
+
return result;
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
|
|
5157
5360
|
for (const field of [
|
|
5158
5361
|
"imageId",
|
|
5159
5362
|
"hoverImageId",
|
|
@@ -7014,6 +7217,185 @@ const validateTransformCreateData = ({ data, errorFactory }) => {
|
|
|
7014
7217
|
}
|
|
7015
7218
|
};
|
|
7016
7219
|
|
|
7220
|
+
const validateParticleCreateData = ({ data, errorFactory }) => {
|
|
7221
|
+
if (!isPlainObject(data)) {
|
|
7222
|
+
return invalidFromErrorFactory(
|
|
7223
|
+
errorFactory,
|
|
7224
|
+
"payload.data must be an object",
|
|
7225
|
+
);
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
if (data.type !== "folder" && data.type !== "particle") {
|
|
7229
|
+
return invalidFromErrorFactory(
|
|
7230
|
+
errorFactory,
|
|
7231
|
+
"payload.data.type must be 'folder' or 'particle'",
|
|
7232
|
+
);
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
{
|
|
7236
|
+
const result = validateAllowedKeys({
|
|
7237
|
+
value: data,
|
|
7238
|
+
allowedKeys:
|
|
7239
|
+
data.type === "folder"
|
|
7240
|
+
? ["type", "name", "description"]
|
|
7241
|
+
: [
|
|
7242
|
+
"type",
|
|
7243
|
+
"name",
|
|
7244
|
+
"description",
|
|
7245
|
+
"width",
|
|
7246
|
+
"height",
|
|
7247
|
+
"seed",
|
|
7248
|
+
"modules",
|
|
7249
|
+
],
|
|
7250
|
+
path: "payload.data",
|
|
7251
|
+
errorFactory,
|
|
7252
|
+
});
|
|
7253
|
+
if (result?.valid === false) {
|
|
7254
|
+
return result;
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
if (!isNonEmptyString(data.name)) {
|
|
7259
|
+
return invalidFromErrorFactory(
|
|
7260
|
+
errorFactory,
|
|
7261
|
+
"payload.data.name must be a non-empty string",
|
|
7262
|
+
);
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
7266
|
+
return invalidFromErrorFactory(
|
|
7267
|
+
errorFactory,
|
|
7268
|
+
"payload.data.description must be a string when provided",
|
|
7269
|
+
);
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
if (data.type !== "particle") {
|
|
7273
|
+
return;
|
|
7274
|
+
}
|
|
7275
|
+
|
|
7276
|
+
if (!isFiniteNumber(data.width) || data.width <= 0) {
|
|
7277
|
+
return invalidFromErrorFactory(
|
|
7278
|
+
errorFactory,
|
|
7279
|
+
"payload.data.width must be a positive finite number",
|
|
7280
|
+
);
|
|
7281
|
+
}
|
|
7282
|
+
|
|
7283
|
+
if (!isFiniteNumber(data.height) || data.height <= 0) {
|
|
7284
|
+
return invalidFromErrorFactory(
|
|
7285
|
+
errorFactory,
|
|
7286
|
+
"payload.data.height must be a positive finite number",
|
|
7287
|
+
);
|
|
7288
|
+
}
|
|
7289
|
+
|
|
7290
|
+
if (
|
|
7291
|
+
data.seed !== undefined &&
|
|
7292
|
+
data.seed !== null &&
|
|
7293
|
+
!isFiniteNumber(data.seed)
|
|
7294
|
+
) {
|
|
7295
|
+
return invalidFromErrorFactory(
|
|
7296
|
+
errorFactory,
|
|
7297
|
+
"payload.data.seed must be a finite number when provided",
|
|
7298
|
+
);
|
|
7299
|
+
}
|
|
7300
|
+
|
|
7301
|
+
{
|
|
7302
|
+
const result = validateParticleModules({
|
|
7303
|
+
modules: data.modules,
|
|
7304
|
+
path: "payload.data.modules",
|
|
7305
|
+
errorFactory,
|
|
7306
|
+
});
|
|
7307
|
+
if (result?.valid === false) {
|
|
7308
|
+
return result;
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
};
|
|
7312
|
+
|
|
7313
|
+
const validateParticleUpdateData = ({ data, errorFactory }) => {
|
|
7314
|
+
{
|
|
7315
|
+
const result = validateAllowedKeys({
|
|
7316
|
+
value: data,
|
|
7317
|
+
allowedKeys: [
|
|
7318
|
+
"name",
|
|
7319
|
+
"description",
|
|
7320
|
+
"width",
|
|
7321
|
+
"height",
|
|
7322
|
+
"seed",
|
|
7323
|
+
"modules",
|
|
7324
|
+
],
|
|
7325
|
+
path: "payload.data",
|
|
7326
|
+
errorFactory,
|
|
7327
|
+
});
|
|
7328
|
+
if (result?.valid === false) {
|
|
7329
|
+
return result;
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
|
|
7333
|
+
if (Object.keys(data).length === 0) {
|
|
7334
|
+
return invalidFromErrorFactory(
|
|
7335
|
+
errorFactory,
|
|
7336
|
+
"payload.data must include at least one updatable field",
|
|
7337
|
+
);
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7340
|
+
if (data.name !== undefined && !isNonEmptyString(data.name)) {
|
|
7341
|
+
return invalidFromErrorFactory(
|
|
7342
|
+
errorFactory,
|
|
7343
|
+
"payload.data.name must be a non-empty string when provided",
|
|
7344
|
+
);
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7347
|
+
if (data.description !== undefined && !isString(data.description)) {
|
|
7348
|
+
return invalidFromErrorFactory(
|
|
7349
|
+
errorFactory,
|
|
7350
|
+
"payload.data.description must be a string when provided",
|
|
7351
|
+
);
|
|
7352
|
+
}
|
|
7353
|
+
|
|
7354
|
+
if (
|
|
7355
|
+
data.width !== undefined &&
|
|
7356
|
+
(!isFiniteNumber(data.width) || data.width <= 0)
|
|
7357
|
+
) {
|
|
7358
|
+
return invalidFromErrorFactory(
|
|
7359
|
+
errorFactory,
|
|
7360
|
+
"payload.data.width must be a positive finite number when provided",
|
|
7361
|
+
);
|
|
7362
|
+
}
|
|
7363
|
+
|
|
7364
|
+
if (
|
|
7365
|
+
data.height !== undefined &&
|
|
7366
|
+
(!isFiniteNumber(data.height) || data.height <= 0)
|
|
7367
|
+
) {
|
|
7368
|
+
return invalidFromErrorFactory(
|
|
7369
|
+
errorFactory,
|
|
7370
|
+
"payload.data.height must be a positive finite number when provided",
|
|
7371
|
+
);
|
|
7372
|
+
}
|
|
7373
|
+
|
|
7374
|
+
if (
|
|
7375
|
+
data.seed !== undefined &&
|
|
7376
|
+
data.seed !== null &&
|
|
7377
|
+
!isFiniteNumber(data.seed)
|
|
7378
|
+
) {
|
|
7379
|
+
return invalidFromErrorFactory(
|
|
7380
|
+
errorFactory,
|
|
7381
|
+
"payload.data.seed must be a finite number when provided",
|
|
7382
|
+
);
|
|
7383
|
+
}
|
|
7384
|
+
|
|
7385
|
+
if (data.modules !== undefined) {
|
|
7386
|
+
{
|
|
7387
|
+
const result = validateParticleModules({
|
|
7388
|
+
modules: data.modules,
|
|
7389
|
+
path: "payload.data.modules",
|
|
7390
|
+
errorFactory,
|
|
7391
|
+
});
|
|
7392
|
+
if (result?.valid === false) {
|
|
7393
|
+
return result;
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
}
|
|
7397
|
+
};
|
|
7398
|
+
|
|
7017
7399
|
const validateTransformUpdateData = ({ data, errorFactory }) => {
|
|
7018
7400
|
{
|
|
7019
7401
|
const result = validateAllowedKeys({
|
|
@@ -7124,7 +7506,7 @@ const validateVariableCreateData = ({ data, errorFactory }) => {
|
|
|
7124
7506
|
if (!VARIABLE_SCOPE_KEYS.includes(data.scope)) {
|
|
7125
7507
|
return invalidFromErrorFactory(
|
|
7126
7508
|
errorFactory,
|
|
7127
|
-
"payload.data.scope must be 'context', '
|
|
7509
|
+
"payload.data.scope must be 'context', 'device', or 'account'",
|
|
7128
7510
|
);
|
|
7129
7511
|
}
|
|
7130
7512
|
|
|
@@ -7190,7 +7572,7 @@ const validateVariableUpdateData = ({ data, errorFactory }) => {
|
|
|
7190
7572
|
if (data.scope !== undefined && !VARIABLE_SCOPE_KEYS.includes(data.scope)) {
|
|
7191
7573
|
return invalidFromErrorFactory(
|
|
7192
7574
|
errorFactory,
|
|
7193
|
-
"payload.data.scope must be 'context', '
|
|
7575
|
+
"payload.data.scope must be 'context', 'device', or 'account' when provided",
|
|
7194
7576
|
);
|
|
7195
7577
|
}
|
|
7196
7578
|
};
|
|
@@ -8130,6 +8512,22 @@ const validateVisualElementReferenceTargets = ({
|
|
|
8130
8512
|
}
|
|
8131
8513
|
}
|
|
8132
8514
|
|
|
8515
|
+
if (data.type === "particle" || data.particleId !== undefined) {
|
|
8516
|
+
const particle = state.particles?.items?.[data.particleId];
|
|
8517
|
+
if (!isPlainObject(particle) || particle.type === "folder") {
|
|
8518
|
+
return invalidFromErrorFactory(
|
|
8519
|
+
errorFactory,
|
|
8520
|
+
`${ownerLabel} element particleId must reference an existing non-folder particle`,
|
|
8521
|
+
{
|
|
8522
|
+
[ownerIdField]: ownerId,
|
|
8523
|
+
elementId,
|
|
8524
|
+
field: "particleId",
|
|
8525
|
+
targetId: data.particleId,
|
|
8526
|
+
},
|
|
8527
|
+
);
|
|
8528
|
+
}
|
|
8529
|
+
}
|
|
8530
|
+
|
|
8133
8531
|
if (data.imageId !== undefined) {
|
|
8134
8532
|
const image = state.images.items[data.imageId];
|
|
8135
8533
|
if (!isPlainObject(image) || image.type === "folder") {
|
|
@@ -12950,6 +13348,63 @@ const COMMAND_DEFINITIONS = [
|
|
|
12950
13348
|
return state;
|
|
12951
13349
|
},
|
|
12952
13350
|
},
|
|
13351
|
+
...createFolderedCollectionCommandDefinitions({
|
|
13352
|
+
familyName: "particle",
|
|
13353
|
+
collectionKey: "particles",
|
|
13354
|
+
idField: "particleId",
|
|
13355
|
+
itemLabel: "particle item",
|
|
13356
|
+
createDataValidator: validateParticleCreateData,
|
|
13357
|
+
updateDataValidator: validateParticleUpdateData,
|
|
13358
|
+
createItem: ({ payload }) => {
|
|
13359
|
+
const item = {
|
|
13360
|
+
id: payload.particleId,
|
|
13361
|
+
type: payload.data.type,
|
|
13362
|
+
name: payload.data.name,
|
|
13363
|
+
};
|
|
13364
|
+
|
|
13365
|
+
if (payload.data.description !== undefined) {
|
|
13366
|
+
item.description = payload.data.description;
|
|
13367
|
+
}
|
|
13368
|
+
|
|
13369
|
+
if (payload.data.type !== "particle") {
|
|
13370
|
+
return item;
|
|
13371
|
+
}
|
|
13372
|
+
|
|
13373
|
+
item.width = payload.data.width;
|
|
13374
|
+
item.height = payload.data.height;
|
|
13375
|
+
item.modules = structuredClone(payload.data.modules);
|
|
13376
|
+
|
|
13377
|
+
if (payload.data.seed !== undefined && payload.data.seed !== null) {
|
|
13378
|
+
item.seed = payload.data.seed;
|
|
13379
|
+
}
|
|
13380
|
+
|
|
13381
|
+
return item;
|
|
13382
|
+
},
|
|
13383
|
+
updateItem: ({ currentItem, payload }) => {
|
|
13384
|
+
const nextItem = {
|
|
13385
|
+
...structuredClone(currentItem),
|
|
13386
|
+
...structuredClone(payload.data),
|
|
13387
|
+
};
|
|
13388
|
+
|
|
13389
|
+
if (payload.data.seed === null) {
|
|
13390
|
+
delete nextItem.seed;
|
|
13391
|
+
}
|
|
13392
|
+
|
|
13393
|
+
return nextItem;
|
|
13394
|
+
},
|
|
13395
|
+
validateUpdateState: ({ payload, currentItem }) => {
|
|
13396
|
+
if (
|
|
13397
|
+
currentItem.type === "folder" &&
|
|
13398
|
+
Object.keys(payload.data).some(
|
|
13399
|
+
(key) => key !== "name" && key !== "description",
|
|
13400
|
+
)
|
|
13401
|
+
) {
|
|
13402
|
+
return invalidPrecondition(
|
|
13403
|
+
"folder particle items cannot update particle fields",
|
|
13404
|
+
);
|
|
13405
|
+
}
|
|
13406
|
+
},
|
|
13407
|
+
}),
|
|
12953
13408
|
...createFolderedCollectionCommandDefinitions({
|
|
12954
13409
|
familyName: "transform",
|
|
12955
13410
|
collectionKey: "transforms",
|
|
@@ -14960,7 +15415,8 @@ export const processCommand = ({ state, command }) => {
|
|
|
14960
15415
|
const shouldMaterializeNormalizedState =
|
|
14961
15416
|
typeof command?.type === "string" &&
|
|
14962
15417
|
(command.type.startsWith("control.") ||
|
|
14963
|
-
command.type.startsWith("spritesheet.")
|
|
15418
|
+
command.type.startsWith("spritesheet.") ||
|
|
15419
|
+
command.type.startsWith("particle."));
|
|
14964
15420
|
|
|
14965
15421
|
if (!isPlainObject(command)) {
|
|
14966
15422
|
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
|
-
};
|