@routevn/creator-model 1.12.0 → 1.12.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.
Files changed (3) hide show
  1. package/README.md +38 -0
  2. package/package.json +1 -1
  3. package/src/model.js +203 -46
package/README.md CHANGED
@@ -173,6 +173,40 @@ Computed definitions may use a simple `expr` or literal `value`, or ordered
173
173
  operator grammar, result types, concrete `variables.*`/`runtime.*` paths,
174
174
  declared variable references, and an acyclic computed dependency graph.
175
175
 
176
+ Computed definitions may also store examples. Example inputs mirror the
177
+ evaluation context, while calculated results remain derived and are not
178
+ persisted:
179
+
180
+ ```js
181
+ {
182
+ computed: {
183
+ expr: {
184
+ mul: [
185
+ {
186
+ div: [
187
+ { var: "variables.hp" },
188
+ { var: "variables.maxHp" },
189
+ ],
190
+ },
191
+ 100,
192
+ ],
193
+ },
194
+ examples: [
195
+ {
196
+ id: "example-low-health",
197
+ name: "Low health",
198
+ input: {
199
+ variables: {
200
+ hp: 40,
201
+ maxHp: 80,
202
+ },
203
+ },
204
+ },
205
+ ],
206
+ },
207
+ }
208
+ ```
209
+
176
210
  ## File Structure
177
211
 
178
212
  ```text
@@ -294,6 +328,10 @@ Current animation update tween properties support either:
294
328
  - `keyframes`
295
329
  - `auto: { duration, easing }`
296
330
 
331
+ Transition animation `mask` accepts one mask object or a non-empty ordered
332
+ array. Each mask may define a non-negative safe-integer `delay` in
333
+ milliseconds.
334
+
297
335
  ## Current Scope
298
336
 
299
337
  Currently implemented command types:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routevn/creator-model",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/model.js CHANGED
@@ -1930,6 +1930,7 @@ const validateMaskDefinition = ({ mask, path, errorFactory }) => {
1930
1930
  "softness",
1931
1931
  "invert",
1932
1932
  "sample",
1933
+ "delay",
1933
1934
  "progress",
1934
1935
  "progressDuration",
1935
1936
  "progressEasing",
@@ -2108,6 +2109,16 @@ const validateMaskDefinition = ({ mask, path, errorFactory }) => {
2108
2109
  );
2109
2110
  }
2110
2111
 
2112
+ if (
2113
+ mask.delay !== undefined &&
2114
+ (!Number.isSafeInteger(mask.delay) || mask.delay < 0)
2115
+ ) {
2116
+ return invalidFromErrorFactory(
2117
+ errorFactory,
2118
+ `${path}.delay must be a non-negative safe integer when provided`,
2119
+ );
2120
+ }
2121
+
2111
2122
  if (mask.progress !== undefined) {
2112
2123
  {
2113
2124
  const result = validateTweenProperty({
@@ -2171,6 +2182,30 @@ const validateMaskDefinition = ({ mask, path, errorFactory }) => {
2171
2182
  }
2172
2183
  };
2173
2184
 
2185
+ const validateAnimationMasks = ({ mask, path, errorFactory }) => {
2186
+ if (!Array.isArray(mask)) {
2187
+ return validateMaskDefinition({ mask, path, errorFactory });
2188
+ }
2189
+
2190
+ if (mask.length === 0) {
2191
+ return invalidFromErrorFactory(
2192
+ errorFactory,
2193
+ `${path} must be a non-empty array when provided`,
2194
+ );
2195
+ }
2196
+
2197
+ for (const [index, maskDefinition] of mask.entries()) {
2198
+ const result = validateMaskDefinition({
2199
+ mask: maskDefinition,
2200
+ path: `${path}[${index}]`,
2201
+ errorFactory,
2202
+ });
2203
+ if (result?.valid === false) {
2204
+ return result;
2205
+ }
2206
+ }
2207
+ };
2208
+
2174
2209
  const validateAnimationDefinition = ({ animation, path, errorFactory }) => {
2175
2210
  {
2176
2211
  const result = validateAllowedKeys({
@@ -2276,7 +2311,7 @@ const validateAnimationDefinition = ({ animation, path, errorFactory }) => {
2276
2311
 
2277
2312
  if (animation.mask !== undefined) {
2278
2313
  {
2279
- const result = validateMaskDefinition({
2314
+ const result = validateAnimationMasks({
2280
2315
  mask: animation.mask,
2281
2316
  path: `${path}.mask`,
2282
2317
  errorFactory,
@@ -3754,6 +3789,99 @@ const validateComputedResultConfig = ({
3754
3789
  return VALID_RESULT;
3755
3790
  };
3756
3791
 
3792
+ const validateComputedExamples = ({ examples, path, errorFactory }) => {
3793
+ if (!Array.isArray(examples)) {
3794
+ return invalidFromErrorFactory(errorFactory, `${path} must be an array`);
3795
+ }
3796
+
3797
+ const exampleIds = new Set();
3798
+ for (const [index, example] of examples.entries()) {
3799
+ const examplePath = `${path}[${index}]`;
3800
+ {
3801
+ const result = validateAllowedKeys({
3802
+ value: example,
3803
+ allowedKeys: ["id", "name", "input"],
3804
+ path: examplePath,
3805
+ errorFactory,
3806
+ });
3807
+ if (result?.valid === false) {
3808
+ return result;
3809
+ }
3810
+ }
3811
+
3812
+ if (!isNonEmptyString(example.id)) {
3813
+ return invalidFromErrorFactory(
3814
+ errorFactory,
3815
+ `${examplePath}.id must be a non-empty string`,
3816
+ );
3817
+ }
3818
+ if (exampleIds.has(example.id)) {
3819
+ return invalidFromErrorFactory(
3820
+ errorFactory,
3821
+ `${examplePath}.id must be unique within examples`,
3822
+ );
3823
+ }
3824
+ exampleIds.add(example.id);
3825
+
3826
+ if (Object.hasOwn(example, "name") && !isNonEmptyString(example.name)) {
3827
+ return invalidFromErrorFactory(
3828
+ errorFactory,
3829
+ `${examplePath}.name must be a non-empty string`,
3830
+ );
3831
+ }
3832
+
3833
+ if (!Object.hasOwn(example, "input")) {
3834
+ return invalidFromErrorFactory(
3835
+ errorFactory,
3836
+ `${examplePath}.input is required`,
3837
+ );
3838
+ }
3839
+ if (
3840
+ !isPlainObject(example.input) ||
3841
+ ![Object.prototype, null].includes(Object.getPrototypeOf(example.input))
3842
+ ) {
3843
+ return invalidFromErrorFactory(
3844
+ errorFactory,
3845
+ `${examplePath}.input must be an object`,
3846
+ );
3847
+ }
3848
+ {
3849
+ const result = validateAllowedKeys({
3850
+ value: example.input,
3851
+ allowedKeys: ["variables", "runtime"],
3852
+ path: `${examplePath}.input`,
3853
+ errorFactory,
3854
+ });
3855
+ if (result?.valid === false) {
3856
+ return result;
3857
+ }
3858
+ }
3859
+
3860
+ for (const namespace of ["variables", "runtime"]) {
3861
+ if (!Object.hasOwn(example.input, namespace)) {
3862
+ continue;
3863
+ }
3864
+ const namespacePath = `${examplePath}.input.${namespace}`;
3865
+ if (!isPlainObject(example.input[namespace])) {
3866
+ return invalidFromErrorFactory(
3867
+ errorFactory,
3868
+ `${namespacePath} must be an object`,
3869
+ );
3870
+ }
3871
+ const result = validateComputedDataValue({
3872
+ value: example.input[namespace],
3873
+ path: namespacePath,
3874
+ errorFactory,
3875
+ });
3876
+ if (result?.valid === false) {
3877
+ return result;
3878
+ }
3879
+ }
3880
+ }
3881
+
3882
+ return VALID_RESULT;
3883
+ };
3884
+
3757
3885
  const validateVariableComputedConfig = ({
3758
3886
  computed,
3759
3887
  variableType,
@@ -3766,11 +3894,22 @@ const validateVariableComputedConfig = ({
3766
3894
  return invalidFromErrorFactory(errorFactory, `${path} must be an object`);
3767
3895
  }
3768
3896
 
3897
+ if (Object.hasOwn(computed, "examples")) {
3898
+ const result = validateComputedExamples({
3899
+ examples: computed.examples,
3900
+ path: `${path}.examples`,
3901
+ errorFactory,
3902
+ });
3903
+ if (result?.valid === false) {
3904
+ return result;
3905
+ }
3906
+ }
3907
+
3769
3908
  if (Object.hasOwn(computed, "branches")) {
3770
3909
  {
3771
3910
  const result = validateAllowedKeys({
3772
3911
  value: computed,
3773
- allowedKeys: ["branches", "default"],
3912
+ allowedKeys: ["branches", "default", "examples"],
3774
3913
  path,
3775
3914
  errorFactory,
3776
3915
  });
@@ -3846,6 +3985,7 @@ const validateVariableComputedConfig = ({
3846
3985
  errorFactory,
3847
3986
  variables,
3848
3987
  dependencies,
3988
+ allowedKeys: ["expr", "value", "examples"],
3849
3989
  });
3850
3990
  };
3851
3991
 
@@ -8119,43 +8259,41 @@ const validateAnimationMaskImageReferences = ({
8119
8259
  details = {},
8120
8260
  errorFactory = createPreconditionValidationError,
8121
8261
  }) => {
8122
- if (
8123
- !isPlainObject(animation) ||
8124
- animation.type !== "transition" ||
8125
- !isPlainObject(animation.mask)
8126
- ) {
8262
+ if (!isPlainObject(animation) || animation.type !== "transition") {
8127
8263
  return VALID_RESULT;
8128
8264
  }
8129
8265
 
8130
- const { mask } = animation;
8266
+ const maskDefinitions = Array.isArray(animation.mask)
8267
+ ? animation.mask.map((mask, index) => ({
8268
+ mask,
8269
+ path: `${path}.mask[${index}]`,
8270
+ fieldPrefix: `mask[${index}].`,
8271
+ }))
8272
+ : isPlainObject(animation.mask)
8273
+ ? [
8274
+ {
8275
+ mask: animation.mask,
8276
+ path: `${path}.mask`,
8277
+ fieldPrefix: "",
8278
+ },
8279
+ ]
8280
+ : [];
8131
8281
 
8132
- if (mask.imageId !== undefined) {
8133
- const result = validateImageReference({
8134
- state,
8135
- imageId: mask.imageId,
8136
- path: `${path}.mask.imageId`,
8137
- details: {
8138
- ...details,
8139
- field: "imageId",
8140
- imageId: mask.imageId,
8141
- },
8142
- errorFactory,
8143
- });
8144
- if (!result.valid) {
8145
- return result;
8282
+ for (const maskDefinition of maskDefinitions) {
8283
+ const { mask, path: maskPath, fieldPrefix } = maskDefinition;
8284
+ if (!isPlainObject(mask)) {
8285
+ continue;
8146
8286
  }
8147
- }
8148
8287
 
8149
- if (Array.isArray(mask.imageIds)) {
8150
- for (const [index, imageId] of mask.imageIds.entries()) {
8288
+ if (mask.imageId !== undefined) {
8151
8289
  const result = validateImageReference({
8152
8290
  state,
8153
- imageId,
8154
- path: `${path}.mask.imageIds[${index}]`,
8291
+ imageId: mask.imageId,
8292
+ path: `${maskPath}.imageId`,
8155
8293
  details: {
8156
8294
  ...details,
8157
- field: `imageIds[${index}]`,
8158
- imageId,
8295
+ field: `${fieldPrefix}imageId`,
8296
+ imageId: mask.imageId,
8159
8297
  },
8160
8298
  errorFactory,
8161
8299
  });
@@ -8163,27 +8301,46 @@ const validateAnimationMaskImageReferences = ({
8163
8301
  return result;
8164
8302
  }
8165
8303
  }
8166
- }
8167
8304
 
8168
- if (Array.isArray(mask.items)) {
8169
- for (const [index, item] of mask.items.entries()) {
8170
- if (item?.imageId === undefined) {
8171
- continue;
8305
+ if (Array.isArray(mask.imageIds)) {
8306
+ for (const [index, imageId] of mask.imageIds.entries()) {
8307
+ const result = validateImageReference({
8308
+ state,
8309
+ imageId,
8310
+ path: `${maskPath}.imageIds[${index}]`,
8311
+ details: {
8312
+ ...details,
8313
+ field: `${fieldPrefix}imageIds[${index}]`,
8314
+ imageId,
8315
+ },
8316
+ errorFactory,
8317
+ });
8318
+ if (!result.valid) {
8319
+ return result;
8320
+ }
8172
8321
  }
8322
+ }
8173
8323
 
8174
- const result = validateImageReference({
8175
- state,
8176
- imageId: item.imageId,
8177
- path: `${path}.mask.items[${index}].imageId`,
8178
- details: {
8179
- ...details,
8180
- field: `items[${index}].imageId`,
8324
+ if (Array.isArray(mask.items)) {
8325
+ for (const [index, item] of mask.items.entries()) {
8326
+ if (item?.imageId === undefined) {
8327
+ continue;
8328
+ }
8329
+
8330
+ const result = validateImageReference({
8331
+ state,
8181
8332
  imageId: item.imageId,
8182
- },
8183
- errorFactory,
8184
- });
8185
- if (!result.valid) {
8186
- return result;
8333
+ path: `${maskPath}.items[${index}].imageId`,
8334
+ details: {
8335
+ ...details,
8336
+ field: `${fieldPrefix}items[${index}].imageId`,
8337
+ imageId: item.imageId,
8338
+ },
8339
+ errorFactory,
8340
+ });
8341
+ if (!result.valid) {
8342
+ return result;
8343
+ }
8187
8344
  }
8188
8345
  }
8189
8346
  }