@wmfs/statelint 1.23.0 → 1.25.0

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 (53) hide show
  1. package/.releaserc.json +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +1 -1
  4. package/lib/schema/StateMachine.j2119 +84 -14
  5. package/lib/statelint/state_node.js +113 -25
  6. package/package.json +9 -9
  7. package/test/fixtures/backoff-with-invalid-jitter-on-retry.json +17 -0
  8. package/test/fixtures/backoff-with-jitter-on-retry.json +17 -0
  9. package/test/fixtures/choice-rule-with-comment.json +19 -0
  10. package/test/fixtures/choice-with-context-object.json +22 -0
  11. package/test/fixtures/choice-with-resultselector.json +23 -0
  12. package/test/fixtures/fail-with-error-and-cause-path-using-intrinsic-functions.json +10 -0
  13. package/test/fixtures/fail-with-error-and-cause-path.json +10 -0
  14. package/test/fixtures/fail-with-resultselector.json +13 -0
  15. package/test/fixtures/fail-with-static-and-dynamic-cause.json +10 -0
  16. package/test/fixtures/fail-with-static-and-dynamic-error.json +10 -0
  17. package/test/fixtures/invalid-backoff-with-jitter-on-retry.json +17 -0
  18. package/test/fixtures/invalid-function-invocation.json +13 -0
  19. package/test/fixtures/map-with-itemprocessor-and-iterator.json +30 -0
  20. package/test/fixtures/map-with-itemprocessor.json +21 -0
  21. package/test/fixtures/map-with-itemspath-context-object.json +21 -0
  22. package/test/fixtures/map-with-null-itemspath.json +20 -0
  23. package/test/fixtures/map-with-parameters-and-itemselector.json +27 -0
  24. package/test/fixtures/map-with-parameters.json +19 -0
  25. package/test/fixtures/map-with-resultselector.json +24 -0
  26. package/test/fixtures/parallel-with-resultselector.json +25 -0
  27. package/test/fixtures/pass-with-intrinsic-function-inputpath.json +12 -0
  28. package/test/fixtures/pass-with-io-path-context-object.json +13 -0
  29. package/test/fixtures/pass-with-null-inputpath.json +12 -0
  30. package/test/fixtures/pass-with-null-outputpath.json +12 -0
  31. package/test/fixtures/pass-with-resultselector.json +15 -0
  32. package/test/fixtures/states-array-intrinsic-functions.json +19 -0
  33. package/test/fixtures/states-array-invocation-leftpad.json +13 -0
  34. package/test/fixtures/states-array-invocation.json +13 -0
  35. package/test/fixtures/states-encoding-decoding-intrinsic-functions.json +14 -0
  36. package/test/fixtures/states-format-invocation.json +13 -0
  37. package/test/fixtures/states-hash-intrinsic-functions.json +13 -0
  38. package/test/fixtures/states-json-intrinsic-functions.json +15 -0
  39. package/test/fixtures/states-jsontostring-invocation.json +14 -0
  40. package/test/fixtures/states-math-intrinsic-functions.json +14 -0
  41. package/test/fixtures/states-string-intrinsic-functions.json +13 -0
  42. package/test/fixtures/states-stringtojson-invocation.json +13 -0
  43. package/test/fixtures/states-uuid-intrinsic-functions.json +13 -0
  44. package/test/fixtures/succeed-with-comment-in-catcher.json +23 -0
  45. package/test/fixtures/succeed-with-resultselector.json +13 -0
  46. package/test/fixtures/task-with-credentials.json +13 -0
  47. package/test/fixtures/task-with-dynamic-timeouts.json +13 -0
  48. package/test/fixtures/task-with-invalid-credentials.json +11 -0
  49. package/test/fixtures/task-with-resultselector.json +15 -0
  50. package/test/fixtures/task-with-static-and-dynamic-heartbeat.json +13 -0
  51. package/test/fixtures/task-with-static-and-dynamic-timeout.json +13 -0
  52. package/test/fixtures/wait-with-resultselector.json +15 -0
  53. package/test/statelint_test.js +230 -157
package/.releaserc.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "@semantic-release/git"
13
13
  ],
14
14
  "generateNotes": {
15
- "preset": "metahub"
15
+ "preset": "angular"
16
16
  },
17
17
  "prepare": [
18
18
  {
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # [1.25.0](https://github.com/wmfs/statelint/compare/v1.24.0...v1.25.0) (2024-09-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * Update to current version of the Amazon State Language ([#292](https://github.com/wmfs/statelint/issues/292)) ([35b08f8](https://github.com/wmfs/statelint/commit/35b08f8b51af12bd851d9bfbe382927899b0239a))
7
+
8
+ # [1.24.0](https://github.com/wmfs/statelint/compare/v1.23.0...v1.24.0) (2023-05-09)
9
+
10
+
11
+ ### 🛠 Builds
12
+
13
+ * **deps-dev:** update dependency [@semantic-release](https://github.com/semantic-release)/changelog to v6.0.3 ([e61465d](https://github.com/wmfs/statelint/commit/e61465d77a118d12160e80e3b56e7ffef68a5def))
14
+ * **deps-dev:** update dependency semantic-release to v20.1.3 ([4b5e9e5](https://github.com/wmfs/statelint/commit/4b5e9e5c5afe3372b0a691b8cb37dbca4d9dfcc2))
15
+ * **deps-dev:** update dependency semantic-release to v21 ([ac64e50](https://github.com/wmfs/statelint/commit/ac64e5002a6a19a646bab5ddecf8e08b8e53ba48))
16
+ * **deps-dev:** update dependency semantic-release to v21.0.1 ([952f93c](https://github.com/wmfs/statelint/commit/952f93cc4bf7d66febc9474dbb7b570bdb6a94c8))
17
+ * **deps-dev:** update dependency semantic-release to v21.0.2 ([7cb33a2](https://github.com/wmfs/statelint/commit/7cb33a216ae9117e1e7edeac3a5ceed9bef96be4))
18
+ * **deps:** update dependency [@wmfs](https://github.com/wmfs)/j2119 to v1.25.0 ([5832e9d](https://github.com/wmfs/statelint/commit/5832e9da83325e5d062df07644b8a9d821cb5179))
19
+
1
20
  # [1.23.0](https://github.com/wmfs/statelint/compare/v1.22.0...v1.23.0) (2023-03-13)
2
21
 
3
22
 
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  An npm package providing a validator for [Amazon States Language](https://states-language.net/spec.html) JSON files. Usable from the command-line or as a library.
6
6
 
7
- This package is derived from Amazon Web Services Labs' [awslabs/statelint](https://github.com/awslabs/statelint) and is up to date with the [changes announced on 29 November 2018](https://aws.amazon.com/blogs/aws/new-compute-database-messaging-analytics-and-machine-learning-integration-for-aws-step-functions/).
7
+ This package is derived from Amazon Web Services Labs' [awslabs/statelint](https://github.com/awslabs/statelint) and is up to date with [Amazon States Language](https://states-language.net/) as of [September 7, 2023](https://states-language.net/#september-7-2023).
8
8
 
9
9
  ## From the Command-line
10
10
 
@@ -4,7 +4,7 @@ A State Machine MUST have a string field named "StartAt".
4
4
  A State Machine MAY have a string field named "Comment".
5
5
  A State Machine MAY have a string field named "Version".
6
6
  A State Machine MAY have a positive-integer field named "TimeoutSeconds" whose value MUST be less than 99999999.
7
- A State MUST have a string field named "Type" whose value MUST be one of "Pass", "Succeed", "Fail", "Task", "Choice", "Wait", or "Parallel".
7
+ A State MUST have a string field named "Type" whose value MUST be one of "Pass", "Succeed", "Fail", "Task", "Choice", "Wait", "Parallel", or "Map".
8
8
  A State whose "Type" field's value is "Pass" is a "Pass State".
9
9
  A State whose "Type" field's value is "Succeed" is a "Succeed State".
10
10
  A State whose "Type" field's value is "Fail" is a "Fail State".
@@ -12,30 +12,43 @@ A State whose "Type" field's value is "Task" is a "Task State".
12
12
  A State whose "Type" field's value is "Choice" is a "Choice State".
13
13
  A State whose "Type" field's value is "Wait" is a "Wait State".
14
14
  A State whose "Type" field's value is "Parallel" is a "Parallel State".
15
+ A State whose "Type" field's value is "Map" is a "Map State".
15
16
  A State MAY have a string field named "Comment".
16
- Each of a Pass State, a Task State, a Wait State, and a Parallel State MAY have a boolean field named "End".
17
- Each of a Pass State, a Task State, and a Parallel State MAY have a field named "Parameters".
17
+ Each of a Pass State, a Task State, a Wait State, a Parallel State, and a Map State MAY have a boolean field named "End".
18
+ Each of a Pass State, a Task State, a Parallel State, and a Map State MAY have a field named "Parameters".
18
19
  A State whose "End" field's value is true is a "Terminal State".
19
20
  Each of a Succeed State and a Fail State is a "Terminal State".
20
21
  A State which is not a Terminal State or a Choice State MUST have a string field named "Next".
21
22
  A Terminal State MUST NOT have a field named "Next".
22
- A State MAY have a nullable-JSONPath field named "InputPath".
23
- Each of a Pass State, a Task State, and a Parallel State MAY have a nullable-referencePath field named "ResultPath".
24
- A State MAY have a nullable-JSONPath field named "OutputPath".
23
+ A State MAY have a field named "InputPath".
24
+ Each of a Pass State, a Task State, a Parallel State, and a Map State MAY have a nullable-referencePath field named "ResultPath".
25
+ Each of a Task State, a Parallel State, and a Map State MAY have a field named "ResultSelector".
26
+ A State MAY have a field named "OutputPath".
25
27
  A Pass State MAY have a field named "Result".
26
28
  A Fail State MUST NOT have a field named "InputPath".
27
29
  A Fail State MUST NOT have a field named "OutputPath".
28
30
  A Fail State MAY have a string field named "Cause".
31
+ A Fail State MAY have a field named "CausePath".
32
+ A Fail State MAY have only one of "Cause" and "CausePath".
29
33
  A Fail State MAY have a string field named "Error".
30
- Each of a Task State and a Parallel State MAY have an object-array field named "Retry"; each element is a "Retrier".
34
+ A Fail State MAY have a field named "ErrorPath".
35
+ A Fail State MAY have only one of "Error" and "ErrorPath".
36
+ Each of a Task State, a Parallel State, and a Map State MAY have an object-array field named "Retry"; each element is a "Retrier".
31
37
  A Task State MUST have a URI field named "Resource".
32
38
  A Task State MAY have a positive-integer field named "TimeoutSeconds" whose value MUST be less than 99999999.
33
39
  A Task State MAY have a positive-integer field named "HeartbeatSeconds" whose value MUST be less than 99999999.
40
+ A Task State MAY have a referencePath field named "TimeoutSecondsPath".
41
+ A Task State MAY have a referencePath field named "HeartbeatSecondsPath".
42
+ A Task State MAY have only one of "TimeoutSeconds" and "TimeoutSecondsPath".
43
+ A Task State MAY have only one of "HeartbeatSeconds" and "HeartbeatSecondsPath".
44
+ A Task State MAY have an object field named "Credentials".
34
45
  A Retrier MUST have a nonempty-string-array field named "ErrorEquals".
35
46
  A Retrier MAY have an positive-integer field named "IntervalSeconds".
36
47
  A Retrier MAY have a nonnegative-integer field named "MaxAttempts" whose value MUST be less than 99999999.
37
48
  A Retrier MAY have a float field named "BackoffRate" whose value MUST be greater than or equal to 1.0.
38
- Each of a Task State and a Parallel State MAY have an object-array field named "Catch"; each element is a "Catcher".
49
+ A Retrier MAY have a positive-integer field named "MaxDelaySeconds".
50
+ A Retrier MAY have a string field named "JitterStrategy" whose value MUST be one of "FULL" or "NONE".
51
+ Each of a Task State, a Parallel State, and a Map State MAY have an object-array field named "Catch"; each element is a "Catcher".
39
52
  A Catcher MUST have an nonempty-string-array field named "ErrorEquals".
40
53
  A Catcher MUST have a string field named "Next".
41
54
  A Catcher MAY have a nullable-referencePath field named "ResultPath".
@@ -50,10 +63,10 @@ A Choice Rule MUST have a string field named "Next".
50
63
  A Choice Rule with an "And" field is a "Boolean".
51
64
  A Choice Rule with an "Or" field is a "Boolean".
52
65
  A Choice Rule with a "Not" field is a "Boolean".
53
- A Choice Rule MAY have a referencePath field named "Variable".
66
+ A Choice Rule MAY have a field named "Variable".
54
67
  A Choice Rule with a "Variable" field is a "Comparison".
55
- A Comparison MUST have a field named one of "IsUndefined", "StringEquals", "StringLessThan", "StringGreaterThan", "StringLessThanEquals", "StringGreaterThanEquals", "NumericEquals", "NumericLessThan", "NumericGreaterThan", "NumericLessThanEquals", "NumericGreaterThanEquals", "BooleanEquals", "TimestampEquals", "TimestampLessThan", "TimestampGreaterThan", "TimestampLessThanEquals", or "TimestampGreaterThanEquals".
56
- A Comparison MAY have a boolean field named "IsUndefined".
68
+ A Choice Rule MAY have a string field named "Comment".
69
+ A Comparison MUST have a field named one of "StringEquals", "StringLessThan", "StringGreaterThan", "StringLessThanEquals", "StringGreaterThanEquals", "NumericEquals", "NumericLessThan", "NumericGreaterThan", "NumericLessThanEquals", "NumericGreaterThanEquals", "BooleanEquals", "TimestampEquals", "TimestampLessThan", "TimestampGreaterThan", "TimestampLessThanEquals", "TimestampGreaterThanEquals", "StringEqualsPath", "StringLessThanPath", "StringGreaterThanPath", "StringLessThanEqualsPath", "StringGreaterThanEqualsPath", "NumericEqualsPath", "NumericLessThanPath", "NumericGreaterThanPath", "NumericLessThanEqualsPath", "NumericGreaterThanEqualsPath", "BooleanEqualsPath", "TimestampEqualsPath", "TimestampLessThanPath", "TimestampGreaterThanPath", "TimestampLessThanEqualsPath", "TimestampGreaterThanEqualsPath", "IsNull", "IsPresent", "IsNumeric", "IsString", "IsBoolean", "IsTimestamp", or "StringMatches".
57
70
  A Comparison MAY have a string field named "StringEquals".
58
71
  A Comparison MAY have a string field named "StringLessThan".
59
72
  A Comparison MAY have a string field named "StringGreaterThan".
@@ -70,6 +83,29 @@ A Comparison MAY have a timestamp field named "TimestampLessThan".
70
83
  A Comparison MAY have a timestamp field named "TimestampGreaterThan".
71
84
  A Comparison MAY have a timestamp field named "TimestampLessThanEquals".
72
85
  A Comparison MAY have a timestamp field named "TimestampGreaterThanEquals".
86
+ A Comparison MAY have a boolean field named "IsNull".
87
+ A Comparison MAY have a boolean field named "IsPresent".
88
+ A Comparison MAY have a boolean field named "IsNumeric".
89
+ A Comparison MAY have a boolean field named "IsString".
90
+ A Comparison MAY have a boolean field named "IsBoolean".
91
+ A Comparison MAY have a boolean field named "IsTimestamp".
92
+ A Comparison MAY have a string field named "StringMatches".
93
+ A Comparison MAY have a referencePath field named "StringEqualsPath".
94
+ A Comparison MAY have a referencePath field named "StringLessThanPath".
95
+ A Comparison MAY have a referencePath field named "StringGreaterThanPath".
96
+ A Comparison MAY have a referencePath field named "StringLessThanEqualsPath".
97
+ A Comparison MAY have a referencePath field named "StringGreaterThanEqualsPath".
98
+ A Comparison MAY have a referencePath field named "NumericEqualsPath".
99
+ A Comparison MAY have a referencePath field named "NumericLessThanPath".
100
+ A Comparison MAY have a referencePath field named "NumericGreaterThanPath".
101
+ A Comparison MAY have a referencePath field named "NumericLessThanEqualsPath".
102
+ A Comparison MAY have a referencePath field named "NumericGreaterThanEqualsPath".
103
+ A Comparison MAY have a referencePath field named "BooleanEqualsPath".
104
+ A Comparison MAY have a referencePath field named "TimestampEqualsPath".
105
+ A Comparison MAY have a referencePath field named "TimestampLessThanPath".
106
+ A Comparison MAY have a referencePath field named "TimestampGreaterThanPath".
107
+ A Comparison MAY have a referencePath field named "TimestampLessThanEqualsPath".
108
+ A Comparison MAY have a referencePath field named "TimestampGreaterThanEqualsPath".
73
109
  A Comparison MUST NOT have a field named "And".
74
110
  A Comparison MUST NOT have a field named "Or".
75
111
  A Comparison MUST NOT have a field named "Not".
@@ -80,9 +116,8 @@ A Nested Rule with an "And" field is a "Nested Boolean".
80
116
  A Nested Rule with an "Or" field is a "Nested Boolean".
81
117
  A Nested Rule with a "Not" field is a "Nested Boolean".
82
118
  A Nested Rule MUST NOT have a field named "Next".
83
- A Nested Rule MAY have a referencePath field named "Variable".
119
+ A Nested Rule MAY have a field named "Variable".
84
120
  A Nested Rule with a "Variable" field is a "Nested Comparison".
85
- A Nested Comparison MAY have a boolean field named "IsUndefined".
86
121
  A Nested Comparison MAY have a string field named "StringEquals".
87
122
  A Nested Comparison MAY have a string field named "StringLessThan".
88
123
  A Nested Comparison MAY have a string field named "StringGreaterThan".
@@ -99,6 +134,29 @@ A Nested Comparison MAY have a timestamp field named "TimestampLessThan".
99
134
  A Nested Comparison MAY have a timestamp field named "TimestampGreaterThan".
100
135
  A Nested Comparison MAY have a timestamp field named "TimestampLessThanEquals".
101
136
  A Nested Comparison MAY have a timestamp field named "TimestampGreaterThanEquals".
137
+ A Nested Comparison MAY have a boolean field named "IsNull".
138
+ A Nested Comparison MAY have a boolean field named "IsPresent".
139
+ A Nested Comparison MAY have a boolean field named "IsNumeric".
140
+ A Nested Comparison MAY have a boolean field named "IsString".
141
+ A Nested Comparison MAY have a boolean field named "IsBoolean".
142
+ A Nested Comparison MAY have a boolean field named "IsTimestamp".
143
+ A Nested Comparison MAY have a string field named "StringMatches".
144
+ A Nested Comparison MAY have a referencePath field named "StringEqualsPath".
145
+ A Nested Comparison MAY have a referencePath field named "StringLessThanPath".
146
+ A Nested Comparison MAY have a referencePath field named "StringGreaterThanPath".
147
+ A Nested Comparison MAY have a referencePath field named "StringLessThanEqualsPath".
148
+ A Nested Comparison MAY have a referencePath field named "StringGreaterThanEqualsPath".
149
+ A Nested Comparison MAY have a referencePath field named "NumericEqualsPath".
150
+ A Nested Comparison MAY have a referencePath field named "NumericLessThanPath".
151
+ A Nested Comparison MAY have a referencePath field named "NumericGreaterThanPath".
152
+ A Nested Comparison MAY have a referencePath field named "NumericLessThanEqualsPath".
153
+ A Nested Comparison MAY have a referencePath field named "NumericGreaterThanEqualsPath".
154
+ A Nested Comparison MAY have a referencePath field named "BooleanEqualsPath".
155
+ A Nested Comparison MAY have a referencePath field named "TimestampEqualsPath".
156
+ A Nested Comparison MAY have a referencePath field named "TimestampLessThanPath".
157
+ A Nested Comparison MAY have a referencePath field named "TimestampGreaterThanPath".
158
+ A Nested Comparison MAY have a referencePath field named "TimestampLessThanEqualsPath".
159
+ A Nested Comparison MAY have a referencePath field named "TimestampGreaterThanEqualsPath".
102
160
  A Nested Comparison MUST NOT have a field named "And".
103
161
  A Nested Comparison MUST NOT have a field named "Or".
104
162
  A Nested Comparison MUST NOT have a field named "Not".
@@ -109,6 +167,18 @@ A Wait State MAY have a referencePath field named "TimestampPath".
109
167
  A Wait State MUST have only one of "Seconds", "SecondsPath", "Timestamp", and "TimestampPath".
110
168
  A Wait State MUST have a field named one of "Seconds", "SecondsPath", "Timestamp", or "TimestampPath".
111
169
  A Parallel State MUST have an object-array field named "Branches"; each element is a "Branch".
112
- A Branch MUST have an object field named "States"; each element is a "State".
170
+ A Map State MAY have an object field named "Iterator"; its value is a "ItemProcessor".
171
+ A Map State MAY have an object field named "ItemProcessor"; its value is a "ItemProcessor".
172
+ A Map State MUST have only one of "Iterator" and "ItemProcessor".
173
+ A Map State MAY have a field named "ItemsPath".
174
+ A Map State MAY have an object field named "ItemSelector".
175
+ A Map State MAY have only one of "ItemSelector" and "Parameters".
176
+ A Map State MAY have a numeric field named "MaxConcurrency".
177
+ A Branch MUST have an object field named "States"; each field is a "State".
113
178
  A Branch MUST have a string field named "StartAt".
114
179
  A Branch MAY have a string field named "Comment".
180
+ A Catcher MAY have a string field named "Comment".
181
+ A ItemProcessor MAY have an object field named "ProcessorConfig".
182
+ A ItemProcessor MAY have a string field named "Comment".
183
+ A ItemProcessor MUST have a string field named "StartAt".
184
+ A ItemProcessor MUST have an object field named "States"; each field is a "State".
@@ -1,5 +1,15 @@
1
1
  const JSONPathChecker = require('@wmfs/j2119/lib/j2119/json_path_checker')
2
2
 
3
+ const payloadBuilderFields = ['Parameters', 'ResultSelector']
4
+ const contextObjectAccessFields = [
5
+ { field: 'InputPath', nullable: true },
6
+ { field: 'OutputPath', nullable: true },
7
+ { field: 'ItemsPath', nullable: false }
8
+ ]
9
+ const choiceStateNestedOperators = ['And', 'Or', 'Not']
10
+ const intrinsicInvocationRegex = /^States\.(Format|Array|ArrayPartition|ArrayContains|ArrayRange|ArrayGetItem|ArrayLength|ArrayUnique|Base64Encode|Base64Decode|Hash|JsonMerge|JsonToString|StringToJson|MathRandom|MathAdd|StringSplit)\(.+\)$/
11
+ const intrinsicUUIDInvocationRegex = /^States\.UUID\(\)$/
12
+
3
13
  class StateNode {
4
14
  constructor () {
5
15
  this.currentStatesNode = []
@@ -14,7 +24,8 @@ class StateNode {
14
24
  const isMachineTop = isObject(node.States)
15
25
 
16
26
  if (isMachineTop) {
17
- this.checkForDuplicateStates(node, path, problems)
27
+ this.checkStartAt(node, path, problems)
28
+ this.checkStates(node, path, problems)
18
29
  } // if (isMachineTop)
19
30
 
20
31
  this.checkNode(node, path, problems)
@@ -103,7 +114,22 @@ class StateNode {
103
114
  } // for ...
104
115
  } // checkStatesALL
105
116
 
106
- checkForDuplicateStates (node, path, problems) {
117
+ checkStates (node, path, problems) {
118
+ for (const [name, child] of Object.entries(node.States)) {
119
+ if (isObject(child)) {
120
+ const childPath = `${path}.${name}`
121
+ this.probeContextObjectAccess(child, childPath, problems)
122
+ this.probePayloadBuilders(child, childPath, problems)
123
+ if (child.Type === 'Choice' && child.Choices) {
124
+ this.probeChoiceState(child.Choices, `${childPath}.Choices`, problems)
125
+ }
126
+ } // if ...
127
+
128
+ this.checkForDuplicateStates(name, path, problems)
129
+ } // for ...
130
+ } // checkStates
131
+
132
+ checkStartAt (node, path, problems) {
107
133
  this.currentStatesNode.push(node.States)
108
134
  const startAt = node.StartAt
109
135
  if (startAt) {
@@ -114,19 +140,15 @@ class StateNode {
114
140
  } else {
115
141
  this.currentStatesIncoming.push([])
116
142
  }
143
+ } // checkStartAt
117
144
 
118
- for (const [name, child] of Object.entries(node.States)) {
119
- if (isObject(child) && child.Parameters) {
120
- this.probeParameters(child, `${path}.${name}`, problems)
121
- }
122
-
123
- if (this.allStateNames.has(name)) {
124
- problems.push(`State "${name}", defined at ${path}.States, ` +
125
- `is also defined at ${this.allStateNames.get(name)}`)
126
- } else {
127
- this.allStateNames.set(name, `${path}.States`)
128
- }
129
- } // for ...
145
+ checkForDuplicateStates (name, path, problems) {
146
+ if (this.allStateNames.has(name)) {
147
+ problems.push(`State "${name}", defined at ${path}.States, ` +
148
+ `is also defined at ${this.allStateNames.get(name)}`)
149
+ } else {
150
+ this.allStateNames.set(name, `${path}.States`)
151
+ }
130
152
  } // checkForDuplicateStates
131
153
 
132
154
  checkForOrphanStates (path, problems) {
@@ -138,23 +160,63 @@ class StateNode {
138
160
  )
139
161
  } // checkForOrphanStates
140
162
 
141
- probeParameters (node, path, problems) {
142
- if (isArray(node)) {
143
- for (const [index, value] of node.entries()) {
144
- this.probeParameters(value, `${path}[${index}]`, problems)
145
- } // for ...
146
- } else if (isObject(node)) {
163
+ probeContextObjectAccess (node, path, problems) {
164
+ for (const field of contextObjectAccessFields) {
165
+ const fieldName = field.field
166
+ const nullable = field.nullable
167
+ if (Object.hasOwn(node, fieldName)) {
168
+ if (!nullable && !node[fieldName]) {
169
+ problems.push(`Field "${fieldName}" defined at "${path}" should be non-null`)
170
+ }
171
+ if (node[fieldName] && !isValidParametersPath(node[fieldName])) {
172
+ problems.push(`Field "${fieldName}" defined at "${path}" is not a JSONPath`)
173
+ }
174
+ } // if ...
175
+ } // for ...
176
+ } // probeContextObjectAccess
177
+
178
+ probePayloadBuilders (child, childPath, problems) {
179
+ for (const fieldName of payloadBuilderFields) {
180
+ if (child[fieldName]) {
181
+ this.probePayloadBuilder(child[fieldName], childPath, fieldName, problems)
182
+ }
183
+ } // for ...
184
+ } // probePayloadBuilders
185
+
186
+ probePayloadBuilder (node, path, fieldName, problems) {
187
+ if (isObject(node)) {
147
188
  for (const [name, value] of Object.entries(node)) {
148
189
  if (name.endsWith('.$')) {
149
- if (!isString(value) || !JSONPathChecker.isPath(value)) {
150
- problems.push(`Field "${name}" of Parameters at "${path}" is not a JSONPath`)
190
+ if (!isIntrinsicInvocation(value) && !isValidParametersPath(value)) {
191
+ problems.push(`Field "${name}" of ${fieldName} at "${path}" is not a JSONPath or intrinsic function expression`)
151
192
  }
152
193
  } else {
153
- this.probeParameters(value, `${path}.${name}`, problems)
194
+ this.probePayloadBuilder(value, `${path}.${name}`, fieldName, problems)
154
195
  } // if (name.endsWith('.$'))
155
196
  } // for ...
156
- } // if (isArray(node)) ...
157
- } // probeParameters
197
+ } else if (isArray(node)) {
198
+ for (const [index, value] of node.entries()) {
199
+ this.probePayloadBuilder(value, `${path}[${index}]`, fieldName, problems)
200
+ } // for ...
201
+ }
202
+ } // probePayloadBuilder
203
+
204
+ probeChoiceState (node, path, problems) {
205
+ if (isObject(node)) {
206
+ if (node.Variable && !isValidParametersPath(node.Variable)) {
207
+ problems.push(`Field "Variable" of Choice state at "${path}" is not a JSONPath`)
208
+ }
209
+ for (const operator of choiceStateNestedOperators) {
210
+ if (node[operator]) {
211
+ this.probeChoiceState(node[operator], `${path}.${operator}`, problems)
212
+ }
213
+ }
214
+ } else if (isArray(node)) {
215
+ for (const [index, element] of node.entries()) {
216
+ this.probeChoiceState(element, `${path}[${index}]`, problems)
217
+ }
218
+ }
219
+ } // probeChoiceState
158
220
  } // class StateNode
159
221
 
160
222
  function isObject (o) {
@@ -165,6 +227,32 @@ function isString (s) {
165
227
  return (typeof s === 'string')
166
228
  } // isString
167
229
 
230
+ function isIntrinsicInvocation (v) {
231
+ return isString(v) &&
232
+ (
233
+ matchesIntrinsicInvocation(v) ||
234
+ matchesIntrinsicUUIDInvocation(v)
235
+ )
236
+ } // isInstrinsicInvocation
237
+
238
+ function matchesIntrinsicInvocation (v) {
239
+ return intrinsicInvocationRegex.test(v)
240
+ } // matchesIntrinsicInvocation
241
+
242
+ function matchesIntrinsicUUIDInvocation (v) {
243
+ return intrinsicUUIDInvocationRegex.test(v)
244
+ } // matchesIntrinsicUUIDInvocation
245
+
246
+ function isValidParametersPath (v) {
247
+ if (!isString(v)) {
248
+ return false
249
+ }
250
+ if (v.startsWith('$$')) {
251
+ v = v.substring(1)
252
+ }
253
+ return JSONPathChecker.isPath(v)
254
+ } // isValidParametersPath
255
+
168
256
  function isArray (a) {
169
257
  return Array.isArray(a)
170
258
  } // isArray
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wmfs/statelint",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "description": "Validator for Amazon States Language JSON files.",
5
5
  "author": "West Midlands Fire Service",
6
6
  "homepage": "https://github.com/wmfs/statelint#readme",
@@ -23,23 +23,23 @@
23
23
  "statelint": "bin/statelint.js"
24
24
  },
25
25
  "dependencies": {
26
- "@wmfs/j2119": "1.24.0",
26
+ "@wmfs/j2119": "1.25.1",
27
27
  "cli": "1.0.1",
28
28
  "n-readlines": "1.0.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@semantic-release/changelog": "6.0.2",
31
+ "@semantic-release/changelog": "6.0.3",
32
32
  "@semantic-release/git": "10.0.1",
33
- "chai": "4.3.7",
33
+ "chai": "4.5.0",
34
34
  "codecov": "3.8.3",
35
35
  "conventional-changelog-metahub": "4.0.1",
36
36
  "cz-conventional-changelog": "3.3.0",
37
37
  "dirty-chai": "2.0.1",
38
- "mocha": "10.2.0",
39
- "nyc": "15.1.0",
40
- "semantic-release": "20.1.1",
41
- "standard": "17.0.0",
42
- "tmp": "0.2.1"
38
+ "mocha": "10.7.3",
39
+ "nyc": "17.0.0",
40
+ "semantic-release": "24.1.1",
41
+ "standard": "17.1.2",
42
+ "tmp": "0.2.3"
43
43
  },
44
44
  "scripts": {
45
45
  "statelint": "./bin/statelint.js",
@@ -0,0 +1,17 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Task",
6
+ "Resource": "foo:bar",
7
+ "End": true,
8
+ "Retry": [
9
+ {
10
+ "ErrorEquals": ["States.Timeout"],
11
+ "MaxDelaySeconds": 5,
12
+ "JitterStrategy": "PARTIAL"
13
+ }
14
+ ]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Task",
6
+ "Resource": "foo:bar",
7
+ "End": true,
8
+ "Retry": [
9
+ {
10
+ "ErrorEquals": ["States.Timeout"],
11
+ "MaxDelaySeconds": 5,
12
+ "JitterStrategy": "FULL"
13
+ }
14
+ ]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Choice",
6
+ "Choices": [
7
+ {
8
+ "Variable": "$.foo",
9
+ "StringEquals": "x",
10
+ "Next": "x",
11
+ "Comment": "foo"
12
+ }
13
+ ]
14
+ },
15
+ "x": {
16
+ "Type": "Succeed"
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "StartAt": "ChoiceState",
3
+ "States": {
4
+ "ChoiceState": {
5
+ "Type": "Choice",
6
+ "Choices": [
7
+ {
8
+ "Variable": "$$.value",
9
+ "IsNull": false,
10
+ "Next": "SucceedState"
11
+ }
12
+ ],
13
+ "Default": "FailState"
14
+ },
15
+ "SucceedState": {
16
+ "Type": "Succeed"
17
+ },
18
+ "FailState": {
19
+ "Type": "Fail"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Choice",
6
+ "Choices": [
7
+ {
8
+ "Variable": "$.foo",
9
+ "StringEquals": "x",
10
+ "Next": "x"
11
+ }
12
+ ],
13
+ "ResultSelector": {
14
+ "a": "x",
15
+ "b.$": "$.y",
16
+ "c.$": "$$.z"
17
+ }
18
+ },
19
+ "x": {
20
+ "Type": "Succeed"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "StartAt": "FailState",
3
+ "States": {
4
+ "FailState": {
5
+ "Type": "Fail",
6
+ "ErrorPath": "States.Format('this is the error: {}, and this is the cause: {}', $.Error, $.Cause)",
7
+ "CausePath": "States.UUID()"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "StartAt": "FailState",
3
+ "States": {
4
+ "FailState": {
5
+ "Type": "Fail",
6
+ "ErrorPath": "$.Error",
7
+ "CausePath": "$.Cause"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Fail",
6
+ "ResultSelector": {
7
+ "a": "x",
8
+ "b.$": "$.y",
9
+ "c.$": "$$.z"
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "StartAt": "FailState",
3
+ "States": {
4
+ "FailState": {
5
+ "Type": "Fail",
6
+ "Cause": "Cause",
7
+ "CausePath": "$.Cause"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "StartAt": "FailState",
3
+ "States": {
4
+ "FailState": {
5
+ "Type": "Fail",
6
+ "Error": "Error",
7
+ "ErrorPath": "$.Error"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Task",
6
+ "Resource": "foo:bar",
7
+ "End": true,
8
+ "Retry": [
9
+ {
10
+ "ErrorEquals": ["States.Timeout"],
11
+ "MaxDelaySeconds": -1,
12
+ "JitterStrategy": "FULL"
13
+ }
14
+ ]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "StartAt": "p",
3
+ "States": {
4
+ "p": {
5
+ "Type": "Task",
6
+ "Resource": "foo:bar",
7
+ "ResultSelector": {
8
+ "abc.$": "States.Xyz($.result)"
9
+ },
10
+ "End": true
11
+ }
12
+ }
13
+ }