@usearazzo/runner 0.0.0-stage → 1.0.1-alpha.7

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 (151) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +20 -0
  3. package/README.md +784 -2
  4. package/dist/arazzo-runner.browser.js +111247 -0
  5. package/dist/arazzo-runner.browser.min.js +1 -0
  6. package/package.json +88 -4
  7. package/src/action/ActionResolver.cjs +160 -0
  8. package/src/action/ActionResolver.mjs +150 -0
  9. package/src/assembler/OpenAPI2DocumentAssembler.cjs +111 -0
  10. package/src/assembler/OpenAPI2DocumentAssembler.mjs +104 -0
  11. package/src/assembler/OpenAPI30DocumentAssembler.cjs +110 -0
  12. package/src/assembler/OpenAPI30DocumentAssembler.mjs +103 -0
  13. package/src/assembler/OpenAPI31DocumentAssembler.cjs +111 -0
  14. package/src/assembler/OpenAPI31DocumentAssembler.mjs +104 -0
  15. package/src/assembler/OpenAPIDocumentAssembler.cjs +58 -0
  16. package/src/assembler/OpenAPIDocumentAssembler.mjs +50 -0
  17. package/src/client/HTTPClient.cjs +5 -0
  18. package/src/client/HTTPClient.mjs +1 -0
  19. package/src/client/HTTPClientFetch.cjs +16 -0
  20. package/src/client/HTTPClientFetch.mjs +10 -0
  21. package/src/client/OpenAPIOperationRequest.cjs +5 -0
  22. package/src/client/OpenAPIOperationRequest.mjs +1 -0
  23. package/src/client/OpenAPIOperationResponse.cjs +42 -0
  24. package/src/client/OpenAPIOperationResponse.mjs +36 -0
  25. package/src/client/OpenAPIOperationResponseNormalizer.cjs +48 -0
  26. package/src/client/OpenAPIOperationResponseNormalizer.mjs +40 -0
  27. package/src/client/ParameterDelivery.cjs +119 -0
  28. package/src/client/ParameterDelivery.mjs +113 -0
  29. package/src/constants.cjs +9 -0
  30. package/src/constants.mjs +3 -0
  31. package/src/criterion/CriterionEvaluator.cjs +205 -0
  32. package/src/criterion/CriterionEvaluator.mjs +196 -0
  33. package/src/criterion/JSONPathCriterionEvaluator.cjs +47 -0
  34. package/src/criterion/JSONPathCriterionEvaluator.mjs +40 -0
  35. package/src/criterion/RegexCriterionEvaluator.cjs +67 -0
  36. package/src/criterion/RegexCriterionEvaluator.mjs +60 -0
  37. package/src/criterion/SimpleCriterionEvaluator.cjs +50 -0
  38. package/src/criterion/SimpleCriterionEvaluator.mjs +43 -0
  39. package/src/criterion/XPathCriterionEvaluator.cjs +117 -0
  40. package/src/criterion/XPathCriterionEvaluator.mjs +109 -0
  41. package/src/document/APIDocument.cjs +35 -0
  42. package/src/document/APIDocument.mjs +30 -0
  43. package/src/document/ArazzoDocument.cjs +81 -0
  44. package/src/document/ArazzoDocument.mjs +74 -0
  45. package/src/document/ArazzoSourceDescriptionIndex.cjs +26 -0
  46. package/src/document/ArazzoSourceDescriptionIndex.mjs +20 -0
  47. package/src/document/ArazzoStepIndex.cjs +25 -0
  48. package/src/document/ArazzoStepIndex.mjs +19 -0
  49. package/src/document/ArazzoWorkflowIndex.cjs +17 -0
  50. package/src/document/ArazzoWorkflowIndex.mjs +11 -0
  51. package/src/document/OpenAPIDocument.cjs +28 -0
  52. package/src/document/OpenAPIDocument.mjs +21 -0
  53. package/src/document/OpenAPIOperationIndex.cjs +24 -0
  54. package/src/document/OpenAPIOperationIndex.mjs +18 -0
  55. package/src/document/ParameterIdentity.cjs +93 -0
  56. package/src/document/ParameterIdentity.mjs +88 -0
  57. package/src/document/openapi-types.cjs +5 -0
  58. package/src/document/openapi-types.mjs +1 -0
  59. package/src/errors/ArazzoRunnerError.cjs +10 -0
  60. package/src/errors/ArazzoRunnerError.mjs +5 -0
  61. package/src/errors/AssemblerError.cjs +11 -0
  62. package/src/errors/AssemblerError.mjs +4 -0
  63. package/src/errors/ClientError.cjs +11 -0
  64. package/src/errors/ClientError.mjs +4 -0
  65. package/src/errors/CriterionError.cjs +11 -0
  66. package/src/errors/CriterionError.mjs +4 -0
  67. package/src/errors/ExecutionError.cjs +11 -0
  68. package/src/errors/ExecutionError.mjs +4 -0
  69. package/src/errors/ExtractionError.cjs +11 -0
  70. package/src/errors/ExtractionError.mjs +4 -0
  71. package/src/errors/InvalidEntryDocumentError.cjs +11 -0
  72. package/src/errors/InvalidEntryDocumentError.mjs +4 -0
  73. package/src/errors/NormalizationError.cjs +11 -0
  74. package/src/errors/NormalizationError.mjs +4 -0
  75. package/src/errors/ResolverError.cjs +11 -0
  76. package/src/errors/ResolverError.mjs +4 -0
  77. package/src/errors/RuntimeExpressionError.cjs +11 -0
  78. package/src/errors/RuntimeExpressionError.mjs +4 -0
  79. package/src/errors/UnmatchedProviderError.cjs +11 -0
  80. package/src/errors/UnmatchedProviderError.mjs +4 -0
  81. package/src/executor/ArazzoWorkflowLocatorNormalizer.cjs +147 -0
  82. package/src/executor/ArazzoWorkflowLocatorNormalizer.mjs +137 -0
  83. package/src/executor/OpenAPIOperationExecutor.cjs +457 -0
  84. package/src/executor/OpenAPIOperationExecutor.mjs +446 -0
  85. package/src/executor/OpenAPIOperationLocatorNormalizer.cjs +197 -0
  86. package/src/executor/OpenAPIOperationLocatorNormalizer.mjs +189 -0
  87. package/src/executor/SourceDescriptionsLocatorNormalizer.cjs +44 -0
  88. package/src/executor/SourceDescriptionsLocatorNormalizer.mjs +38 -0
  89. package/src/executor/StepExecutor.cjs +333 -0
  90. package/src/executor/StepExecutor.mjs +326 -0
  91. package/src/executor/StepRetryRunner.cjs +226 -0
  92. package/src/executor/StepRetryRunner.mjs +215 -0
  93. package/src/executor/StepTransitionInterpreter.cjs +159 -0
  94. package/src/executor/StepTransitionInterpreter.mjs +151 -0
  95. package/src/executor/WorkflowCallStack.cjs +116 -0
  96. package/src/executor/WorkflowCallStack.mjs +106 -0
  97. package/src/executor/WorkflowExecutor.cjs +1091 -0
  98. package/src/executor/WorkflowExecutor.mjs +1077 -0
  99. package/src/executor/abort.cjs +70 -0
  100. package/src/executor/abort.mjs +60 -0
  101. package/src/expression/RuntimeExpressionContext.cjs +5 -0
  102. package/src/expression/RuntimeExpressionContext.mjs +1 -0
  103. package/src/expression/RuntimeExpressionEvaluator.cjs +337 -0
  104. package/src/expression/RuntimeExpressionEvaluator.mjs +328 -0
  105. package/src/extractor/ArazzoStepExtractor.cjs +48 -0
  106. package/src/extractor/ArazzoStepExtractor.mjs +41 -0
  107. package/src/extractor/ArazzoWorkflowExtractor.cjs +50 -0
  108. package/src/extractor/ArazzoWorkflowExtractor.mjs +43 -0
  109. package/src/extractor/OpenAPIOperationExtractor.cjs +84 -0
  110. package/src/extractor/OpenAPIOperationExtractor.mjs +77 -0
  111. package/src/index.cjs +419 -0
  112. package/src/index.mjs +73 -0
  113. package/src/normalizer/ArazzoStepNormalizer.cjs +23 -0
  114. package/src/normalizer/ArazzoStepNormalizer.mjs +17 -0
  115. package/src/normalizer/ArazzoWorkflowNormalizer.cjs +214 -0
  116. package/src/normalizer/ArazzoWorkflowNormalizer.mjs +206 -0
  117. package/src/normalizer/OpenAPI2OperationNormalizer.cjs +128 -0
  118. package/src/normalizer/OpenAPI2OperationNormalizer.mjs +120 -0
  119. package/src/normalizer/OpenAPI30OperationNormalizer.cjs +129 -0
  120. package/src/normalizer/OpenAPI30OperationNormalizer.mjs +121 -0
  121. package/src/normalizer/OpenAPI31OperationNormalizer.cjs +131 -0
  122. package/src/normalizer/OpenAPI31OperationNormalizer.mjs +123 -0
  123. package/src/normalizer/OpenAPIOperationNormalizer.cjs +58 -0
  124. package/src/normalizer/OpenAPIOperationNormalizer.mjs +50 -0
  125. package/src/registry/DocumentRegistry.cjs +160 -0
  126. package/src/registry/DocumentRegistry.mjs +151 -0
  127. package/src/registry/providers/ArazzoDocumentRegistryProvider.cjs +154 -0
  128. package/src/registry/providers/ArazzoDocumentRegistryProvider.mjs +146 -0
  129. package/src/registry/providers/DocumentRegistryProvider.cjs +31 -0
  130. package/src/registry/providers/DocumentRegistryProvider.mjs +25 -0
  131. package/src/registry/providers/OpenAPIDocumentRegistryProvider.cjs +122 -0
  132. package/src/registry/providers/OpenAPIDocumentRegistryProvider.mjs +114 -0
  133. package/src/resolver/ArazzoValueResolver.cjs +42 -0
  134. package/src/resolver/ArazzoValueResolver.mjs +37 -0
  135. package/src/resolver/OpenAPIOperationParameterResolver.cjs +117 -0
  136. package/src/resolver/OpenAPIOperationParameterResolver.mjs +110 -0
  137. package/src/resolver/OutputResolver.cjs +71 -0
  138. package/src/resolver/OutputResolver.mjs +64 -0
  139. package/src/resolver/RequestBodyResolver.cjs +227 -0
  140. package/src/resolver/RequestBodyResolver.mjs +220 -0
  141. package/src/resolver/ResolverScope.cjs +5 -0
  142. package/src/resolver/ResolverScope.mjs +1 -0
  143. package/src/resolver/StepParameterResolver.cjs +22 -0
  144. package/src/resolver/StepParameterResolver.mjs +15 -0
  145. package/src/resolver/WorkflowParameterResolver.cjs +73 -0
  146. package/src/resolver/WorkflowParameterResolver.mjs +66 -0
  147. package/src/state/WorkflowExecutionState.cjs +99 -0
  148. package/src/state/WorkflowExecutionState.mjs +93 -0
  149. package/src/vendor/swagger-client.cjs +1 -0
  150. package/src/vendor/swagger-client.mjs +1 -0
  151. package/types/runner.d.ts +2341 -0
@@ -0,0 +1,189 @@
1
+ import { test as isRuntimeExpression } from '@swaggerexpert/arazzo-runtime-expression';
2
+ import { URIFragmentIdentifier, testJSONPointer } from '@swaggerexpert/json-pointer';
3
+ import { url } from '@speclynx/apidom-reference/configuration/empty';
4
+ import OpenAPIDocument from "../document/OpenAPIDocument.mjs";
5
+ import RuntimeExpressionEvaluator from "../expression/RuntimeExpressionEvaluator.mjs";
6
+ import ExtractionError from "../errors/ExtractionError.mjs";
7
+ import SourceDescriptionsLocatorNormalizer from "./SourceDescriptionsLocatorNormalizer.mjs";
8
+ /**
9
+ * A canonical OpenAPI operation locator: the OpenAPI document that owns the
10
+ * operation and a JSON Pointer locating the operation within it
11
+ * (`/paths/{path}/{method}`).
12
+ *
13
+ * A step names its operation in one of several ways — a plain `operationId`, a
14
+ * `$sourceDescriptions` runtime expression, or an `operationPath` — which are
15
+ * all locators for the same thing. The normalizer reduces any of them to this
16
+ * single canonical form.
17
+ * @public
18
+ */
19
+ /**
20
+ * Normalizes a step's OpenAPI operation locator to a canonical
21
+ * {@link OpenAPIOperationLocator} — the OpenAPI document and a JSON Pointer to
22
+ * the operation within it.
23
+ *
24
+ * A step names its operation with either `operationId` or `operationPath`:
25
+ * - `operationId` is a plain operation name, or a whole runtime expression
26
+ * `$sourceDescriptions.{name}.{operationId}`. Per the Arazzo Specification, a
27
+ * plain name is only valid when a single (non-arazzo) source description is
28
+ * defined; multiple sources require the expression form. A plain name is
29
+ * therefore resolved against the first source description that has it.
30
+ * - `operationPath` embeds a `{$sourceDescriptions.{name}.url}` runtime
31
+ * expression followed by a JSON Pointer to the operation.
32
+ *
33
+ * The `type` declared on a source description is not trusted (it may be absent
34
+ * or wrong); a source is classified by what it actually parses to. The
35
+ * normalizer acquires the referenced source documents from the registry on
36
+ * demand.
37
+ * @public
38
+ */
39
+ class OpenAPIOperationLocatorNormalizer extends SourceDescriptionsLocatorNormalizer {
40
+ /**
41
+ * Normalizes a plain or expression `operationId` to a canonical locator.
42
+ *
43
+ * A whole runtime expression (`$sourceDescriptions.{name}.{operationId}`) is
44
+ * resolved against its named source; a plain name is resolved against the
45
+ * first source description that has it.
46
+ */
47
+ async normalizeOperationId(operationId, document) {
48
+ if (isRuntimeExpression(operationId)) {
49
+ return this.#resolveSourced(operationId, document);
50
+ }
51
+ return this.#resolvePlainOperationId(operationId, document);
52
+ }
53
+
54
+ /**
55
+ * Normalizes an `operationPath` —
56
+ * `{$sourceDescriptions.{name}.url}#/json/pointer` — to a canonical locator.
57
+ *
58
+ * The reference is interpolated (resolving
59
+ * `{$sourceDescriptions.{name}.url}` to the source's URL) into a URI
60
+ * reference; its fragment is the JSON Pointer to the operation (decoded from
61
+ * the URI fragment representation) and the part before the fragment is the
62
+ * source document to acquire.
63
+ *
64
+ * The runner only needs a URL and a JSON Pointer, so it does not enforce that
65
+ * the source description is identified by a runtime expression rather than a
66
+ * literal URL (a literal URL interpolates to itself and still resolves) — that
67
+ * is a conformance concern for the validator. It fails only when the URL does
68
+ * not resolve to an OpenAPI source or the JSON Pointer is invalid.
69
+ */
70
+ async normalizeOperationPath(operationPath, document) {
71
+ const evaluator = new RuntimeExpressionEvaluator({}, {
72
+ strict: false,
73
+ document,
74
+ registry: this.registry
75
+ });
76
+ const uriReference = evaluator.interpolate(operationPath);
77
+ const documentURI = url.sanitize(url.stripHash(uriReference));
78
+ if (documentURI === '') {
79
+ throw new ExtractionError(`operationPath "${operationPath}" does not resolve to a source description URL`, {
80
+ pointer: operationPath,
81
+ uri: document.uri
82
+ });
83
+ }
84
+ const jsonPointer = URIFragmentIdentifier.fromURIReference(uriReference);
85
+ if (!testJSONPointer(jsonPointer)) {
86
+ throw new ExtractionError(`operationPath "${operationPath}" has an invalid JSON Pointer`, {
87
+ pointer: operationPath,
88
+ uri: document.uri
89
+ });
90
+ }
91
+ const source = await this.registry.acquire(documentURI);
92
+ if (!OpenAPIDocument.is(source)) {
93
+ throw new ExtractionError(`operationPath "${operationPath}" does not reference an OpenAPI document`, {
94
+ pointer: operationPath,
95
+ uri: documentURI
96
+ });
97
+ }
98
+ return {
99
+ document: source,
100
+ jsonPointer
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Parses a `$sourceDescriptions.{name}.{reference}` expression, asserting
106
+ * it is a source-descriptions expression.
107
+ */
108
+ #parseSourceDescriptionsExpression(expression) {
109
+ const parsed = this.parseSourceDescriptionsExpression(expression);
110
+ if (parsed === undefined) {
111
+ throw new ExtractionError(`Unsupported operationId expression "${expression}"`, {
112
+ operationId: expression
113
+ });
114
+ }
115
+ return parsed;
116
+ }
117
+ async #resolveSourced(expression, document) {
118
+ const {
119
+ sourceName,
120
+ reference: operationId
121
+ } = this.#parseSourceDescriptionsExpression(expression);
122
+ const openapiDocument = await this.#sourceDocument(sourceName, document);
123
+ const jsonPointer = openapiDocument.operationIndex.get(operationId);
124
+ if (jsonPointer === undefined) {
125
+ throw new ExtractionError(`Operation "${operationId}" not found in source description "${sourceName}"`, {
126
+ operationId,
127
+ uri: openapiDocument.uri
128
+ });
129
+ }
130
+ return {
131
+ document: openapiDocument,
132
+ jsonPointer
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Resolves a plain operationId against the single OpenAPI source description.
138
+ *
139
+ * Per the Arazzo Specification a plain operationId is only valid when a single
140
+ * non-arazzo source description is defined; multiple such sources require the
141
+ * `$sourceDescriptions` expression form. The first OpenAPI source is therefore
142
+ * the one it targets: the operationId is looked up there and the lookup fails
143
+ * if it is absent — subsequent OpenAPI sources are not searched (their
144
+ * presence would make the plain operationId ambiguous, and hence invalid).
145
+ * Non-OpenAPI (arazzo) sources are skipped. Sources are acquired one at a time
146
+ * until the first OpenAPI source is reached.
147
+ */
148
+ async #resolvePlainOperationId(operationId, document) {
149
+ for (const uri of document.sourceDescriptionURIs()) {
150
+ const source = await this.registry.acquire(uri);
151
+ if (!OpenAPIDocument.is(source)) continue;
152
+ const jsonPointer = source.operationIndex.get(operationId);
153
+ if (jsonPointer === undefined) {
154
+ throw new ExtractionError(`Operation "${operationId}" not found in source description at "${source.uri}"`, {
155
+ operationId,
156
+ uri: source.uri
157
+ });
158
+ }
159
+ return {
160
+ document: source,
161
+ jsonPointer
162
+ };
163
+ }
164
+ throw new ExtractionError(`Operation "${operationId}" has no OpenAPI source description`, {
165
+ operationId,
166
+ uri: document.uri
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Acquires a source description by name and asserts it is an OpenAPI document.
172
+ */
173
+ async #sourceDocument(sourceName, document) {
174
+ const uri = document.resolveSourceDescriptionURI(sourceName);
175
+ if (uri === undefined) {
176
+ throw new ExtractionError(`Source description "${sourceName}" not found`, {
177
+ uri: document.uri
178
+ });
179
+ }
180
+ const source = await this.registry.acquire(uri);
181
+ if (!OpenAPIDocument.is(source)) {
182
+ throw new ExtractionError(`Source description "${sourceName}" is not an OpenAPI document`, {
183
+ uri
184
+ });
185
+ }
186
+ return source;
187
+ }
188
+ }
189
+ export default OpenAPIOperationLocatorNormalizer;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _parser = require("@usearazzo/parser");
8
+ /**
9
+ * Base of the locator normalizers that resolve
10
+ * `$sourceDescriptions.{name}.{reference}` runtime expressions — the OpenAPI
11
+ * operation locator and the Arazzo workflow locator. Owns the registry handle
12
+ * and the one grammar-facing parse, so the expression node's name and field
13
+ * shape are encoded once rather than per subclass.
14
+ * @public
15
+ */
16
+ class SourceDescriptionsLocatorNormalizer {
17
+ registry;
18
+ constructor(registry) {
19
+ this.registry = registry;
20
+ }
21
+
22
+ /**
23
+ * Parses a `$sourceDescriptions.{name}.{reference}` expression, or returns
24
+ * `undefined` when the string is not one (whether it fails to parse or
25
+ * parses to another expression kind) — each subclass raises its own error
26
+ * type, message, and context for its own position.
27
+ */
28
+ parseSourceDescriptionsExpression(expression) {
29
+ const {
30
+ result,
31
+ tree
32
+ } = (0, _parser.parseRuntimeExpression)(expression);
33
+ if (!result.success || tree.type !== 'SourceDescriptionsExpression') return undefined;
34
+ const {
35
+ sourceName,
36
+ reference
37
+ } = tree;
38
+ return {
39
+ sourceName,
40
+ reference
41
+ };
42
+ }
43
+ }
44
+ var _default = exports.default = SourceDescriptionsLocatorNormalizer;
@@ -0,0 +1,38 @@
1
+ import { parseRuntimeExpression } from '@usearazzo/parser';
2
+ /**
3
+ * Base of the locator normalizers that resolve
4
+ * `$sourceDescriptions.{name}.{reference}` runtime expressions — the OpenAPI
5
+ * operation locator and the Arazzo workflow locator. Owns the registry handle
6
+ * and the one grammar-facing parse, so the expression node's name and field
7
+ * shape are encoded once rather than per subclass.
8
+ * @public
9
+ */
10
+ class SourceDescriptionsLocatorNormalizer {
11
+ registry;
12
+ constructor(registry) {
13
+ this.registry = registry;
14
+ }
15
+
16
+ /**
17
+ * Parses a `$sourceDescriptions.{name}.{reference}` expression, or returns
18
+ * `undefined` when the string is not one (whether it fails to parse or
19
+ * parses to another expression kind) — each subclass raises its own error
20
+ * type, message, and context for its own position.
21
+ */
22
+ parseSourceDescriptionsExpression(expression) {
23
+ const {
24
+ result,
25
+ tree
26
+ } = parseRuntimeExpression(expression);
27
+ if (!result.success || tree.type !== 'SourceDescriptionsExpression') return undefined;
28
+ const {
29
+ sourceName,
30
+ reference
31
+ } = tree;
32
+ return {
33
+ sourceName,
34
+ reference
35
+ };
36
+ }
37
+ }
38
+ export default SourceDescriptionsLocatorNormalizer;
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = exports.STEP_TARGET_FIELDS = void 0;
8
+ var _apidomCore = require("@speclynx/apidom-core");
9
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
10
+ var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
11
+ var _RuntimeExpressionEvaluator = _interopRequireDefault(require("../expression/RuntimeExpressionEvaluator.cjs"));
12
+ var _CriterionEvaluator = _interopRequireDefault(require("../criterion/CriterionEvaluator.cjs"));
13
+ var _OpenAPIOperationParameterResolver = _interopRequireDefault(require("../resolver/OpenAPIOperationParameterResolver.cjs"));
14
+ var _RequestBodyResolver = _interopRequireDefault(require("../resolver/RequestBodyResolver.cjs"));
15
+ var _OutputResolver = _interopRequireDefault(require("../resolver/OutputResolver.cjs"));
16
+ var _ActionResolver = _interopRequireDefault(require("../action/ActionResolver.cjs"));
17
+ var _OpenAPIOperationLocatorNormalizer = _interopRequireDefault(require("./OpenAPIOperationLocatorNormalizer.cjs"));
18
+ var _ExecutionError = _interopRequireDefault(require("../errors/ExecutionError.cjs"));
19
+ var _RuntimeExpressionError = _interopRequireDefault(require("../errors/RuntimeExpressionError.cjs"));
20
+ var _abort = require("./abort.cjs");
21
+ /**
22
+ * The mutually exclusive fields by which a step names its target. A step may
23
+ * declare at most one. Extend this as the Arazzo Specification adds targets
24
+ * (e.g. `channelPath` in 1.1.0) so every exclusivity check stays complete —
25
+ * both this module's and the workflow executor's, which makes the same check
26
+ * for a step it intercepts before {@link StepExecutor.execute}.
27
+ * @internal
28
+ */
29
+ const STEP_TARGET_FIELDS = exports.STEP_TARGET_FIELDS = ['operationId', 'operationPath', 'workflowId'];
30
+
31
+ /**
32
+ * A read-only source of the runtime expression context for a step.
33
+ *
34
+ * The step executor only reads accumulated run state — it produces the
35
+ * pre-request context (`toContext()`) and the post-request context
36
+ * (`toContext(request, response)`) — and never mutates it, so it depends on
37
+ * this narrow interface rather than the full execution state.
38
+ * @public
39
+ */
40
+
41
+ /**
42
+ * Options for the StepExecutor.
43
+ * @public
44
+ */
45
+
46
+ /**
47
+ * The outcome of executing a single step.
48
+ * @public
49
+ */
50
+
51
+ /**
52
+ * Executes a single Arazzo step that invokes an OpenAPI operation.
53
+ *
54
+ * The pipeline locates the step's operation (`operationId` / `operationPath`),
55
+ * resolves the step's parameters and request body against the pre-request
56
+ * context, delegates to {@link OpenAPIOperationExecutor} to run the located
57
+ * operation, then evaluates `successCriteria`, resolves `outputs`, and selects
58
+ * the `onSuccess`/`onFailure` action against the post-request context.
59
+ *
60
+ * It reads run state through a {@link ContextSource} and mutates nothing — the
61
+ * caller records the returned outputs and interprets the returned action. A step
62
+ * targeting a `workflowId` (a sub-workflow) is not an operation step and throws
63
+ * {@link ExecutionError}; running sub-workflows is the workflow executor's
64
+ * concern. A received response with unmet criteria is a normal
65
+ * `successful: false` outcome; malformed input (e.g. an invalid criterion or
66
+ * expression) throws, as does a run the caller has cancelled through an
67
+ * `AbortSignal` in the execute options.
68
+ * @public
69
+ */
70
+ class StepExecutor {
71
+ #document;
72
+ #registry;
73
+ #locatorNormalizer;
74
+ #operationExecutor;
75
+ #parameterResolver = new _OpenAPIOperationParameterResolver.default();
76
+ #requestBodyResolver = new _RequestBodyResolver.default();
77
+ #outputResolver = new _OutputResolver.default();
78
+ #criterionEvaluator = new _CriterionEvaluator.default();
79
+ #actionResolver = new _ActionResolver.default();
80
+ constructor(options) {
81
+ this.#document = options.document;
82
+ this.#registry = options.registry;
83
+ this.#locatorNormalizer = new _OpenAPIOperationLocatorNormalizer.default(options.registry);
84
+ this.#operationExecutor = options.operationExecutor;
85
+ }
86
+
87
+ /**
88
+ * A step executor bound to the given document, sharing this one's registry
89
+ * and operation executor — this instance itself when the document is
90
+ * already the one it is bound to.
91
+ *
92
+ * A step executor's identity includes the document its steps belong to: a
93
+ * plain `operationId` resolves against *that* document's source
94
+ * descriptions, and `$components` / `$sourceDescriptions` expressions
95
+ * resolve against it too. A cross-document workflow run therefore derives
96
+ * a sibling per foreign document rather than re-pointing this one.
97
+ */
98
+ forDocument(document) {
99
+ if (document === this.#document) return this;
100
+ return new StepExecutor({
101
+ document,
102
+ registry: this.#registry,
103
+ operationExecutor: this.#operationExecutor
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Executes the step against the given run state, returning its outcome.
109
+ *
110
+ * `executeOptions` is an opaque bag passed through to the operation executor
111
+ * (e.g. a `server` to run the step against, or an `AbortSignal`). The
112
+ * Arazzo-derived options (`operationId`, `parameters`, `requestBody`) always
113
+ * take precedence over it.
114
+ *
115
+ * A `signal` in that bag is honored here and not only at the transport: an
116
+ * already-aborted signal throws `reason: 'aborted'` instead of issuing a
117
+ * request nobody is waiting for, whose wire-level cancellation would surface
118
+ * as a transport {@link ClientError} — a request that failed on its own terms
119
+ * — rather than as the withdrawal it is.
120
+ */
121
+ async execute(step, state, executeOptions = {}) {
122
+ const stepId = (0, _apidomCore.toValue)(step.stepId);
123
+
124
+ // a step's target fields are mutually exclusive; declaring more than one is
125
+ // malformed and has no defined resolution.
126
+ const declaredTargets = STEP_TARGET_FIELDS.filter(field => (0, _apidomDatamodel.isStringElement)(step[field]));
127
+ if (declaredTargets.length > 1) {
128
+ throw new _ExecutionError.default(`Step "${stepId}" declares more than one of ${STEP_TARGET_FIELDS.join(', ')} (mutually exclusive)`, {
129
+ stepId,
130
+ reason: 'ambiguous-target'
131
+ });
132
+ }
133
+ if ((0, _apidomDatamodel.isStringElement)(step.workflowId)) {
134
+ throw new _ExecutionError.default(`Step "${stepId}" targets a workflowId; use the workflow executor`, {
135
+ stepId,
136
+ reason: 'workflow-step'
137
+ });
138
+ }
139
+
140
+ // locate which operation the step invokes — reduced to a (document, JSON
141
+ // Pointer) locator that the operation executor extracts, normalizes,
142
+ // assembles into a standalone doc, and runs.
143
+ const locator = await this.#locateOperation(step, stepId);
144
+
145
+ // resolve parameters and request body against the pre-request context.
146
+ // keyed by '{in}.{name}' rather than bare name, so two parameters that
147
+ // legally differ only in their location each reach their own place.
148
+ const scope = {
149
+ stepId
150
+ };
151
+ const preContext = state.toContext();
152
+ const parameters = this.#parameterResolver.resolve(step.parameters, expression => this.#evaluate(preContext, expression), scope);
153
+ const requestBody = this.#requestBodyResolver.resolve(step.requestBody, expression => this.#evaluate(preContext, expression), scope);
154
+
155
+ // checked here rather than on entry: locating the operation and resolving
156
+ // the expressions above are awaited, so a run cancelled during them would
157
+ // still reach this line — and this is the last moment before the step has a
158
+ // live side effect. The signal stays in the bag, so a request already in
159
+ // flight is aborted by the transport as before.
160
+ const signal = (0, _abort.readAbortSignal)(executeOptions);
161
+ (0, _abort.throwIfAborted)(signal, {
162
+ stepId
163
+ });
164
+
165
+ // the Arazzo-derived parameters and request body are spread after the opaque
166
+ // executeOptions bag so they take precedence over it. the operation executor
167
+ // then forces the operation target on top of this before executing.
168
+ let response;
169
+ try {
170
+ response = await this.#operationExecutor.execute(locator, {
171
+ ...executeOptions,
172
+ parameters,
173
+ ...this.#requestBodyOptions(requestBody)
174
+ });
175
+ } catch (error) {
176
+ // a transport that honors the signal rejects the request it was told to
177
+ // drop, which the operation executor wraps as a `ClientError` — the shape
178
+ // reserved for a request that failed on its own terms. Cancelled while
179
+ // cancelled, it did not: reporting it as the abort keeps one error shape
180
+ // whether the run was withdrawn between two steps or mid-flight, and keeps
181
+ // the whole feature from reading differently per transport.
182
+ (0, _abort.throwIfAborted)(signal, {
183
+ stepId
184
+ });
185
+ throw error;
186
+ }
187
+
188
+ // evaluate criteria, outputs, and the next action against the post-request
189
+ // context (with $request / $url / $method and $response / $statusCode).
190
+ const postContext = state.toContext(this.#requestContext(response), this.#responseContext(response));
191
+ const successful = this.evaluateCriteria(step, postContext);
192
+ const outputs = this.#outputResolver.resolve(step.outputs, expression => this.#evaluate(postContext, expression), scope);
193
+ const matchedActions = this.selectActions(step, successful, postContext);
194
+ return {
195
+ stepId,
196
+ response,
197
+ successful,
198
+ outputs,
199
+ action: matchedActions[0],
200
+ matchedActions
201
+ };
202
+ }
203
+
204
+ /**
205
+ * The request-body execute options for the step, as a typed partial to spread
206
+ * into the client options.
207
+ *
208
+ * Empty when the step has no request body; the `requestContentType` key is
209
+ * present only when a content type was resolved, so it is omitted rather than
210
+ * spread as `undefined`.
211
+ */
212
+ #requestBodyOptions(requestBody) {
213
+ if (requestBody === undefined) return {};
214
+ return requestBody.contentType === undefined ? {
215
+ requestBody: requestBody.payload
216
+ } : {
217
+ requestBody: requestBody.payload,
218
+ requestContentType: requestBody.contentType
219
+ };
220
+ }
221
+ async #locateOperation(step, stepId) {
222
+ if ((0, _apidomDatamodel.isStringElement)(step.operationId)) {
223
+ return this.#locatorNormalizer.normalizeOperationId((0, _apidomCore.toValue)(step.operationId), this.#document);
224
+ }
225
+ if ((0, _apidomDatamodel.isStringElement)(step.operationPath)) {
226
+ return this.#locatorNormalizer.normalizeOperationPath((0, _apidomCore.toValue)(step.operationPath), this.#document);
227
+ }
228
+ throw new _ExecutionError.default(`Step "${stepId}" has no operationId or operationPath`, {
229
+ stepId,
230
+ reason: 'missing-operation'
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Evaluates the step's `successCriteria`; all criteria must pass. A step with
236
+ * no criteria succeeds on a received response.
237
+ *
238
+ * Public for the same reason as {@link StepExecutor.selectActions}: a step
239
+ * targeting a `workflowId` is run by the workflow executor and never reaches
240
+ * {@link StepExecutor.execute}, but its criteria are still the author's
241
+ * assertion about the step and must be evaluated somewhere.
242
+ */
243
+ evaluateCriteria(step, context) {
244
+ const criteria = step.successCriteria;
245
+ // no (or non-array) successCriteria: the step succeeds on a received response.
246
+ if (!(0, _apidomDatamodel.isArrayElement)(criteria)) return true;
247
+ for (const criterion of criteria) {
248
+ if (!(0, _apidomNsArazzo.isCriterionElement)(criterion)) continue;
249
+ const met = this.#criterionEvaluator.evaluate(criterion, expression => this.#evaluate(context, expression));
250
+ if (!met) return false;
251
+ }
252
+ return true;
253
+ }
254
+
255
+ /**
256
+ * Selects every matching `onSuccess` / `onFailure` action for the outcome, in
257
+ * list order, gating each action's criteria against the context.
258
+ *
259
+ * The step's own list is the whole story here: a step that declares none of
260
+ * its own already carries the workflow-level defaults, inherited by
261
+ * {@link ArazzoWorkflowNormalizer} — the same place a step's inherited
262
+ * `parameters` come from — so there is no fallback left to apply at selection
263
+ * time.
264
+ *
265
+ * The full matching list (not just the first) is returned so the caller can
266
+ * honor "retryLimit exhausted prior to subsequent failure actions"; the common
267
+ * caller simply takes the first.
268
+ *
269
+ * Public because selecting a step's actions is meaningful beyond running one:
270
+ * the workflow executor selects them for a step targeting a `workflowId`, which
271
+ * it runs itself and which therefore never reaches
272
+ * {@link StepExecutor.execute}. Both callers reach the criteria evaluator and
273
+ * the expression context through this one method rather than wiring their own.
274
+ */
275
+ selectActions(step, successful, context) {
276
+ const isCriterionMet = criterion => {
277
+ try {
278
+ return this.#criterionEvaluator.evaluate(criterion, expression => this.#evaluate(context, expression));
279
+ } catch (error) {
280
+ // an action's criteria decide whether it applies to this outcome, and
281
+ // actions are shared — inherited from the workflow, reused from
282
+ // components. A condition on an HTTP exchange cannot apply to a step
283
+ // that calls a workflow, so the action does not match; the run does not
284
+ // abort over a default written for the operation steps.
285
+ if (error instanceof _RuntimeExpressionError.default && error.reason === 'unavailable-expression') {
286
+ return false;
287
+ }
288
+ throw error;
289
+ }
290
+ };
291
+ const scope = {
292
+ stepId: (0, _apidomCore.toValue)(step.stepId)
293
+ };
294
+ return successful ? this.#actionResolver.resolveOnSuccess(step.onSuccess, isCriterionMet, scope) : this.#actionResolver.resolveOnFailure(step.onFailure, isCriterionMet, scope);
295
+ }
296
+
297
+ /**
298
+ * Resolves a runtime expression leniently against a context, forwarding
299
+ * `$components` / `$sourceDescriptions` resolution to the document and
300
+ * registry.
301
+ */
302
+ #evaluate(context, expression) {
303
+ return new _RuntimeExpressionEvaluator.default(context, {
304
+ strict: false,
305
+ document: this.#document,
306
+ registry: this.#registry
307
+ }).evaluate(expression);
308
+ }
309
+ #responseContext(response) {
310
+ return {
311
+ statusCode: response.status,
312
+ header: response.headers,
313
+ body: response.body
314
+ };
315
+ }
316
+
317
+ /**
318
+ * The request context (`$url`, `$method`, `$request.header.*`,
319
+ * `$request.body`) from the request the client actually sent, or `undefined`
320
+ * when the client did not report one.
321
+ */
322
+ #requestContext(response) {
323
+ const request = response.request;
324
+ if (request === undefined) return undefined;
325
+ return {
326
+ url: request.url,
327
+ method: request.method,
328
+ header: request.headers,
329
+ body: request.body
330
+ };
331
+ }
332
+ }
333
+ var _default = exports.default = StepExecutor;