@usearazzo/runner 0.0.0-stage → 1.0.1-alpha.11
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/CHANGELOG.md +37 -0
- package/LICENSE +202 -0
- package/NOTICE +20 -0
- package/README.md +163 -2
- package/dist/arazzo-runner.browser.js +113245 -0
- package/dist/arazzo-runner.browser.min.js +1 -0
- package/package.json +95 -4
- package/src/ArazzoRunner.cjs +122 -0
- package/src/ArazzoRunner.mjs +113 -0
- package/src/action/ActionResolver.cjs +160 -0
- package/src/action/ActionResolver.mjs +150 -0
- package/src/assembler/OpenAPI2DocumentAssembler.cjs +111 -0
- package/src/assembler/OpenAPI2DocumentAssembler.mjs +104 -0
- package/src/assembler/OpenAPI30DocumentAssembler.cjs +110 -0
- package/src/assembler/OpenAPI30DocumentAssembler.mjs +103 -0
- package/src/assembler/OpenAPI31DocumentAssembler.cjs +111 -0
- package/src/assembler/OpenAPI31DocumentAssembler.mjs +104 -0
- package/src/assembler/OpenAPIDocumentAssembler.cjs +58 -0
- package/src/assembler/OpenAPIDocumentAssembler.mjs +50 -0
- package/src/client/HTTPClient.cjs +5 -0
- package/src/client/HTTPClient.mjs +1 -0
- package/src/client/HTTPClientFetch.cjs +16 -0
- package/src/client/HTTPClientFetch.mjs +10 -0
- package/src/client/OpenAPIOperationRequest.cjs +5 -0
- package/src/client/OpenAPIOperationRequest.mjs +1 -0
- package/src/client/OpenAPIOperationResponse.cjs +42 -0
- package/src/client/OpenAPIOperationResponse.mjs +36 -0
- package/src/client/OpenAPIOperationResponseNormalizer.cjs +48 -0
- package/src/client/OpenAPIOperationResponseNormalizer.mjs +40 -0
- package/src/client/ParameterDelivery.cjs +119 -0
- package/src/client/ParameterDelivery.mjs +113 -0
- package/src/constants.cjs +9 -0
- package/src/constants.mjs +3 -0
- package/src/criterion/CriterionEvaluator.cjs +205 -0
- package/src/criterion/CriterionEvaluator.mjs +196 -0
- package/src/criterion/JSONPathCriterionEvaluator.cjs +47 -0
- package/src/criterion/JSONPathCriterionEvaluator.mjs +40 -0
- package/src/criterion/RegexCriterionEvaluator.cjs +67 -0
- package/src/criterion/RegexCriterionEvaluator.mjs +60 -0
- package/src/criterion/SimpleCriterionEvaluator.cjs +50 -0
- package/src/criterion/SimpleCriterionEvaluator.mjs +43 -0
- package/src/criterion/XPathCriterionEvaluator.cjs +117 -0
- package/src/criterion/XPathCriterionEvaluator.mjs +109 -0
- package/src/document/APIDocument.cjs +35 -0
- package/src/document/APIDocument.mjs +30 -0
- package/src/document/ArazzoDocument.cjs +81 -0
- package/src/document/ArazzoDocument.mjs +74 -0
- package/src/document/ArazzoSourceDescriptionIndex.cjs +26 -0
- package/src/document/ArazzoSourceDescriptionIndex.mjs +20 -0
- package/src/document/ArazzoStepIndex.cjs +25 -0
- package/src/document/ArazzoStepIndex.mjs +19 -0
- package/src/document/ArazzoWorkflowIndex.cjs +17 -0
- package/src/document/ArazzoWorkflowIndex.mjs +11 -0
- package/src/document/OpenAPIDocument.cjs +28 -0
- package/src/document/OpenAPIDocument.mjs +21 -0
- package/src/document/OpenAPIOperationIndex.cjs +24 -0
- package/src/document/OpenAPIOperationIndex.mjs +18 -0
- package/src/document/ParameterIdentity.cjs +93 -0
- package/src/document/ParameterIdentity.mjs +88 -0
- package/src/document/openapi-types.cjs +5 -0
- package/src/document/openapi-types.mjs +1 -0
- package/src/errors/ArazzoRunnerError.cjs +10 -0
- package/src/errors/ArazzoRunnerError.mjs +5 -0
- package/src/errors/AssemblerError.cjs +11 -0
- package/src/errors/AssemblerError.mjs +4 -0
- package/src/errors/ClientError.cjs +11 -0
- package/src/errors/ClientError.mjs +4 -0
- package/src/errors/CriterionError.cjs +11 -0
- package/src/errors/CriterionError.mjs +4 -0
- package/src/errors/ExecutionError.cjs +11 -0
- package/src/errors/ExecutionError.mjs +4 -0
- package/src/errors/ExtractionError.cjs +11 -0
- package/src/errors/ExtractionError.mjs +4 -0
- package/src/errors/InvalidEntryDocumentError.cjs +11 -0
- package/src/errors/InvalidEntryDocumentError.mjs +4 -0
- package/src/errors/NormalizationError.cjs +11 -0
- package/src/errors/NormalizationError.mjs +4 -0
- package/src/errors/ResolverError.cjs +11 -0
- package/src/errors/ResolverError.mjs +4 -0
- package/src/errors/RuntimeExpressionError.cjs +11 -0
- package/src/errors/RuntimeExpressionError.mjs +4 -0
- package/src/errors/UnmatchedProviderError.cjs +11 -0
- package/src/errors/UnmatchedProviderError.mjs +4 -0
- package/src/executor/ArazzoWorkflowLocatorNormalizer.cjs +147 -0
- package/src/executor/ArazzoWorkflowLocatorNormalizer.mjs +137 -0
- package/src/executor/OpenAPIOperationExecutor.cjs +458 -0
- package/src/executor/OpenAPIOperationExecutor.mjs +447 -0
- package/src/executor/OpenAPIOperationLocatorNormalizer.cjs +197 -0
- package/src/executor/OpenAPIOperationLocatorNormalizer.mjs +189 -0
- package/src/executor/SourceDescriptionsLocatorNormalizer.cjs +44 -0
- package/src/executor/SourceDescriptionsLocatorNormalizer.mjs +38 -0
- package/src/executor/StepExecutor.cjs +333 -0
- package/src/executor/StepExecutor.mjs +326 -0
- package/src/executor/StepRetryRunner.cjs +226 -0
- package/src/executor/StepRetryRunner.mjs +215 -0
- package/src/executor/StepTransitionInterpreter.cjs +159 -0
- package/src/executor/StepTransitionInterpreter.mjs +151 -0
- package/src/executor/WorkflowCallStack.cjs +116 -0
- package/src/executor/WorkflowCallStack.mjs +106 -0
- package/src/executor/WorkflowExecutor.cjs +1091 -0
- package/src/executor/WorkflowExecutor.mjs +1077 -0
- package/src/executor/abort.cjs +70 -0
- package/src/executor/abort.mjs +60 -0
- package/src/expression/RuntimeExpressionContext.cjs +5 -0
- package/src/expression/RuntimeExpressionContext.mjs +1 -0
- package/src/expression/RuntimeExpressionEvaluator.cjs +337 -0
- package/src/expression/RuntimeExpressionEvaluator.mjs +328 -0
- package/src/extractor/ArazzoStepExtractor.cjs +48 -0
- package/src/extractor/ArazzoStepExtractor.mjs +41 -0
- package/src/extractor/ArazzoWorkflowExtractor.cjs +50 -0
- package/src/extractor/ArazzoWorkflowExtractor.mjs +43 -0
- package/src/extractor/OpenAPIOperationExtractor.cjs +84 -0
- package/src/extractor/OpenAPIOperationExtractor.mjs +77 -0
- package/src/index.cjs +426 -0
- package/src/index.mjs +75 -0
- package/src/normalizer/ArazzoStepNormalizer.cjs +23 -0
- package/src/normalizer/ArazzoStepNormalizer.mjs +17 -0
- package/src/normalizer/ArazzoWorkflowNormalizer.cjs +214 -0
- package/src/normalizer/ArazzoWorkflowNormalizer.mjs +206 -0
- package/src/normalizer/OpenAPI2OperationNormalizer.cjs +128 -0
- package/src/normalizer/OpenAPI2OperationNormalizer.mjs +120 -0
- package/src/normalizer/OpenAPI30OperationNormalizer.cjs +129 -0
- package/src/normalizer/OpenAPI30OperationNormalizer.mjs +121 -0
- package/src/normalizer/OpenAPI31OperationNormalizer.cjs +131 -0
- package/src/normalizer/OpenAPI31OperationNormalizer.mjs +123 -0
- package/src/normalizer/OpenAPIOperationNormalizer.cjs +58 -0
- package/src/normalizer/OpenAPIOperationNormalizer.mjs +50 -0
- package/src/registry/DocumentRegistry.cjs +160 -0
- package/src/registry/DocumentRegistry.mjs +151 -0
- package/src/registry/providers/ArazzoDocumentRegistryProvider.cjs +154 -0
- package/src/registry/providers/ArazzoDocumentRegistryProvider.mjs +146 -0
- package/src/registry/providers/DocumentRegistryProvider.cjs +31 -0
- package/src/registry/providers/DocumentRegistryProvider.mjs +25 -0
- package/src/registry/providers/OpenAPIDocumentRegistryProvider.cjs +122 -0
- package/src/registry/providers/OpenAPIDocumentRegistryProvider.mjs +114 -0
- package/src/resolver/ArazzoValueResolver.cjs +42 -0
- package/src/resolver/ArazzoValueResolver.mjs +37 -0
- package/src/resolver/OpenAPIOperationParameterResolver.cjs +117 -0
- package/src/resolver/OpenAPIOperationParameterResolver.mjs +110 -0
- package/src/resolver/OutputResolver.cjs +71 -0
- package/src/resolver/OutputResolver.mjs +64 -0
- package/src/resolver/RequestBodyResolver.cjs +227 -0
- package/src/resolver/RequestBodyResolver.mjs +220 -0
- package/src/resolver/ResolverScope.cjs +5 -0
- package/src/resolver/ResolverScope.mjs +1 -0
- package/src/resolver/StepParameterResolver.cjs +22 -0
- package/src/resolver/StepParameterResolver.mjs +15 -0
- package/src/resolver/WorkflowParameterResolver.cjs +73 -0
- package/src/resolver/WorkflowParameterResolver.mjs +66 -0
- package/src/state/WorkflowExecutionState.cjs +99 -0
- package/src/state/WorkflowExecutionState.mjs +93 -0
- package/src/vendor/swagger-client.cjs +1 -0
- package/src/vendor/swagger-client.mjs +1 -0
- package/types/runner.d.ts +2410 -0
|
@@ -0,0 +1,70 @@
|
|
|
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.throwIfAborted = exports.readAbortSignal = void 0;
|
|
8
|
+
var _ExecutionError = _interopRequireDefault(require("../errors/ExecutionError.cjs"));
|
|
9
|
+
/**
|
|
10
|
+
* Where a run was when it observed the caller's abort — carried into the error
|
|
11
|
+
* so both the message and its structured fields name the boundary the run
|
|
12
|
+
* stopped at. Both names are optional because the observers sit at different
|
|
13
|
+
* depths: the workflow executor knows the workflow and (between attempts) the
|
|
14
|
+
* step, while a {@link StepExecutor} used on its own knows only the step.
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reads the `AbortSignal` a caller put in the opaque execute options bag, or
|
|
20
|
+
* `undefined` when there is none.
|
|
21
|
+
*
|
|
22
|
+
* Duck-typed rather than `instanceof AbortSignal`: the bag is the caller's, and
|
|
23
|
+
* a signal made in another realm (a worker, a test harness, a polyfill) must
|
|
24
|
+
* still cancel the run.
|
|
25
|
+
*
|
|
26
|
+
* The whole surface the runner uses is required, not just `aborted`: what comes
|
|
27
|
+
* back is subscribed to as well as read (the abortable retry wait), so admitting
|
|
28
|
+
* a bare `{ aborted: false }` would trade a wrong-but-working run for a
|
|
29
|
+
* `TypeError` thrown from inside a `retryAfter` delay. Anything short of that
|
|
30
|
+
* surface is treated as no signal at all — the bag is open, and a value the
|
|
31
|
+
* runner cannot drive is not a cancellation.
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
const readAbortSignal = options => {
|
|
35
|
+
const signal = options.signal;
|
|
36
|
+
if (typeof signal !== 'object' || signal === null) return undefined;
|
|
37
|
+
const usable = typeof signal.aborted === 'boolean' && typeof signal.addEventListener === 'function' && typeof signal.removeEventListener === 'function';
|
|
38
|
+
return usable ? signal : undefined;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Throws `reason: 'aborted'` when the caller has cancelled the run, and does
|
|
43
|
+
* nothing otherwise.
|
|
44
|
+
*
|
|
45
|
+
* Cancellation throws rather than resolving as a `failed` result because it is
|
|
46
|
+
* not a workflow outcome: nothing was judged by `successCriteria`, no failure
|
|
47
|
+
* action was consulted, and the steps that did not run were not *decided*
|
|
48
|
+
* against. A result would be indistinguishable from the API having answered.
|
|
49
|
+
* The signal's own `reason` travels as the error's `cause`, so a caller that
|
|
50
|
+
* aborted with a meaningful value keeps it.
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
exports.readAbortSignal = readAbortSignal;
|
|
54
|
+
const throwIfAborted = (signal, context) => {
|
|
55
|
+
if (signal?.aborted !== true) return;
|
|
56
|
+
const {
|
|
57
|
+
workflowId,
|
|
58
|
+
stepId,
|
|
59
|
+
callStack
|
|
60
|
+
} = context;
|
|
61
|
+
const where = [stepId === undefined ? undefined : `step "${stepId}"`, workflowId === undefined ? undefined : `workflow "${workflowId}"`].filter(part => part !== undefined).join(' in ');
|
|
62
|
+
throw new _ExecutionError.default(`run aborted before ${where}`, {
|
|
63
|
+
workflowId,
|
|
64
|
+
stepId,
|
|
65
|
+
reason: 'aborted',
|
|
66
|
+
path: callStack?.path,
|
|
67
|
+
cause: signal.reason
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
exports.throwIfAborted = throwIfAborted;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import ExecutionError from "../errors/ExecutionError.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Where a run was when it observed the caller's abort — carried into the error
|
|
4
|
+
* so both the message and its structured fields name the boundary the run
|
|
5
|
+
* stopped at. Both names are optional because the observers sit at different
|
|
6
|
+
* depths: the workflow executor knows the workflow and (between attempts) the
|
|
7
|
+
* step, while a {@link StepExecutor} used on its own knows only the step.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Reads the `AbortSignal` a caller put in the opaque execute options bag, or
|
|
12
|
+
* `undefined` when there is none.
|
|
13
|
+
*
|
|
14
|
+
* Duck-typed rather than `instanceof AbortSignal`: the bag is the caller's, and
|
|
15
|
+
* a signal made in another realm (a worker, a test harness, a polyfill) must
|
|
16
|
+
* still cancel the run.
|
|
17
|
+
*
|
|
18
|
+
* The whole surface the runner uses is required, not just `aborted`: what comes
|
|
19
|
+
* back is subscribed to as well as read (the abortable retry wait), so admitting
|
|
20
|
+
* a bare `{ aborted: false }` would trade a wrong-but-working run for a
|
|
21
|
+
* `TypeError` thrown from inside a `retryAfter` delay. Anything short of that
|
|
22
|
+
* surface is treated as no signal at all — the bag is open, and a value the
|
|
23
|
+
* runner cannot drive is not a cancellation.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export const readAbortSignal = options => {
|
|
27
|
+
const signal = options.signal;
|
|
28
|
+
if (typeof signal !== 'object' || signal === null) return undefined;
|
|
29
|
+
const usable = typeof signal.aborted === 'boolean' && typeof signal.addEventListener === 'function' && typeof signal.removeEventListener === 'function';
|
|
30
|
+
return usable ? signal : undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Throws `reason: 'aborted'` when the caller has cancelled the run, and does
|
|
35
|
+
* nothing otherwise.
|
|
36
|
+
*
|
|
37
|
+
* Cancellation throws rather than resolving as a `failed` result because it is
|
|
38
|
+
* not a workflow outcome: nothing was judged by `successCriteria`, no failure
|
|
39
|
+
* action was consulted, and the steps that did not run were not *decided*
|
|
40
|
+
* against. A result would be indistinguishable from the API having answered.
|
|
41
|
+
* The signal's own `reason` travels as the error's `cause`, so a caller that
|
|
42
|
+
* aborted with a meaningful value keeps it.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export const throwIfAborted = (signal, context) => {
|
|
46
|
+
if (signal?.aborted !== true) return;
|
|
47
|
+
const {
|
|
48
|
+
workflowId,
|
|
49
|
+
stepId,
|
|
50
|
+
callStack
|
|
51
|
+
} = context;
|
|
52
|
+
const where = [stepId === undefined ? undefined : `step "${stepId}"`, workflowId === undefined ? undefined : `workflow "${workflowId}"`].filter(part => part !== undefined).join(' in ');
|
|
53
|
+
throw new ExecutionError(`run aborted before ${where}`, {
|
|
54
|
+
workflowId,
|
|
55
|
+
stepId,
|
|
56
|
+
reason: 'aborted',
|
|
57
|
+
path: callStack?.path,
|
|
58
|
+
cause: signal.reason
|
|
59
|
+
});
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,337 @@
|
|
|
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 = void 0;
|
|
8
|
+
var _parser = require("@usearazzo/parser");
|
|
9
|
+
var _arazzoRuntimeExpression = require("@swaggerexpert/arazzo-runtime-expression");
|
|
10
|
+
var _jsonPointer = require("@swaggerexpert/json-pointer");
|
|
11
|
+
var _json = _interopRequireDefault(require("@swaggerexpert/json-pointer/evaluate/realms/json"));
|
|
12
|
+
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
13
|
+
var _apidomCore = require("@speclynx/apidom-core");
|
|
14
|
+
var _RuntimeExpressionError = _interopRequireDefault(require("../errors/RuntimeExpressionError.cjs"));
|
|
15
|
+
var _ArazzoDocument = _interopRequireDefault(require("../document/ArazzoDocument.cjs"));
|
|
16
|
+
var _OpenAPIDocument = _interopRequireDefault(require("../document/OpenAPIDocument.cjs"));
|
|
17
|
+
/**
|
|
18
|
+
* Options for the RuntimeExpressionEvaluator.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The request/response side of the context a `$request`/`$response` source is
|
|
24
|
+
* evaluated against. Both request and response contexts are assignable to this
|
|
25
|
+
* shape; sources absent from a side (e.g. `$response.query`) resolve to
|
|
26
|
+
* `undefined` naturally.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Realm for drilling JSON Pointers into payload values. Composing the ApiDOM
|
|
31
|
+
* realm ahead of the JSON realm lets a single `evaluate` call handle plain JS
|
|
32
|
+
* values, ApiDOM elements, and mixed trees: the ApiDOM realm claims elements
|
|
33
|
+
* and falls through to the JSON realm for plain values. Order matters — the
|
|
34
|
+
* JSON realm would otherwise greedily claim ApiDOM elements and fail.
|
|
35
|
+
*/
|
|
36
|
+
const pointerRealm = (0, _jsonPointer.composeRealms)(new _apidomJsonPointer.ApiDOMEvaluationRealm(), new _json.default());
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The expression roots that describe one HTTP exchange — unavailable in a step
|
|
40
|
+
* that calls a workflow, which makes none.
|
|
41
|
+
*/
|
|
42
|
+
const HTTP_EXCHANGE_NODES = new Set(['UrlExpression', 'MethodExpression', 'StatusCodeExpression', 'RequestExpression', 'ResponseExpression']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Evaluates Arazzo runtime expressions against a context.
|
|
46
|
+
*
|
|
47
|
+
* A single evaluator is bound to one context and can evaluate many expressions
|
|
48
|
+
* against it — matching how a step executor resolves a step's parameters,
|
|
49
|
+
* success criteria, and outputs from the same state.
|
|
50
|
+
*
|
|
51
|
+
* The context holds only runtime state (`$inputs`, `$steps`, `$request`,
|
|
52
|
+
* `$response`, …). Static, document-sourced expressions are resolved on demand:
|
|
53
|
+
* `$components` and `$workflows.{workflowId}.steps.{stepId}` from the
|
|
54
|
+
* evaluator's document, and `$sourceDescriptions` from the external document
|
|
55
|
+
* (via the registry) the source name points at. A step reference resolves to
|
|
56
|
+
* the Step Object as authored — a locator, not the step's execution result.
|
|
57
|
+
*
|
|
58
|
+
* A reference resolves to `undefined` when it is absent. Runtime values
|
|
59
|
+
* originate from JSON (or ApiDOM), which cannot represent `undefined` — an
|
|
60
|
+
* absent value is a missing property and an explicit null is `null` — so
|
|
61
|
+
* `undefined` unambiguously marks "unresolved".
|
|
62
|
+
*
|
|
63
|
+
* Three modes are supported:
|
|
64
|
+
* - {@link RuntimeExpressionEvaluator.evaluate} resolves a pure expression
|
|
65
|
+
* (e.g. `$response.body#/id`) to its typed value.
|
|
66
|
+
* - {@link RuntimeExpressionEvaluator.interpolate} renders a template with
|
|
67
|
+
* embedded `{expression}` occurrences to a string.
|
|
68
|
+
* - {@link RuntimeExpressionEvaluator.resolve} applies Arazzo "value" semantics:
|
|
69
|
+
* a value that is itself a complete expression resolves to a typed value,
|
|
70
|
+
* otherwise the value is interpolated to a string.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
class RuntimeExpressionEvaluator {
|
|
74
|
+
#context;
|
|
75
|
+
#strict;
|
|
76
|
+
#document;
|
|
77
|
+
#registry;
|
|
78
|
+
constructor(context, options = {}) {
|
|
79
|
+
this.#context = context;
|
|
80
|
+
this.#strict = options.strict ?? true;
|
|
81
|
+
this.#document = options.document;
|
|
82
|
+
this.#registry = options.registry;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Evaluates a pure runtime expression to its typed value.
|
|
87
|
+
*
|
|
88
|
+
* The expression must be the bare form without surrounding braces
|
|
89
|
+
* (e.g. `$inputs.username`, `$response.body#/pets/0/id`). In strict mode an
|
|
90
|
+
* unresolvable reference throws; otherwise it returns `undefined`.
|
|
91
|
+
*/
|
|
92
|
+
evaluate(expression) {
|
|
93
|
+
const node = this.#parse(expression);
|
|
94
|
+
this.#assertAvailable(node, expression);
|
|
95
|
+
const value = this.#evaluateNode(node, expression);
|
|
96
|
+
if (value === undefined && this.#strict) {
|
|
97
|
+
throw new _RuntimeExpressionError.default(`Runtime expression "${expression}" could not be resolved against the context`, {
|
|
98
|
+
expression,
|
|
99
|
+
reason: 'unresolved-reference'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Renders a template string, replacing each embedded `{expression}` with the
|
|
107
|
+
* string form of its evaluated value. Returns a string.
|
|
108
|
+
*
|
|
109
|
+
* Object and array values are serialized with `JSON.stringify`. In non-strict
|
|
110
|
+
* mode, unresolvable references render as an empty string. A `{…}` that is not
|
|
111
|
+
* a valid embedded expression (e.g. a literal JSON payload) is left unchanged.
|
|
112
|
+
*/
|
|
113
|
+
interpolate(template) {
|
|
114
|
+
return (0, _arazzoRuntimeExpression.interpolate)(template, expression => this.evaluate(expression));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Resolves an Arazzo "value" string.
|
|
119
|
+
*
|
|
120
|
+
* A value that is itself a complete runtime expression (the unbraced form,
|
|
121
|
+
* e.g. `$inputs.userId`) resolves to its typed value — "runtime expressions
|
|
122
|
+
* preserve the type of the referenced value". Any other string is an
|
|
123
|
+
* expression-string template: embedded `{expression}` occurrences are
|
|
124
|
+
* interpolated and a string is returned, so a whole-string `{expression}`
|
|
125
|
+
* yields the string representation of its value, not the typed value. A
|
|
126
|
+
* string containing no expressions is returned unchanged.
|
|
127
|
+
*/
|
|
128
|
+
resolve(value) {
|
|
129
|
+
// `test` returns true only when the entire string is a valid runtime
|
|
130
|
+
// expression, so a braced or mixed string falls through to interpolation.
|
|
131
|
+
return (0, _arazzoRuntimeExpression.test)(value) ? this.evaluate(value) : this.interpolate(value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Parses an expression to its AST, throwing on failure regardless of strict mode.
|
|
136
|
+
*/
|
|
137
|
+
#parse(expression) {
|
|
138
|
+
let result;
|
|
139
|
+
try {
|
|
140
|
+
result = (0, _parser.parseRuntimeExpression)(expression);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new _RuntimeExpressionError.default(`Failed to parse runtime expression "${expression}"`, {
|
|
143
|
+
cause: error,
|
|
144
|
+
expression,
|
|
145
|
+
reason: 'parse-error'
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (!result.result.success || result.tree === undefined) {
|
|
149
|
+
throw new _RuntimeExpressionError.default(`Invalid runtime expression "${expression}"`, {
|
|
150
|
+
expression,
|
|
151
|
+
reason: 'invalid-expression'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return result.tree;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Rejects an HTTP-exchange expression in a step that calls a workflow. Such a
|
|
159
|
+
* step made no HTTP call, so the expression is an authoring error there — not
|
|
160
|
+
* a reference that merely did not resolve — and throws regardless of `strict`.
|
|
161
|
+
*/
|
|
162
|
+
#assertAvailable(node, expression) {
|
|
163
|
+
const {
|
|
164
|
+
calledWorkflow
|
|
165
|
+
} = this.#context;
|
|
166
|
+
if (calledWorkflow === undefined || !HTTP_EXCHANGE_NODES.has(node.type)) return;
|
|
167
|
+
throw new _RuntimeExpressionError.default(`Runtime expression "${expression}" is not available: step "${calledWorkflow.stepId}" calls workflow "${calledWorkflow.workflowId}", not an operation, so it has no HTTP request or response`, {
|
|
168
|
+
expression,
|
|
169
|
+
reason: 'unavailable-expression'
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Walks an AST node against the context, returning the referenced value or
|
|
175
|
+
* `undefined` when the reference does not resolve.
|
|
176
|
+
*/
|
|
177
|
+
#evaluateNode(node, expression) {
|
|
178
|
+
switch (node.type) {
|
|
179
|
+
case 'UrlExpression':
|
|
180
|
+
return this.#context.request?.url;
|
|
181
|
+
case 'MethodExpression':
|
|
182
|
+
return this.#context.request?.method;
|
|
183
|
+
case 'StatusCodeExpression':
|
|
184
|
+
return this.#context.response?.statusCode;
|
|
185
|
+
case 'RequestExpression':
|
|
186
|
+
return this.#source(node.source, this.#context.request);
|
|
187
|
+
case 'ResponseExpression':
|
|
188
|
+
return this.#source(node.source, this.#context.response);
|
|
189
|
+
case 'InputsExpression':
|
|
190
|
+
return this.#drill(this.#get(this.#context.inputs, node.name));
|
|
191
|
+
case 'OutputsExpression':
|
|
192
|
+
return this.#drill(this.#get(this.#context.outputs, node.name));
|
|
193
|
+
case 'StepsExpression':
|
|
194
|
+
return this.#drill(this.#get(this.#context.steps?.[node.stepId]?.outputs, node.outputName), node.jsonPointer?.value);
|
|
195
|
+
case 'WorkflowsExpression':
|
|
196
|
+
return this.#drill(this.#get(this.#context.workflows?.[node.workflowId]?.[node.field], node.fieldName), node.jsonPointer?.value);
|
|
197
|
+
case 'WorkflowsStepsExpression':
|
|
198
|
+
return this.#step(this.#document, node.workflowId, node.stepId);
|
|
199
|
+
case 'SourceDescriptionsExpression':
|
|
200
|
+
return this.#sourceDescription(node);
|
|
201
|
+
case 'ComponentsExpression':
|
|
202
|
+
return this.#component(node.componentType, node.componentName);
|
|
203
|
+
default:
|
|
204
|
+
throw new _RuntimeExpressionError.default(`Unsupported runtime expression node "${node.type}"`, {
|
|
205
|
+
expression,
|
|
206
|
+
reason: 'unsupported-node'
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Evaluates a `$request`/`$response` source reference against the request or
|
|
213
|
+
* response side of the context.
|
|
214
|
+
*/
|
|
215
|
+
#source(source, message) {
|
|
216
|
+
const {
|
|
217
|
+
reference
|
|
218
|
+
} = source;
|
|
219
|
+
switch (reference.type) {
|
|
220
|
+
case 'HeaderReference':
|
|
221
|
+
return this.#header(message?.header, reference.token);
|
|
222
|
+
case 'QueryReference':
|
|
223
|
+
return this.#get(message?.query, reference.name);
|
|
224
|
+
case 'PathReference':
|
|
225
|
+
return this.#get(message?.path, reference.name);
|
|
226
|
+
case 'BodyReference':
|
|
227
|
+
return this.#drill(message?.body, reference.jsonPointer?.value);
|
|
228
|
+
default:
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resolves `$components.{field}.{name}` by drilling into the evaluator's
|
|
235
|
+
* document. Returns `undefined` when no document is configured or the
|
|
236
|
+
* component is absent.
|
|
237
|
+
*/
|
|
238
|
+
#component(field, name) {
|
|
239
|
+
return this.#drillDocument(this.#document, (0, _jsonPointer.compile)(['components', field, name]));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Resolves `{workflowId}.steps.{stepId}` to the Step Object in an Arazzo
|
|
244
|
+
* document. Returns `undefined` when the document is absent or not Arazzo, or
|
|
245
|
+
* the workflow or step is unknown.
|
|
246
|
+
*/
|
|
247
|
+
#step(document, workflowId, stepId) {
|
|
248
|
+
if (document === undefined || !_ArazzoDocument.default.is(document)) return undefined;
|
|
249
|
+
const pointer = document.stepIndex.getStep(workflowId, stepId);
|
|
250
|
+
return pointer === undefined ? undefined : this.#drillDocument(document, pointer);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Resolves `$sourceDescriptions.{name}.{reference}` with the resolution
|
|
255
|
+
* priority defined by the Arazzo Specification:
|
|
256
|
+
*
|
|
257
|
+
* 1. `{reference}` is matched as an operationId (OpenAPI source) or workflowId
|
|
258
|
+
* (Arazzo source) in the referenced document, resolving to that operation
|
|
259
|
+
* or workflow. A `{workflowId}.steps.{stepId}` reference resolves to that
|
|
260
|
+
* step of the referenced Arazzo document.
|
|
261
|
+
* 2. failing that, `{reference}` is matched as a field of the Source
|
|
262
|
+
* Description Object itself (e.g. `url`, `type`) — so
|
|
263
|
+
* `$sourceDescriptions.{name}.url` yields the source's URL. An operationId
|
|
264
|
+
* that collides with a field name takes precedence (tier 1 wins).
|
|
265
|
+
*
|
|
266
|
+
* Returns `undefined` when nothing matches.
|
|
267
|
+
*/
|
|
268
|
+
#sourceDescription({
|
|
269
|
+
sourceName,
|
|
270
|
+
reference,
|
|
271
|
+
stepsReference
|
|
272
|
+
}) {
|
|
273
|
+
const uri = this.#document?.resolveSourceDescriptionURI(sourceName);
|
|
274
|
+
|
|
275
|
+
// tier 1: operationId / workflowId / workflow step in the referenced document.
|
|
276
|
+
const apiDocument = uri === undefined ? undefined : this.#registry?.get(uri);
|
|
277
|
+
if (apiDocument !== undefined) {
|
|
278
|
+
if (stepsReference !== undefined) {
|
|
279
|
+
return this.#step(apiDocument, stepsReference.workflowId, stepsReference.stepId);
|
|
280
|
+
}
|
|
281
|
+
const pointer = _OpenAPIDocument.default.is(apiDocument) ? apiDocument.operationIndex.get(reference) : _ArazzoDocument.default.is(apiDocument) ? apiDocument.workflowIndex.get(reference) : undefined;
|
|
282
|
+
if (pointer !== undefined) return this.#drillDocument(apiDocument, pointer);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// tier 2: a field of the Source Description Object (url, type, ...).
|
|
286
|
+
return this.#document?.resolveSourceDescriptionField(sourceName, reference);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Reads a property from a context slot, or `undefined` when the slot is not
|
|
291
|
+
* an object or lacks the key. Own properties only, so prototype members
|
|
292
|
+
* (`constructor`, `toString`) never leak into a resolution.
|
|
293
|
+
*/
|
|
294
|
+
#get(slot, key) {
|
|
295
|
+
if (typeof slot !== 'object' || slot === null) return undefined;
|
|
296
|
+
if (!Object.hasOwn(slot, key)) return undefined;
|
|
297
|
+
return slot[key];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Reads a header case-insensitively. The parser already lowercases the header
|
|
302
|
+
* token, so only the context keys (populated from an HTTP response with
|
|
303
|
+
* arbitrary casing) need normalizing to match.
|
|
304
|
+
*/
|
|
305
|
+
#header(headers, token) {
|
|
306
|
+
if (headers === undefined) return undefined;
|
|
307
|
+
const key = Object.keys(headers).find(k => k.toLowerCase() === token);
|
|
308
|
+
return key === undefined ? undefined : headers[key];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Drills a JSON Pointer into a document's API element. Returns `undefined`
|
|
313
|
+
* when the document is absent or the pointer does not resolve.
|
|
314
|
+
*/
|
|
315
|
+
#drillDocument(document, pointer) {
|
|
316
|
+
return document === undefined ? undefined : this.#drill(document.parseResult.api, pointer);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Drills an optional JSON Pointer into a payload value, returning the plain
|
|
321
|
+
* runtime value. `toValue` is idempotent on plain values, so the leaf is plain
|
|
322
|
+
* whether the payload was JSON or an ApiDOM element (see `pointerRealm`).
|
|
323
|
+
*/
|
|
324
|
+
#drill(value, pointer) {
|
|
325
|
+
if (value === undefined) return undefined;
|
|
326
|
+
if (pointer === undefined) return (0, _apidomCore.toValue)(value);
|
|
327
|
+
try {
|
|
328
|
+
return (0, _apidomCore.toValue)((0, _jsonPointer.evaluate)(value, pointer, {
|
|
329
|
+
realm: pointerRealm
|
|
330
|
+
}));
|
|
331
|
+
} catch (error) {
|
|
332
|
+
if (error instanceof _jsonPointer.JSONPointerEvaluateError) return undefined;
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
var _default = exports.default = RuntimeExpressionEvaluator;
|