@usearazzo/runner 1.0.1-alpha.10 → 1.0.1-alpha.12
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 +18 -0
- package/README.md +75 -691
- package/dist/arazzo-runner.browser.js +394 -376
- package/dist/arazzo-runner.browser.min.js +1 -1
- package/package.json +9 -5
- package/src/ArazzoRunner.cjs +122 -0
- package/src/ArazzoRunner.mjs +113 -0
- package/src/client/OpenAPIOperationResponse.cjs +37 -2
- package/src/client/OpenAPIOperationResponse.mjs +37 -2
- package/src/client/OpenAPIOperationResponseNormalizer.cjs +47 -3
- package/src/client/OpenAPIOperationResponseNormalizer.mjs +48 -3
- package/src/index.cjs +7 -0
- package/src/index.mjs +2 -0
- package/types/runner.d.ts +99 -3
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usearazzo/runner",
|
|
3
|
-
"version": "1.0.1-alpha.
|
|
3
|
+
"version": "1.0.1-alpha.12",
|
|
4
4
|
"description": "Runner for Arazzo Workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arazzo",
|
|
7
7
|
"arazzo-specification",
|
|
8
8
|
"runner",
|
|
9
|
+
"execute",
|
|
10
|
+
"executor",
|
|
9
11
|
"workflow",
|
|
10
|
-
"workflows"
|
|
12
|
+
"workflows",
|
|
13
|
+
"api",
|
|
14
|
+
"api-workflow"
|
|
11
15
|
],
|
|
12
16
|
"publishConfig": {
|
|
13
17
|
"access": "public",
|
|
@@ -68,8 +72,8 @@
|
|
|
68
72
|
"@swaggerexpert/json-pointer": "^3.0.2",
|
|
69
73
|
"@swaggerexpert/jsonpath": "^4.0.5",
|
|
70
74
|
"@swaggerexpert/media-types": "^1.0.1",
|
|
71
|
-
"@usearazzo/parser": "1.0.1-alpha.
|
|
72
|
-
"@usearazzo/resolver": "1.0.1-alpha.
|
|
75
|
+
"@usearazzo/parser": "1.0.1-alpha.12",
|
|
76
|
+
"@usearazzo/resolver": "1.0.1-alpha.12",
|
|
73
77
|
"@xmldom/xmldom": "^0.9.12",
|
|
74
78
|
"fontoxpath": "^3.34.0",
|
|
75
79
|
"ramda": "^0.32.0",
|
|
@@ -89,5 +93,5 @@
|
|
|
89
93
|
"devDependencies": {
|
|
90
94
|
"swagger-client": "^3.38.4"
|
|
91
95
|
},
|
|
92
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "cc5d9b2fb744662650068a5993ebcdd19e8568c2"
|
|
93
97
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
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 _DocumentRegistry = _interopRequireDefault(require("./registry/DocumentRegistry.cjs"));
|
|
9
|
+
var _OpenAPIOperationExecutor = _interopRequireDefault(require("./executor/OpenAPIOperationExecutor.cjs"));
|
|
10
|
+
var _StepExecutor = _interopRequireDefault(require("./executor/StepExecutor.cjs"));
|
|
11
|
+
var _WorkflowExecutor = _interopRequireDefault(require("./executor/WorkflowExecutor.cjs"));
|
|
12
|
+
var _InvalidEntryDocumentError = _interopRequireDefault(require("./errors/InvalidEntryDocumentError.cjs"));
|
|
13
|
+
/**
|
|
14
|
+
* Options for {@link ArazzoRunner.load}.
|
|
15
|
+
*
|
|
16
|
+
* Every option is a seam of a building block the runner wires: `registry` is
|
|
17
|
+
* the {@link DocumentRegistry} the documents load into (a fresh one with the
|
|
18
|
+
* default providers when omitted), `httpClient` and `requestInterceptor` go to
|
|
19
|
+
* the {@link OpenAPIOperationExecutor}, and the rest go to the
|
|
20
|
+
* {@link WorkflowExecutor}.
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for the ArazzoRunner constructor.
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Runs the workflows of one Arazzo document.
|
|
31
|
+
*
|
|
32
|
+
* A thin facade over the building blocks: it wires a {@link DocumentRegistry},
|
|
33
|
+
* an {@link OpenAPIOperationExecutor}, a {@link StepExecutor} and a
|
|
34
|
+
* {@link WorkflowExecutor} with their defaults, so running a workflow takes a
|
|
35
|
+
* `load` and an `execute`. The building blocks stay public for callers that
|
|
36
|
+
* need to compose them differently.
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
class ArazzoRunner {
|
|
40
|
+
document;
|
|
41
|
+
registry;
|
|
42
|
+
#executor;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Loads the entry Arazzo document at `uri` (a file system path or URL) and
|
|
46
|
+
* returns a runner bound to it.
|
|
47
|
+
*
|
|
48
|
+
* @throws UnmatchedProviderError - When no provider can load the document
|
|
49
|
+
* @throws InvalidEntryDocumentError - When the document is not an Arazzo
|
|
50
|
+
* document, or the given registry already backs another entry document
|
|
51
|
+
*/
|
|
52
|
+
static async load(uri, options = {}) {
|
|
53
|
+
const {
|
|
54
|
+
registry = new _DocumentRegistry.default(),
|
|
55
|
+
...rest
|
|
56
|
+
} = options;
|
|
57
|
+
// acquiring a second entry would pin it for good and, through the LRU
|
|
58
|
+
// order, could displace the first as the registry's entry; a registry that
|
|
59
|
+
// already backs one only looks the document up.
|
|
60
|
+
const entry = registry.entryDocument ?? (await registry.acquireEntryDocument(uri));
|
|
61
|
+
const document = await registry.acquire(uri);
|
|
62
|
+
if (document !== entry) {
|
|
63
|
+
throw new _InvalidEntryDocumentError.default(`Document at "${uri}" is not the entry document of the given registry`, {
|
|
64
|
+
uri
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return new ArazzoRunner({
|
|
68
|
+
...rest,
|
|
69
|
+
document: entry,
|
|
70
|
+
registry
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Binds a runner to an entry document the caller already acquired.
|
|
76
|
+
*
|
|
77
|
+
* @throws InvalidEntryDocumentError - When `document` is not the entry document of `registry`
|
|
78
|
+
*/
|
|
79
|
+
constructor(options) {
|
|
80
|
+
const {
|
|
81
|
+
document,
|
|
82
|
+
registry,
|
|
83
|
+
httpClient,
|
|
84
|
+
requestInterceptor,
|
|
85
|
+
...workflowExecutorOptions
|
|
86
|
+
} = options;
|
|
87
|
+
|
|
88
|
+
// a registry pins every entry document it acquires but reports only the
|
|
89
|
+
// first as its entry, so one registry serves one entry document.
|
|
90
|
+
if (registry.entryDocument !== document) {
|
|
91
|
+
throw new _InvalidEntryDocumentError.default(`Document at "${document.uri}" is not the entry document of the given registry`, {
|
|
92
|
+
uri: document.uri
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const operationExecutor = new _OpenAPIOperationExecutor.default({
|
|
96
|
+
httpClient,
|
|
97
|
+
requestInterceptor
|
|
98
|
+
});
|
|
99
|
+
const stepExecutor = new _StepExecutor.default({
|
|
100
|
+
document,
|
|
101
|
+
registry,
|
|
102
|
+
operationExecutor
|
|
103
|
+
});
|
|
104
|
+
this.document = document;
|
|
105
|
+
this.registry = registry;
|
|
106
|
+
this.#executor = new _WorkflowExecutor.default({
|
|
107
|
+
...workflowExecutorOptions,
|
|
108
|
+
document,
|
|
109
|
+
registry,
|
|
110
|
+
stepExecutor
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Runs the named workflow to completion, returning its outcome; see
|
|
116
|
+
* {@link WorkflowExecutor.execute}.
|
|
117
|
+
*/
|
|
118
|
+
execute(workflowId, options = {}) {
|
|
119
|
+
return this.#executor.execute(workflowId, options);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
var _default = exports.default = ArazzoRunner;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import DocumentRegistry from "./registry/DocumentRegistry.mjs";
|
|
2
|
+
import OpenAPIOperationExecutor from "./executor/OpenAPIOperationExecutor.mjs";
|
|
3
|
+
import StepExecutor from "./executor/StepExecutor.mjs";
|
|
4
|
+
import WorkflowExecutor from "./executor/WorkflowExecutor.mjs";
|
|
5
|
+
import InvalidEntryDocumentError from "./errors/InvalidEntryDocumentError.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* Options for {@link ArazzoRunner.load}.
|
|
8
|
+
*
|
|
9
|
+
* Every option is a seam of a building block the runner wires: `registry` is
|
|
10
|
+
* the {@link DocumentRegistry} the documents load into (a fresh one with the
|
|
11
|
+
* default providers when omitted), `httpClient` and `requestInterceptor` go to
|
|
12
|
+
* the {@link OpenAPIOperationExecutor}, and the rest go to the
|
|
13
|
+
* {@link WorkflowExecutor}.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Options for the ArazzoRunner constructor.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Runs the workflows of one Arazzo document.
|
|
22
|
+
*
|
|
23
|
+
* A thin facade over the building blocks: it wires a {@link DocumentRegistry},
|
|
24
|
+
* an {@link OpenAPIOperationExecutor}, a {@link StepExecutor} and a
|
|
25
|
+
* {@link WorkflowExecutor} with their defaults, so running a workflow takes a
|
|
26
|
+
* `load` and an `execute`. The building blocks stay public for callers that
|
|
27
|
+
* need to compose them differently.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
class ArazzoRunner {
|
|
31
|
+
document;
|
|
32
|
+
registry;
|
|
33
|
+
#executor;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loads the entry Arazzo document at `uri` (a file system path or URL) and
|
|
37
|
+
* returns a runner bound to it.
|
|
38
|
+
*
|
|
39
|
+
* @throws UnmatchedProviderError - When no provider can load the document
|
|
40
|
+
* @throws InvalidEntryDocumentError - When the document is not an Arazzo
|
|
41
|
+
* document, or the given registry already backs another entry document
|
|
42
|
+
*/
|
|
43
|
+
static async load(uri, options = {}) {
|
|
44
|
+
const {
|
|
45
|
+
registry = new DocumentRegistry(),
|
|
46
|
+
...rest
|
|
47
|
+
} = options;
|
|
48
|
+
// acquiring a second entry would pin it for good and, through the LRU
|
|
49
|
+
// order, could displace the first as the registry's entry; a registry that
|
|
50
|
+
// already backs one only looks the document up.
|
|
51
|
+
const entry = registry.entryDocument ?? (await registry.acquireEntryDocument(uri));
|
|
52
|
+
const document = await registry.acquire(uri);
|
|
53
|
+
if (document !== entry) {
|
|
54
|
+
throw new InvalidEntryDocumentError(`Document at "${uri}" is not the entry document of the given registry`, {
|
|
55
|
+
uri
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return new ArazzoRunner({
|
|
59
|
+
...rest,
|
|
60
|
+
document: entry,
|
|
61
|
+
registry
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Binds a runner to an entry document the caller already acquired.
|
|
67
|
+
*
|
|
68
|
+
* @throws InvalidEntryDocumentError - When `document` is not the entry document of `registry`
|
|
69
|
+
*/
|
|
70
|
+
constructor(options) {
|
|
71
|
+
const {
|
|
72
|
+
document,
|
|
73
|
+
registry,
|
|
74
|
+
httpClient,
|
|
75
|
+
requestInterceptor,
|
|
76
|
+
...workflowExecutorOptions
|
|
77
|
+
} = options;
|
|
78
|
+
|
|
79
|
+
// a registry pins every entry document it acquires but reports only the
|
|
80
|
+
// first as its entry, so one registry serves one entry document.
|
|
81
|
+
if (registry.entryDocument !== document) {
|
|
82
|
+
throw new InvalidEntryDocumentError(`Document at "${document.uri}" is not the entry document of the given registry`, {
|
|
83
|
+
uri: document.uri
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const operationExecutor = new OpenAPIOperationExecutor({
|
|
87
|
+
httpClient,
|
|
88
|
+
requestInterceptor
|
|
89
|
+
});
|
|
90
|
+
const stepExecutor = new StepExecutor({
|
|
91
|
+
document,
|
|
92
|
+
registry,
|
|
93
|
+
operationExecutor
|
|
94
|
+
});
|
|
95
|
+
this.document = document;
|
|
96
|
+
this.registry = registry;
|
|
97
|
+
this.#executor = new WorkflowExecutor({
|
|
98
|
+
...workflowExecutorOptions,
|
|
99
|
+
document,
|
|
100
|
+
registry,
|
|
101
|
+
stepExecutor
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Runs the named workflow to completion, returning its outcome; see
|
|
107
|
+
* {@link WorkflowExecutor.execute}.
|
|
108
|
+
*/
|
|
109
|
+
execute(workflowId, options = {}) {
|
|
110
|
+
return this.#executor.execute(workflowId, options);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export default ArazzoRunner;
|
|
@@ -17,6 +17,14 @@ exports.default = void 0;
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Response from an OpenAPI operation execution.
|
|
20
|
+
*
|
|
21
|
+
* `body` is the payload parsed by its content type, what `$response.body`
|
|
22
|
+
* reads. The body as received is read through `text()`, `blob()` and
|
|
23
|
+
* `bytes()`, named after the WHATWG `Response` methods but synchronous, and
|
|
24
|
+
* empty when the response carried no body (e.g. `204`, `304`). `bytes()` and
|
|
25
|
+
* `blob()` always return the bytes received. A body that is valid UTF-8 is held
|
|
26
|
+
* as its decoded string, which a plain-text or XML `body` shares rather than
|
|
27
|
+
* duplicates, and is encoded back on read; any other body is held as bytes.
|
|
20
28
|
* @public
|
|
21
29
|
*/
|
|
22
30
|
class OpenAPIOperationResponse {
|
|
@@ -25,18 +33,45 @@ class OpenAPIOperationResponse {
|
|
|
25
33
|
status;
|
|
26
34
|
statusText;
|
|
27
35
|
headers;
|
|
28
|
-
text;
|
|
29
36
|
body;
|
|
30
37
|
request;
|
|
38
|
+
#content;
|
|
31
39
|
constructor(raw) {
|
|
32
40
|
this.ok = raw.ok;
|
|
33
41
|
this.url = raw.url;
|
|
34
42
|
this.status = raw.status;
|
|
35
43
|
this.statusText = raw.statusText;
|
|
36
44
|
this.headers = raw.headers;
|
|
37
|
-
this.text = raw.text;
|
|
38
45
|
this.body = raw.body;
|
|
46
|
+
this.#content = raw.content;
|
|
39
47
|
this.request = raw.request;
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The body as text, like `Response.text()`; `''` when there is none.
|
|
52
|
+
*/
|
|
53
|
+
text() {
|
|
54
|
+
return typeof this.#content === 'string' ? this.#content : new TextDecoder().decode(this.#content);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The body as a `Blob` typed by the response's `Content-Type`, like
|
|
59
|
+
* `Response.blob()`.
|
|
60
|
+
*/
|
|
61
|
+
blob() {
|
|
62
|
+
// the lib types only take ArrayBuffer-backed views; the normalizer never
|
|
63
|
+
// produces a shared one.
|
|
64
|
+
return new Blob([this.#content], {
|
|
65
|
+
type: this.headers['content-type'] ?? ''
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The body as bytes, like `Response.bytes()`; a fresh copy on every call, so
|
|
71
|
+
* the response stays immutable.
|
|
72
|
+
*/
|
|
73
|
+
bytes() {
|
|
74
|
+
return typeof this.#content === 'string' ? new TextEncoder().encode(this.#content) : this.#content.slice();
|
|
75
|
+
}
|
|
41
76
|
}
|
|
42
77
|
var _default = exports.default = OpenAPIOperationResponse;
|
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Response from an OpenAPI operation execution.
|
|
14
|
+
*
|
|
15
|
+
* `body` is the payload parsed by its content type, what `$response.body`
|
|
16
|
+
* reads. The body as received is read through `text()`, `blob()` and
|
|
17
|
+
* `bytes()`, named after the WHATWG `Response` methods but synchronous, and
|
|
18
|
+
* empty when the response carried no body (e.g. `204`, `304`). `bytes()` and
|
|
19
|
+
* `blob()` always return the bytes received. A body that is valid UTF-8 is held
|
|
20
|
+
* as its decoded string, which a plain-text or XML `body` shares rather than
|
|
21
|
+
* duplicates, and is encoded back on read; any other body is held as bytes.
|
|
14
22
|
* @public
|
|
15
23
|
*/
|
|
16
24
|
class OpenAPIOperationResponse {
|
|
@@ -19,18 +27,45 @@ class OpenAPIOperationResponse {
|
|
|
19
27
|
status;
|
|
20
28
|
statusText;
|
|
21
29
|
headers;
|
|
22
|
-
text;
|
|
23
30
|
body;
|
|
24
31
|
request;
|
|
32
|
+
#content;
|
|
25
33
|
constructor(raw) {
|
|
26
34
|
this.ok = raw.ok;
|
|
27
35
|
this.url = raw.url;
|
|
28
36
|
this.status = raw.status;
|
|
29
37
|
this.statusText = raw.statusText;
|
|
30
38
|
this.headers = raw.headers;
|
|
31
|
-
this.text = raw.text;
|
|
32
39
|
this.body = raw.body;
|
|
40
|
+
this.#content = raw.content;
|
|
33
41
|
this.request = raw.request;
|
|
34
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The body as text, like `Response.text()`; `''` when there is none.
|
|
46
|
+
*/
|
|
47
|
+
text() {
|
|
48
|
+
return typeof this.#content === 'string' ? this.#content : new TextDecoder().decode(this.#content);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The body as a `Blob` typed by the response's `Content-Type`, like
|
|
53
|
+
* `Response.blob()`.
|
|
54
|
+
*/
|
|
55
|
+
blob() {
|
|
56
|
+
// the lib types only take ArrayBuffer-backed views; the normalizer never
|
|
57
|
+
// produces a shared one.
|
|
58
|
+
return new Blob([this.#content], {
|
|
59
|
+
type: this.headers['content-type'] ?? ''
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The body as bytes, like `Response.bytes()`; a fresh copy on every call, so
|
|
65
|
+
* the response stays immutable.
|
|
66
|
+
*/
|
|
67
|
+
bytes() {
|
|
68
|
+
return typeof this.#content === 'string' ? new TextEncoder().encode(this.#content) : this.#content.slice();
|
|
69
|
+
}
|
|
35
70
|
}
|
|
36
71
|
export default OpenAPIOperationResponse;
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
|
+
var _mediaTypes = require("@swaggerexpert/media-types");
|
|
8
9
|
var _swaggerClient = require("../vendor/swagger-client.cjs");
|
|
9
10
|
var _OpenAPIOperationResponse = _interopRequireDefault(require("./OpenAPIOperationResponse.cjs"));
|
|
10
11
|
// @ts-expect-error vendored swagger-client bundle has no type declarations
|
|
@@ -20,7 +21,11 @@ var _OpenAPIOperationResponse = _interopRequireDefault(require("./OpenAPIOperati
|
|
|
20
21
|
* Headers are taken from the `Response` directly rather than from the
|
|
21
22
|
* deserializer, which splits every value containing `", "` into an array:
|
|
22
23
|
* values stay strings as sent, with repeated fields (`Set-Cookie` included)
|
|
23
|
-
* combined by `Headers.get`.
|
|
24
|
+
* combined by `Headers.get`. The body is read once, as bytes, and handed to the
|
|
25
|
+
* deserializer from memory. A body that decodes as UTF-8 without loss is kept
|
|
26
|
+
* as that one string, the same one the deserializer parses, so a plain-text or
|
|
27
|
+
* XML `body` shares it; any other body (binary, another charset, a leading
|
|
28
|
+
* byte-order mark) is kept as the bytes, so they are never re-encoded lossily.
|
|
24
29
|
* @public
|
|
25
30
|
*/
|
|
26
31
|
class OpenAPIOperationResponseNormalizer {
|
|
@@ -31,7 +36,30 @@ class OpenAPIOperationResponseNormalizer {
|
|
|
31
36
|
* carries none (e.g. a stubbed transport).
|
|
32
37
|
*/
|
|
33
38
|
async normalize(response, request) {
|
|
34
|
-
const
|
|
39
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
40
|
+
const text = this.#decodeLosslessly(bytes);
|
|
41
|
+
// the deserializer matches the raw Content-Type case-sensitively and against
|
|
42
|
+
// its parameters too, so it is handed only the parsed essence; the response's
|
|
43
|
+
// own `headers` below stay as sent.
|
|
44
|
+
const deserializerHeaders = new Headers(response.headers);
|
|
45
|
+
const contentType = response.headers.get('content-type');
|
|
46
|
+
const mediaType = contentType === null ? undefined : (0, _mediaTypes.parse)(contentType, {
|
|
47
|
+
type: 'media-type'
|
|
48
|
+
}).tree;
|
|
49
|
+
if (mediaType !== undefined) {
|
|
50
|
+
deserializerHeaders.set('content-type', `${mediaType.type}/${mediaType.subtype}`);
|
|
51
|
+
}
|
|
52
|
+
// the deserializer only reads the body through text() or blob(), by content
|
|
53
|
+
// type; answering from memory spares a second copy of the stream.
|
|
54
|
+
const deserialized = await (0, _swaggerClient.deserializeResponse)({
|
|
55
|
+
ok: response.ok,
|
|
56
|
+
url: response.url,
|
|
57
|
+
status: response.status,
|
|
58
|
+
statusText: response.statusText,
|
|
59
|
+
headers: deserializerHeaders,
|
|
60
|
+
text: async () => text ?? new TextDecoder().decode(bytes),
|
|
61
|
+
blob: async () => new Blob([bytes])
|
|
62
|
+
}, request?.url);
|
|
35
63
|
const headers = Object.fromEntries(Array.from(response.headers.keys(), name => [name, response.headers.get(name)]));
|
|
36
64
|
return new _OpenAPIOperationResponse.default({
|
|
37
65
|
ok: deserialized.ok,
|
|
@@ -39,10 +67,26 @@ class OpenAPIOperationResponseNormalizer {
|
|
|
39
67
|
status: deserialized.status,
|
|
40
68
|
statusText: deserialized.statusText,
|
|
41
69
|
headers,
|
|
42
|
-
text: deserialized.text,
|
|
43
70
|
body: deserialized.body,
|
|
71
|
+
content: text ?? bytes,
|
|
44
72
|
request
|
|
45
73
|
});
|
|
46
74
|
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The bytes as a string that encodes back to exactly them, or `undefined`
|
|
78
|
+
* when there is none: invalid UTF-8, or a leading byte-order mark, which
|
|
79
|
+
* decoding strips.
|
|
80
|
+
*/
|
|
81
|
+
#decodeLosslessly(bytes) {
|
|
82
|
+
if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) return undefined;
|
|
83
|
+
try {
|
|
84
|
+
return new TextDecoder('utf-8', {
|
|
85
|
+
fatal: true
|
|
86
|
+
}).decode(bytes);
|
|
87
|
+
} catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
47
91
|
}
|
|
48
92
|
var _default = exports.default = OpenAPIOperationResponseNormalizer;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parse } from '@swaggerexpert/media-types';
|
|
2
|
+
|
|
1
3
|
// @ts-expect-error vendored swagger-client bundle has no type declarations
|
|
2
4
|
import { deserializeResponse } from '../vendor/swagger-client.mjs';
|
|
3
5
|
import OpenAPIOperationResponse from "./OpenAPIOperationResponse.mjs";
|
|
@@ -12,7 +14,11 @@ import OpenAPIOperationResponse from "./OpenAPIOperationResponse.mjs";
|
|
|
12
14
|
* Headers are taken from the `Response` directly rather than from the
|
|
13
15
|
* deserializer, which splits every value containing `", "` into an array:
|
|
14
16
|
* values stay strings as sent, with repeated fields (`Set-Cookie` included)
|
|
15
|
-
* combined by `Headers.get`.
|
|
17
|
+
* combined by `Headers.get`. The body is read once, as bytes, and handed to the
|
|
18
|
+
* deserializer from memory. A body that decodes as UTF-8 without loss is kept
|
|
19
|
+
* as that one string, the same one the deserializer parses, so a plain-text or
|
|
20
|
+
* XML `body` shares it; any other body (binary, another charset, a leading
|
|
21
|
+
* byte-order mark) is kept as the bytes, so they are never re-encoded lossily.
|
|
16
22
|
* @public
|
|
17
23
|
*/
|
|
18
24
|
class OpenAPIOperationResponseNormalizer {
|
|
@@ -23,7 +29,30 @@ class OpenAPIOperationResponseNormalizer {
|
|
|
23
29
|
* carries none (e.g. a stubbed transport).
|
|
24
30
|
*/
|
|
25
31
|
async normalize(response, request) {
|
|
26
|
-
const
|
|
32
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
33
|
+
const text = this.#decodeLosslessly(bytes);
|
|
34
|
+
// the deserializer matches the raw Content-Type case-sensitively and against
|
|
35
|
+
// its parameters too, so it is handed only the parsed essence; the response's
|
|
36
|
+
// own `headers` below stay as sent.
|
|
37
|
+
const deserializerHeaders = new Headers(response.headers);
|
|
38
|
+
const contentType = response.headers.get('content-type');
|
|
39
|
+
const mediaType = contentType === null ? undefined : parse(contentType, {
|
|
40
|
+
type: 'media-type'
|
|
41
|
+
}).tree;
|
|
42
|
+
if (mediaType !== undefined) {
|
|
43
|
+
deserializerHeaders.set('content-type', `${mediaType.type}/${mediaType.subtype}`);
|
|
44
|
+
}
|
|
45
|
+
// the deserializer only reads the body through text() or blob(), by content
|
|
46
|
+
// type; answering from memory spares a second copy of the stream.
|
|
47
|
+
const deserialized = await deserializeResponse({
|
|
48
|
+
ok: response.ok,
|
|
49
|
+
url: response.url,
|
|
50
|
+
status: response.status,
|
|
51
|
+
statusText: response.statusText,
|
|
52
|
+
headers: deserializerHeaders,
|
|
53
|
+
text: async () => text ?? new TextDecoder().decode(bytes),
|
|
54
|
+
blob: async () => new Blob([bytes])
|
|
55
|
+
}, request?.url);
|
|
27
56
|
const headers = Object.fromEntries(Array.from(response.headers.keys(), name => [name, response.headers.get(name)]));
|
|
28
57
|
return new OpenAPIOperationResponse({
|
|
29
58
|
ok: deserialized.ok,
|
|
@@ -31,10 +60,26 @@ class OpenAPIOperationResponseNormalizer {
|
|
|
31
60
|
status: deserialized.status,
|
|
32
61
|
statusText: deserialized.statusText,
|
|
33
62
|
headers,
|
|
34
|
-
text: deserialized.text,
|
|
35
63
|
body: deserialized.body,
|
|
64
|
+
content: text ?? bytes,
|
|
36
65
|
request
|
|
37
66
|
});
|
|
38
67
|
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The bytes as a string that encodes back to exactly them, or `undefined`
|
|
71
|
+
* when there is none: invalid UTF-8, or a leading byte-order mark, which
|
|
72
|
+
* decoding strips.
|
|
73
|
+
*/
|
|
74
|
+
#decodeLosslessly(bytes) {
|
|
75
|
+
if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) return undefined;
|
|
76
|
+
try {
|
|
77
|
+
return new TextDecoder('utf-8', {
|
|
78
|
+
fatal: true
|
|
79
|
+
}).decode(bytes);
|
|
80
|
+
} catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
39
84
|
}
|
|
40
85
|
export default OpenAPIOperationResponseNormalizer;
|
package/src/index.cjs
CHANGED
|
@@ -29,6 +29,12 @@ Object.defineProperty(exports, "ArazzoDocumentRegistryProvider", {
|
|
|
29
29
|
return _ArazzoDocumentRegistryProvider.default;
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
|
+
Object.defineProperty(exports, "ArazzoRunner", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () {
|
|
35
|
+
return _ArazzoRunner.default;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
32
38
|
Object.defineProperty(exports, "ArazzoRunnerError", {
|
|
33
39
|
enumerable: true,
|
|
34
40
|
get: function () {
|
|
@@ -359,6 +365,7 @@ Object.defineProperty(exports, "settledResult", {
|
|
|
359
365
|
return _WorkflowExecutor.settledResult;
|
|
360
366
|
}
|
|
361
367
|
});
|
|
368
|
+
var _ArazzoRunner = _interopRequireDefault(require("./ArazzoRunner.cjs"));
|
|
362
369
|
var _DocumentRegistry = _interopRequireDefault(require("./registry/DocumentRegistry.cjs"));
|
|
363
370
|
var _DocumentRegistryProvider = _interopRequireDefault(require("./registry/providers/DocumentRegistryProvider.cjs"));
|
|
364
371
|
var _ArazzoDocumentRegistryProvider = _interopRequireDefault(require("./registry/providers/ArazzoDocumentRegistryProvider.cjs"));
|