@usearazzo/runner 1.0.1-alpha.19 → 1.0.1-alpha.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usearazzo/runner",
3
- "version": "1.0.1-alpha.19",
3
+ "version": "1.0.1-alpha.21",
4
4
  "description": "Runner for Arazzo Workflows.",
5
5
  "keywords": [
6
6
  "arazzo",
@@ -73,8 +73,8 @@
73
73
  "@swaggerexpert/json-pointer": "^3.0.2",
74
74
  "@swaggerexpert/jsonpath": "^4.0.5",
75
75
  "@swaggerexpert/media-types": "^1.0.2",
76
- "@usearazzo/parser": "1.0.1-alpha.19",
77
- "@usearazzo/resolver": "1.0.1-alpha.19",
76
+ "@usearazzo/parser": "1.0.1-alpha.21",
77
+ "@usearazzo/resolver": "1.0.1-alpha.21",
78
78
  "@xmldom/xmldom": "^0.9.12",
79
79
  "fontoxpath": "^3.34.0",
80
80
  "ramda": "^0.32.0",
@@ -94,5 +94,5 @@
94
94
  "devDependencies": {
95
95
  "swagger-client": "^3.38.4"
96
96
  },
97
- "gitHead": "54a9b81c5607984c87d82ec159506f522b33a4d7"
97
+ "gitHead": "d342b02adbd11f5362aa4850b2417521e59448ce"
98
98
  }
@@ -9,10 +9,11 @@ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
9
9
  var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
10
10
  var _ResolverError = _interopRequireDefault(require("../errors/ResolverError.cjs"));
11
11
  /**
12
- * Determines whether a single criterion is met.
12
+ * Determines whether a single criterion of an action is met. Receives the
13
+ * action declaring the criterion, so a caller can attribute the outcome to it.
13
14
  *
14
15
  * Bridged to a criterion evaluator
15
- * (`(criterion) => criterionEvaluator.evaluate(criterion, resolve)`); an
16
+ * (`(criterion) => criterionEvaluator.evaluate(criterion, resolve).met`); an
16
17
  * unresolvable/failing criterion is `false`, not an error.
17
18
  * @public
18
19
  */
@@ -144,6 +145,7 @@ class ActionResolver {
144
145
  reason: 'malformed-criteria'
145
146
  });
146
147
  }
148
+ let met = true;
147
149
  for (const criterion of criteria) {
148
150
  if (!(0, _apidomNsArazzo.isCriterionElement)(criterion)) {
149
151
  throw new _ResolverError.default("An action's `criteria` contains an entry that is not a Criterion Object", {
@@ -152,9 +154,11 @@ class ActionResolver {
152
154
  reason: 'malformed-criterion'
153
155
  });
154
156
  }
155
- if (!isCriterionMet(criterion)) return false;
157
+ // every criterion is evaluated, not only up to the first unmet one, so
158
+ // a predicate that records outcomes sees the whole list.
159
+ if (!isCriterionMet(criterion, action)) met = false;
156
160
  }
157
- return true;
161
+ return met;
158
162
  }
159
163
  }
160
164
  var _default = exports.default = ActionResolver;
@@ -2,10 +2,11 @@ import { isArrayElement } from '@speclynx/apidom-datamodel';
2
2
  import { isSuccessActionElement, isFailureActionElement, isCriterionElement } from '@speclynx/apidom-ns-arazzo-1';
3
3
  import ResolverError from "../errors/ResolverError.mjs";
4
4
  /**
5
- * Determines whether a single criterion is met.
5
+ * Determines whether a single criterion of an action is met. Receives the
6
+ * action declaring the criterion, so a caller can attribute the outcome to it.
6
7
  *
7
8
  * Bridged to a criterion evaluator
8
- * (`(criterion) => criterionEvaluator.evaluate(criterion, resolve)`); an
9
+ * (`(criterion) => criterionEvaluator.evaluate(criterion, resolve).met`); an
9
10
  * unresolvable/failing criterion is `false`, not an error.
10
11
  * @public
11
12
  */
@@ -134,6 +135,7 @@ class ActionResolver {
134
135
  reason: 'malformed-criteria'
135
136
  });
136
137
  }
138
+ let met = true;
137
139
  for (const criterion of criteria) {
138
140
  if (!isCriterionElement(criterion)) {
139
141
  throw new ResolverError("An action's `criteria` contains an entry that is not a Criterion Object", {
@@ -142,9 +144,11 @@ class ActionResolver {
142
144
  reason: 'malformed-criterion'
143
145
  });
144
146
  }
145
- if (!isCriterionMet(criterion)) return false;
147
+ // every criterion is evaluated, not only up to the first unmet one, so
148
+ // a predicate that records outcomes sees the whole list.
149
+ if (!isCriterionMet(criterion, action)) met = false;
146
150
  }
147
- return true;
151
+ return met;
148
152
  }
149
153
  }
150
154
  export default ActionResolver;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ /**
8
+ * The `reason` a {@link CriterionError} carries.
9
+ *
10
+ * Structural reasons mark a criterion that is malformed as written; they are
11
+ * thrown as a plain `CriterionError`. Evaluation reasons (`InvalidSimple`,
12
+ * `InvalidRegex`, `InvalidJSONPath`, `InvalidXPath`, `InvalidXPathContext`) mark
13
+ * a condition that cannot be evaluated; they are thrown as a
14
+ * {@link CriterionEvaluationError}, which `CriterionEvaluator` turns into an
15
+ * unmet criterion.
16
+ * @public
17
+ */
18
+ const CriterionErrorReason = {
19
+ /** The element is not a Criterion Object. */
20
+ InvalidCriterion: 'invalid-criterion',
21
+ /** The `condition` is missing or empty. */
22
+ MissingCondition: 'missing-condition',
23
+ /** The `type` is present but unreadable. */
24
+ InvalidType: 'invalid-type',
25
+ /** The `type` names no known criterion type. */
26
+ UnknownType: 'unknown-type',
27
+ /** The expression type `version` is present but not a string. */
28
+ InvalidVersion: 'invalid-version',
29
+ /** The expression type `version` is not the one this runner supports. */
30
+ UnsupportedVersion: 'unsupported-version',
31
+ /** The `context` is not a runtime expression. */
32
+ InvalidContext: 'invalid-context',
33
+ /** A `regex`, `jsonpath` or `xpath` criterion has no `context`. */
34
+ MissingContext: 'missing-context',
35
+ /** The `simple` condition cannot be parsed. */
36
+ InvalidSimple: 'invalid-simple',
37
+ /** The `regex` condition is not a valid regular expression. */
38
+ InvalidRegex: 'invalid-regex',
39
+ /** The `jsonpath` condition is not a valid JSONPath query. */
40
+ InvalidJSONPath: 'invalid-jsonpath',
41
+ /** The `xpath` condition is invalid or fails to evaluate. */
42
+ InvalidXPath: 'invalid-xpath',
43
+ /** The resolved `xpath` context is not XML. */
44
+ InvalidXPathContext: 'invalid-xpath-context'
45
+ };
46
+
47
+ /**
48
+ * One of the `CriterionErrorReason` values.
49
+ * @public
50
+ */
51
+ var _default = exports.default = CriterionErrorReason;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The `reason` a {@link CriterionError} carries.
3
+ *
4
+ * Structural reasons mark a criterion that is malformed as written; they are
5
+ * thrown as a plain `CriterionError`. Evaluation reasons (`InvalidSimple`,
6
+ * `InvalidRegex`, `InvalidJSONPath`, `InvalidXPath`, `InvalidXPathContext`) mark
7
+ * a condition that cannot be evaluated; they are thrown as a
8
+ * {@link CriterionEvaluationError}, which `CriterionEvaluator` turns into an
9
+ * unmet criterion.
10
+ * @public
11
+ */
12
+ const CriterionErrorReason = {
13
+ /** The element is not a Criterion Object. */
14
+ InvalidCriterion: 'invalid-criterion',
15
+ /** The `condition` is missing or empty. */
16
+ MissingCondition: 'missing-condition',
17
+ /** The `type` is present but unreadable. */
18
+ InvalidType: 'invalid-type',
19
+ /** The `type` names no known criterion type. */
20
+ UnknownType: 'unknown-type',
21
+ /** The expression type `version` is present but not a string. */
22
+ InvalidVersion: 'invalid-version',
23
+ /** The expression type `version` is not the one this runner supports. */
24
+ UnsupportedVersion: 'unsupported-version',
25
+ /** The `context` is not a runtime expression. */
26
+ InvalidContext: 'invalid-context',
27
+ /** A `regex`, `jsonpath` or `xpath` criterion has no `context`. */
28
+ MissingContext: 'missing-context',
29
+ /** The `simple` condition cannot be parsed. */
30
+ InvalidSimple: 'invalid-simple',
31
+ /** The `regex` condition is not a valid regular expression. */
32
+ InvalidRegex: 'invalid-regex',
33
+ /** The `jsonpath` condition is not a valid JSONPath query. */
34
+ InvalidJSONPath: 'invalid-jsonpath',
35
+ /** The `xpath` condition is invalid or fails to evaluate. */
36
+ InvalidXPath: 'invalid-xpath',
37
+ /** The resolved `xpath` context is not XML. */
38
+ InvalidXPathContext: 'invalid-xpath-context'
39
+ };
40
+
41
+ /**
42
+ * One of the `CriterionErrorReason` values.
43
+ * @public
44
+ */
45
+
46
+ export default CriterionErrorReason;
@@ -10,6 +10,8 @@ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
10
10
  var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
11
11
  var _arazzoRuntimeExpression = require("@swaggerexpert/arazzo-runtime-expression");
12
12
  var _CriterionError = _interopRequireDefault(require("../errors/CriterionError.cjs"));
13
+ var _CriterionEvaluationError = _interopRequireDefault(require("../errors/CriterionEvaluationError.cjs"));
14
+ var _CriterionErrorReason = _interopRequireDefault(require("./CriterionErrorReason.cjs"));
13
15
  var _RegexCriterionEvaluator = _interopRequireDefault(require("./RegexCriterionEvaluator.cjs"));
14
16
  var _SimpleCriterionEvaluator = _interopRequireDefault(require("./SimpleCriterionEvaluator.cjs"));
15
17
  var _JSONPathCriterionEvaluator = _interopRequireDefault(require("./JSONPathCriterionEvaluator.cjs"));
@@ -26,13 +28,22 @@ var _XPathCriterionEvaluator = _interopRequireDefault(require("./XPathCriterionE
26
28
  * @public
27
29
  */
28
30
 
31
+ /**
32
+ * The outcome of evaluating one criterion.
33
+ * @public
34
+ */
35
+
29
36
  /**
30
37
  * Options for the CriterionEvaluator.
38
+ *
39
+ * An injected evaluator signals a condition it cannot evaluate by throwing a
40
+ * {@link CriterionEvaluationError}, which fails the criterion; any other error
41
+ * it throws, a plain {@link CriterionError} included, propagates.
31
42
  * @public
32
43
  */
33
44
 
34
45
  /**
35
- * Evaluates an Arazzo Criterion Object to a boolean.
46
+ * Evaluates an Arazzo Criterion Object to a {@link CriterionOutcome}.
36
47
  *
37
48
  * Dispatches on the criterion's `type` (defaulting to `simple` when omitted):
38
49
  * - `simple` — evaluates the condition directly, resolving the runtime
@@ -41,12 +52,27 @@ var _XPathCriterionEvaluator = _interopRequireDefault(require("./XPathCriterionE
41
52
  * pattern to the resolved value.
42
53
  * - `jsonpath` — resolves the `context` runtime expression, then applies the
43
54
  * JSONPath query to the resolved value (met when the query matches). Only the
44
- * `rfc9535` version is supported (the spec default); an explicit other version
45
- * throws.
55
+ * `rfc9535` version is supported; an explicit other version throws.
46
56
  * - `xpath` — resolves the `context` runtime expression, then applies the XPath
47
57
  * 3.1 query to the resolved XML value (met by the Effective Boolean Value).
48
- * Only the `xpath-31` version is supported (the spec default); an explicit
49
- * other version throws.
58
+ * Only the `xpath-31` version is supported; an explicit other version throws.
59
+ *
60
+ * Versions follow Arazzo 1.1.0's Expression Type Object: `rfc9535` and
61
+ * `xpath-31` are allowed values, and an omitted `version` defaults to them.
62
+ * Arazzo 1.0.x lists neither and requires `version`; this evaluator applies
63
+ * the 1.1.0 rule to every document, which accepts more but never evaluates a
64
+ * query under the wrong dialect.
65
+ *
66
+ * A condition that cannot be evaluated fails the criterion (`met: false`), as
67
+ * Arazzo 1.1.0 requires: a syntax error in the condition, a context the
68
+ * condition cannot apply to (non-XML data for `xpath`), or an XPath evaluation
69
+ * error. The type-specific evaluators report these as a
70
+ * {@link CriterionEvaluationError}, which this facade returns as the
71
+ * outcome's `error` so the caller can report it; any other error they raise
72
+ * propagates. A criterion that is malformed as written still throws: a
73
+ * missing `condition`, an unknown or unreadable `type`, an unsupported
74
+ * expression `version`, or a `context` that is missing or not a runtime
75
+ * expression.
50
76
  *
51
77
  * The evaluator is agnostic about how runtime expressions are resolved: the
52
78
  * caller supplies a {@link CriterionContextResolver} per evaluation, keeping
@@ -67,7 +93,8 @@ class CriterionEvaluator {
67
93
  }
68
94
 
69
95
  /**
70
- * Evaluates a criterion element, returning whether the condition is met.
96
+ * Evaluates a criterion element, returning whether the condition is met and,
97
+ * when it cannot be evaluated, why.
71
98
  *
72
99
  * `resolve` turns a runtime expression into a value: for `simple` it resolves
73
100
  * the expressions embedded in the condition; for the other types it resolves
@@ -76,7 +103,7 @@ class CriterionEvaluator {
76
103
  evaluate(criterion, resolve) {
77
104
  if (!(0, _apidomNsArazzo.isCriterionElement)(criterion)) {
78
105
  throw new _CriterionError.default('Element is not a Criterion Object', {
79
- reason: 'invalid-criterion'
106
+ reason: _CriterionErrorReason.default.InvalidCriterion
80
107
  });
81
108
  }
82
109
  const condition = (0, _apidomDatamodel.isStringElement)(criterion.condition) ? (0, _apidomCore.toValue)(criterion.condition) : undefined;
@@ -84,34 +111,52 @@ class CriterionEvaluator {
84
111
  // (e.g. an empty regex would match unconditionally), so reject it too.
85
112
  if (condition === undefined || condition === '') {
86
113
  throw new _CriterionError.default('Criterion is missing a "condition"', {
87
- reason: 'missing-condition'
114
+ reason: _CriterionErrorReason.default.MissingCondition
88
115
  });
89
116
  }
90
117
  const type = this.#resolveType(criterion);
91
118
  this.#requireValidContext(criterion, type);
92
- switch (type) {
93
- case 'simple':
94
- return this.#simple.evaluate(condition, resolve);
95
- case 'regex':
96
- return this.#regex.evaluate(condition, this.#resolveContext(criterion, type, resolve));
97
- case 'jsonpath':
98
- // only rfc9535 is supported; it is the spec default, so an omitted
99
- // version is accepted. draft-goessner has different semantics and is
100
- // rejected rather than mis-evaluated.
101
- this.#requireVersion(criterion, type, condition, 'rfc9535');
102
- return this.#jsonpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
103
- case 'xpath':
104
- // only XPath 3.1 is supported; xpath-31 is the spec default, so an
105
- // omitted version is accepted. Older versions are rejected.
106
- this.#requireVersion(criterion, type, condition, 'xpath-31');
107
- return this.#xpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
108
- default:
109
- throw new _CriterionError.default(`Unknown criterion type "${type}"`, {
110
- condition,
111
- type,
112
- reason: 'unknown-type'
113
- });
119
+ let met;
120
+ try {
121
+ switch (type) {
122
+ case 'simple':
123
+ met = this.#simple.evaluate(condition, resolve);
124
+ break;
125
+ case 'regex':
126
+ met = this.#regex.evaluate(condition, this.#resolveContext(criterion, type, resolve));
127
+ break;
128
+ case 'jsonpath':
129
+ // only rfc9535 is supported, Arazzo 1.1.0's default, so an omitted
130
+ // version is accepted. draft-goessner has different semantics and is
131
+ // rejected rather than mis-evaluated.
132
+ this.#requireVersion(criterion, type, condition, 'rfc9535');
133
+ met = this.#jsonpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
134
+ break;
135
+ case 'xpath':
136
+ // only XPath 3.1 is supported; xpath-31 is Arazzo 1.1.0's default, so
137
+ // an omitted version is accepted. Older versions are rejected.
138
+ this.#requireVersion(criterion, type, condition, 'xpath-31');
139
+ met = this.#xpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
140
+ break;
141
+ default:
142
+ throw new _CriterionError.default(`Unknown criterion type "${type}"`, {
143
+ condition,
144
+ type,
145
+ reason: _CriterionErrorReason.default.UnknownType
146
+ });
147
+ }
148
+ } catch (error) {
149
+ // Arazzo 1.1.0: a condition that cannot be evaluated MUST evaluate to fail.
150
+ // The structural checks throw a plain CriterionError and still propagate.
151
+ if (error instanceof _CriterionEvaluationError.default) return {
152
+ met: false,
153
+ error
154
+ };
155
+ throw error;
114
156
  }
157
+ return {
158
+ met
159
+ };
115
160
  }
116
161
 
117
162
  /**
@@ -128,15 +173,15 @@ class CriterionEvaluator {
128
173
  }
129
174
  if ((0, _apidomDatamodel.isStringElement)(type)) return (0, _apidomCore.toValue)(type);
130
175
  throw new _CriterionError.default('Criterion has an invalid "type"', {
131
- reason: 'invalid-type'
176
+ reason: _CriterionErrorReason.default.InvalidType
132
177
  });
133
178
  }
134
179
 
135
180
  /**
136
181
  * Extracts the expression `version` from a Criterion Expression Type Object,
137
182
  * or `undefined` when no version is declared (a bare-string `type`, or a type
138
- * object without a `version`) — in which case the spec default for the type
139
- * applies. A `version` that is present but not a string is malformed and
183
+ * object without a `version`) — in which case Arazzo 1.1.0's default for the
184
+ * type applies. A `version` that is present but not a string is malformed and
140
185
  * throws a {@link CriterionError} rather than silently defaulting.
141
186
  */
142
187
  #resolveVersion(criterion) {
@@ -148,13 +193,13 @@ class CriterionEvaluator {
148
193
  return (0, _apidomCore.toValue)(type.version);
149
194
  }
150
195
  throw new _CriterionError.default('Criterion has an invalid expression type "version"', {
151
- reason: 'invalid-version'
196
+ reason: _CriterionErrorReason.default.InvalidVersion
152
197
  });
153
198
  }
154
199
 
155
200
  /**
156
201
  * Enforces that a versioned condition type declares only the one version this
157
- * runner supports. An omitted version is accepted (it is the spec default);
202
+ * runner supports. An omitted version is accepted (Arazzo 1.1.0's default);
158
203
  * any other declared version throws a {@link CriterionError}.
159
204
  */
160
205
  #requireVersion(criterion, type, condition, supported) {
@@ -163,7 +208,7 @@ class CriterionEvaluator {
163
208
  throw new _CriterionError.default(`Unsupported ${type} version "${version}" (only ${supported})`, {
164
209
  condition,
165
210
  type,
166
- reason: 'unsupported-version'
211
+ reason: _CriterionErrorReason.default.UnsupportedVersion
167
212
  });
168
213
  }
169
214
  }
@@ -181,7 +226,7 @@ class CriterionEvaluator {
181
226
  if (!(0, _apidomDatamodel.isStringElement)(context) || !(0, _arazzoRuntimeExpression.test)((0, _apidomCore.toValue)(context))) {
182
227
  throw new _CriterionError.default('Criterion has a "context" that is not a valid runtime expression', {
183
228
  type,
184
- reason: 'invalid-context'
229
+ reason: _CriterionErrorReason.default.InvalidContext
185
230
  });
186
231
  }
187
232
  }
@@ -196,7 +241,7 @@ class CriterionEvaluator {
196
241
  if (!(0, _apidomDatamodel.isStringElement)(criterion.context)) {
197
242
  throw new _CriterionError.default(`Criterion type "${type}" requires a "context"`, {
198
243
  type,
199
- reason: 'missing-context'
244
+ reason: _CriterionErrorReason.default.MissingContext
200
245
  });
201
246
  }
202
247
  return resolve((0, _apidomCore.toValue)(criterion.context));
@@ -3,6 +3,8 @@ import { isStringElement } from '@speclynx/apidom-datamodel';
3
3
  import { isCriterionElement, isCriterionExpressionTypeElement } from '@speclynx/apidom-ns-arazzo-1';
4
4
  import { test as isRuntimeExpression } from '@swaggerexpert/arazzo-runtime-expression';
5
5
  import CriterionError from "../errors/CriterionError.mjs";
6
+ import CriterionEvaluationError from "../errors/CriterionEvaluationError.mjs";
7
+ import CriterionErrorReason from "./CriterionErrorReason.mjs";
6
8
  import RegexCriterionEvaluator from "./RegexCriterionEvaluator.mjs";
7
9
  import SimpleCriterionEvaluator from "./SimpleCriterionEvaluator.mjs";
8
10
  import JSONPathCriterionEvaluator from "./JSONPathCriterionEvaluator.mjs";
@@ -18,12 +20,20 @@ import XPathCriterionEvaluator from "./XPathCriterionEvaluator.mjs";
18
20
  * lenient evaluator).
19
21
  * @public
20
22
  */
23
+ /**
24
+ * The outcome of evaluating one criterion.
25
+ * @public
26
+ */
21
27
  /**
22
28
  * Options for the CriterionEvaluator.
29
+ *
30
+ * An injected evaluator signals a condition it cannot evaluate by throwing a
31
+ * {@link CriterionEvaluationError}, which fails the criterion; any other error
32
+ * it throws, a plain {@link CriterionError} included, propagates.
23
33
  * @public
24
34
  */
25
35
  /**
26
- * Evaluates an Arazzo Criterion Object to a boolean.
36
+ * Evaluates an Arazzo Criterion Object to a {@link CriterionOutcome}.
27
37
  *
28
38
  * Dispatches on the criterion's `type` (defaulting to `simple` when omitted):
29
39
  * - `simple` — evaluates the condition directly, resolving the runtime
@@ -32,12 +42,27 @@ import XPathCriterionEvaluator from "./XPathCriterionEvaluator.mjs";
32
42
  * pattern to the resolved value.
33
43
  * - `jsonpath` — resolves the `context` runtime expression, then applies the
34
44
  * JSONPath query to the resolved value (met when the query matches). Only the
35
- * `rfc9535` version is supported (the spec default); an explicit other version
36
- * throws.
45
+ * `rfc9535` version is supported; an explicit other version throws.
37
46
  * - `xpath` — resolves the `context` runtime expression, then applies the XPath
38
47
  * 3.1 query to the resolved XML value (met by the Effective Boolean Value).
39
- * Only the `xpath-31` version is supported (the spec default); an explicit
40
- * other version throws.
48
+ * Only the `xpath-31` version is supported; an explicit other version throws.
49
+ *
50
+ * Versions follow Arazzo 1.1.0's Expression Type Object: `rfc9535` and
51
+ * `xpath-31` are allowed values, and an omitted `version` defaults to them.
52
+ * Arazzo 1.0.x lists neither and requires `version`; this evaluator applies
53
+ * the 1.1.0 rule to every document, which accepts more but never evaluates a
54
+ * query under the wrong dialect.
55
+ *
56
+ * A condition that cannot be evaluated fails the criterion (`met: false`), as
57
+ * Arazzo 1.1.0 requires: a syntax error in the condition, a context the
58
+ * condition cannot apply to (non-XML data for `xpath`), or an XPath evaluation
59
+ * error. The type-specific evaluators report these as a
60
+ * {@link CriterionEvaluationError}, which this facade returns as the
61
+ * outcome's `error` so the caller can report it; any other error they raise
62
+ * propagates. A criterion that is malformed as written still throws: a
63
+ * missing `condition`, an unknown or unreadable `type`, an unsupported
64
+ * expression `version`, or a `context` that is missing or not a runtime
65
+ * expression.
41
66
  *
42
67
  * The evaluator is agnostic about how runtime expressions are resolved: the
43
68
  * caller supplies a {@link CriterionContextResolver} per evaluation, keeping
@@ -58,7 +83,8 @@ class CriterionEvaluator {
58
83
  }
59
84
 
60
85
  /**
61
- * Evaluates a criterion element, returning whether the condition is met.
86
+ * Evaluates a criterion element, returning whether the condition is met and,
87
+ * when it cannot be evaluated, why.
62
88
  *
63
89
  * `resolve` turns a runtime expression into a value: for `simple` it resolves
64
90
  * the expressions embedded in the condition; for the other types it resolves
@@ -67,7 +93,7 @@ class CriterionEvaluator {
67
93
  evaluate(criterion, resolve) {
68
94
  if (!isCriterionElement(criterion)) {
69
95
  throw new CriterionError('Element is not a Criterion Object', {
70
- reason: 'invalid-criterion'
96
+ reason: CriterionErrorReason.InvalidCriterion
71
97
  });
72
98
  }
73
99
  const condition = isStringElement(criterion.condition) ? toValue(criterion.condition) : undefined;
@@ -75,34 +101,52 @@ class CriterionEvaluator {
75
101
  // (e.g. an empty regex would match unconditionally), so reject it too.
76
102
  if (condition === undefined || condition === '') {
77
103
  throw new CriterionError('Criterion is missing a "condition"', {
78
- reason: 'missing-condition'
104
+ reason: CriterionErrorReason.MissingCondition
79
105
  });
80
106
  }
81
107
  const type = this.#resolveType(criterion);
82
108
  this.#requireValidContext(criterion, type);
83
- switch (type) {
84
- case 'simple':
85
- return this.#simple.evaluate(condition, resolve);
86
- case 'regex':
87
- return this.#regex.evaluate(condition, this.#resolveContext(criterion, type, resolve));
88
- case 'jsonpath':
89
- // only rfc9535 is supported; it is the spec default, so an omitted
90
- // version is accepted. draft-goessner has different semantics and is
91
- // rejected rather than mis-evaluated.
92
- this.#requireVersion(criterion, type, condition, 'rfc9535');
93
- return this.#jsonpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
94
- case 'xpath':
95
- // only XPath 3.1 is supported; xpath-31 is the spec default, so an
96
- // omitted version is accepted. Older versions are rejected.
97
- this.#requireVersion(criterion, type, condition, 'xpath-31');
98
- return this.#xpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
99
- default:
100
- throw new CriterionError(`Unknown criterion type "${type}"`, {
101
- condition,
102
- type,
103
- reason: 'unknown-type'
104
- });
109
+ let met;
110
+ try {
111
+ switch (type) {
112
+ case 'simple':
113
+ met = this.#simple.evaluate(condition, resolve);
114
+ break;
115
+ case 'regex':
116
+ met = this.#regex.evaluate(condition, this.#resolveContext(criterion, type, resolve));
117
+ break;
118
+ case 'jsonpath':
119
+ // only rfc9535 is supported, Arazzo 1.1.0's default, so an omitted
120
+ // version is accepted. draft-goessner has different semantics and is
121
+ // rejected rather than mis-evaluated.
122
+ this.#requireVersion(criterion, type, condition, 'rfc9535');
123
+ met = this.#jsonpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
124
+ break;
125
+ case 'xpath':
126
+ // only XPath 3.1 is supported; xpath-31 is Arazzo 1.1.0's default, so
127
+ // an omitted version is accepted. Older versions are rejected.
128
+ this.#requireVersion(criterion, type, condition, 'xpath-31');
129
+ met = this.#xpath.evaluate(condition, this.#resolveContext(criterion, type, resolve));
130
+ break;
131
+ default:
132
+ throw new CriterionError(`Unknown criterion type "${type}"`, {
133
+ condition,
134
+ type,
135
+ reason: CriterionErrorReason.UnknownType
136
+ });
137
+ }
138
+ } catch (error) {
139
+ // Arazzo 1.1.0: a condition that cannot be evaluated MUST evaluate to fail.
140
+ // The structural checks throw a plain CriterionError and still propagate.
141
+ if (error instanceof CriterionEvaluationError) return {
142
+ met: false,
143
+ error
144
+ };
145
+ throw error;
105
146
  }
147
+ return {
148
+ met
149
+ };
106
150
  }
107
151
 
108
152
  /**
@@ -119,15 +163,15 @@ class CriterionEvaluator {
119
163
  }
120
164
  if (isStringElement(type)) return toValue(type);
121
165
  throw new CriterionError('Criterion has an invalid "type"', {
122
- reason: 'invalid-type'
166
+ reason: CriterionErrorReason.InvalidType
123
167
  });
124
168
  }
125
169
 
126
170
  /**
127
171
  * Extracts the expression `version` from a Criterion Expression Type Object,
128
172
  * or `undefined` when no version is declared (a bare-string `type`, or a type
129
- * object without a `version`) — in which case the spec default for the type
130
- * applies. A `version` that is present but not a string is malformed and
173
+ * object without a `version`) — in which case Arazzo 1.1.0's default for the
174
+ * type applies. A `version` that is present but not a string is malformed and
131
175
  * throws a {@link CriterionError} rather than silently defaulting.
132
176
  */
133
177
  #resolveVersion(criterion) {
@@ -139,13 +183,13 @@ class CriterionEvaluator {
139
183
  return toValue(type.version);
140
184
  }
141
185
  throw new CriterionError('Criterion has an invalid expression type "version"', {
142
- reason: 'invalid-version'
186
+ reason: CriterionErrorReason.InvalidVersion
143
187
  });
144
188
  }
145
189
 
146
190
  /**
147
191
  * Enforces that a versioned condition type declares only the one version this
148
- * runner supports. An omitted version is accepted (it is the spec default);
192
+ * runner supports. An omitted version is accepted (Arazzo 1.1.0's default);
149
193
  * any other declared version throws a {@link CriterionError}.
150
194
  */
151
195
  #requireVersion(criterion, type, condition, supported) {
@@ -154,7 +198,7 @@ class CriterionEvaluator {
154
198
  throw new CriterionError(`Unsupported ${type} version "${version}" (only ${supported})`, {
155
199
  condition,
156
200
  type,
157
- reason: 'unsupported-version'
201
+ reason: CriterionErrorReason.UnsupportedVersion
158
202
  });
159
203
  }
160
204
  }
@@ -172,7 +216,7 @@ class CriterionEvaluator {
172
216
  if (!isStringElement(context) || !isRuntimeExpression(toValue(context))) {
173
217
  throw new CriterionError('Criterion has a "context" that is not a valid runtime expression', {
174
218
  type,
175
- reason: 'invalid-context'
219
+ reason: CriterionErrorReason.InvalidContext
176
220
  });
177
221
  }
178
222
  }
@@ -187,7 +231,7 @@ class CriterionEvaluator {
187
231
  if (!isStringElement(criterion.context)) {
188
232
  throw new CriterionError(`Criterion type "${type}" requires a "context"`, {
189
233
  type,
190
- reason: 'missing-context'
234
+ reason: CriterionErrorReason.MissingContext
191
235
  });
192
236
  }
193
237
  return resolve(toValue(criterion.context));