@routevn/creator-model 1.1.10 → 1.1.12
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/README.md +20 -0
- package/package.json +5 -2
- package/src/model.js +28 -15
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@ Shared RouteVN domain model package.
|
|
|
5
5
|
Repo rules and contribution expectations are in
|
|
6
6
|
[GUIDELINES.md](./GUIDELINES.md).
|
|
7
7
|
|
|
8
|
+
Schema compatibility maintenance rules are in
|
|
9
|
+
[docs/schema-compatibility.md](./docs/schema-compatibility.md).
|
|
10
|
+
|
|
8
11
|
This repo is intended to be the single source of truth for:
|
|
9
12
|
|
|
10
13
|
- state validation
|
|
@@ -79,6 +82,9 @@ Design rules:
|
|
|
79
82
|
- pure functions whenever possible
|
|
80
83
|
- command payload shape is validated separately from state-aware preconditions
|
|
81
84
|
- `SCHEMA_VERSION` is the source of truth for persisted command schema versioning
|
|
85
|
+
- `SCHEMA_VERSION` must stay aligned with the minor version from `package.json`
|
|
86
|
+
- patch releases must not change persisted schema compatibility
|
|
87
|
+
- `bun run test:compat` is the required compatibility gate for model changes
|
|
82
88
|
- `processCommand()` is the authoritative state transition
|
|
83
89
|
- model state should contain project-owned runtime data only
|
|
84
90
|
- app-owned metadata like project id, name, and description should stay out of
|
|
@@ -190,6 +196,20 @@ YAML Puty specs use [tests/support/putyApi.js](./tests/support/putyApi.js) as a
|
|
|
190
196
|
small adapter so the declarative `throws:` assertions can stay concise while the
|
|
191
197
|
real public API returns `{ valid: ... }` result objects.
|
|
192
198
|
|
|
199
|
+
Compatibility fixtures live under `tests/compat/schema-<n>/`.
|
|
200
|
+
|
|
201
|
+
- `payloads/` fixtures are frozen command payload shapes for that schema version
|
|
202
|
+
- `states/` fixtures are frozen persisted-state snapshots for that schema version
|
|
203
|
+
- `streams/` fixtures are frozen command sequences for that schema version
|
|
204
|
+
- current tests must continue to validate/replay every archived compatibility
|
|
205
|
+
fixture from the same or older schema versions
|
|
206
|
+
- current schema payload coverage must include `minimal.yaml` and `full.yaml` for
|
|
207
|
+
every public command type
|
|
208
|
+
|
|
209
|
+
See also:
|
|
210
|
+
|
|
211
|
+
- [docs/schema-compatibility.md](./docs/schema-compatibility.md)
|
|
212
|
+
|
|
193
213
|
## Current Scope
|
|
194
214
|
|
|
195
215
|
Currently implemented command types:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routevn/creator-model",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,10 +27,13 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"test": "bunx vitest run",
|
|
30
|
+
"test:compat": "bunx vitest run tests/compatibility-fixtures.test.js",
|
|
30
31
|
"test:watch": "bunx vitest",
|
|
31
|
-
"bench": "bun scripts/benchmark-process-command.js"
|
|
32
|
+
"bench": "bun scripts/benchmark-process-command.js",
|
|
33
|
+
"generate:compat-fixtures": "node scripts/generate-compat-fixtures.js"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
36
|
+
"js-yaml": "^4.1.0",
|
|
34
37
|
"puty": "0.1.2",
|
|
35
38
|
"vitest": "^3.2.1"
|
|
36
39
|
}
|
package/src/model.js
CHANGED
|
@@ -145,7 +145,7 @@ const LAYOUT_ELEMENT_BASE_TYPES = [
|
|
|
145
145
|
"container-ref-confirm-dialog-ok",
|
|
146
146
|
"container-ref-confirm-dialog-cancel",
|
|
147
147
|
];
|
|
148
|
-
export const SCHEMA_VERSION =
|
|
148
|
+
export const SCHEMA_VERSION = 1;
|
|
149
149
|
const LAYOUT_CONTAINER_ELEMENT_TYPES = [
|
|
150
150
|
"folder",
|
|
151
151
|
"container",
|
|
@@ -1357,30 +1357,21 @@ const validateMaskDefinition = ({ mask, path, errorFactory }) => {
|
|
|
1357
1357
|
}
|
|
1358
1358
|
}
|
|
1359
1359
|
|
|
1360
|
-
if (
|
|
1361
|
-
item.texture !== undefined &&
|
|
1362
|
-
!isNonEmptyString(item.texture)
|
|
1363
|
-
) {
|
|
1360
|
+
if (item.texture !== undefined && !isNonEmptyString(item.texture)) {
|
|
1364
1361
|
return invalidFromErrorFactory(
|
|
1365
1362
|
errorFactory,
|
|
1366
1363
|
`${itemPath}.texture must be a non-empty string when provided`,
|
|
1367
1364
|
);
|
|
1368
1365
|
}
|
|
1369
1366
|
|
|
1370
|
-
if (
|
|
1371
|
-
item.imageId !== undefined &&
|
|
1372
|
-
!isNonEmptyString(item.imageId)
|
|
1373
|
-
) {
|
|
1367
|
+
if (item.imageId !== undefined && !isNonEmptyString(item.imageId)) {
|
|
1374
1368
|
return invalidFromErrorFactory(
|
|
1375
1369
|
errorFactory,
|
|
1376
1370
|
`${itemPath}.imageId must be a non-empty string when provided`,
|
|
1377
1371
|
);
|
|
1378
1372
|
}
|
|
1379
1373
|
|
|
1380
|
-
if (
|
|
1381
|
-
!isNonEmptyString(item.texture) &&
|
|
1382
|
-
!isNonEmptyString(item.imageId)
|
|
1383
|
-
) {
|
|
1374
|
+
if (!isNonEmptyString(item.texture) && !isNonEmptyString(item.imageId)) {
|
|
1384
1375
|
return invalidFromErrorFactory(
|
|
1385
1376
|
errorFactory,
|
|
1386
1377
|
`${itemPath} must define texture or imageId`,
|
|
@@ -1987,7 +1978,15 @@ const validateVariableItems = ({ items, path, errorFactory }) => {
|
|
|
1987
1978
|
allowedKeys:
|
|
1988
1979
|
variableType === "folder"
|
|
1989
1980
|
? ["id", "type", "name", "description"]
|
|
1990
|
-
: [
|
|
1981
|
+
: [
|
|
1982
|
+
"id",
|
|
1983
|
+
"type",
|
|
1984
|
+
"name",
|
|
1985
|
+
"description",
|
|
1986
|
+
"scope",
|
|
1987
|
+
"default",
|
|
1988
|
+
"value",
|
|
1989
|
+
],
|
|
1991
1990
|
path: itemPath,
|
|
1992
1991
|
errorFactory,
|
|
1993
1992
|
});
|
|
@@ -2474,6 +2473,7 @@ const validateLayoutElementData = ({
|
|
|
2474
2473
|
"y",
|
|
2475
2474
|
"width",
|
|
2476
2475
|
"height",
|
|
2476
|
+
"aspectRatioLock",
|
|
2477
2477
|
"anchorX",
|
|
2478
2478
|
"anchorY",
|
|
2479
2479
|
"scaleX",
|
|
@@ -2574,6 +2574,16 @@ const validateLayoutElementData = ({
|
|
|
2574
2574
|
}
|
|
2575
2575
|
}
|
|
2576
2576
|
|
|
2577
|
+
if (
|
|
2578
|
+
data.aspectRatioLock !== undefined &&
|
|
2579
|
+
(!isFiniteNumber(data.aspectRatioLock) || data.aspectRatioLock <= 0)
|
|
2580
|
+
) {
|
|
2581
|
+
return invalidFromErrorFactory(
|
|
2582
|
+
errorFactory,
|
|
2583
|
+
`${path}.aspectRatioLock must be a finite number greater than 0 when provided`,
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2577
2587
|
if (
|
|
2578
2588
|
data.initialValue !== undefined &&
|
|
2579
2589
|
!isFiniteNumber(data.initialValue) &&
|
|
@@ -2959,6 +2969,7 @@ const validateLayoutElementItems = ({ items, path, errorFactory }) => {
|
|
|
2959
2969
|
"y",
|
|
2960
2970
|
"width",
|
|
2961
2971
|
"height",
|
|
2972
|
+
"aspectRatioLock",
|
|
2962
2973
|
"anchorX",
|
|
2963
2974
|
"anchorY",
|
|
2964
2975
|
"scaleX",
|
|
@@ -4490,7 +4501,9 @@ export const assertInvariants = ({ state }) => {
|
|
|
4490
4501
|
}
|
|
4491
4502
|
}
|
|
4492
4503
|
|
|
4493
|
-
for (const [animationId, animation] of Object.entries(
|
|
4504
|
+
for (const [animationId, animation] of Object.entries(
|
|
4505
|
+
state.animations.items,
|
|
4506
|
+
)) {
|
|
4494
4507
|
if (animation.type !== "animation") {
|
|
4495
4508
|
continue;
|
|
4496
4509
|
}
|