@usearazzo/runner 1.0.1-alpha.16 → 1.0.1-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usearazzo/runner",
3
- "version": "1.0.1-alpha.16",
3
+ "version": "1.0.1-alpha.18",
4
4
  "description": "Runner for Arazzo Workflows.",
5
5
  "keywords": [
6
6
  "arazzo",
@@ -56,7 +56,7 @@
56
56
  "author": "Vladimir Gorej <vladimir.gorej@gmail.com>",
57
57
  "license": "Apache-2.0",
58
58
  "dependencies": {
59
- "@babel/runtime-corejs3": "^8.0.7",
59
+ "@babel/runtime-corejs3": "^8.0.8",
60
60
  "@speclynx/apidom-core": "^6.1.0",
61
61
  "@speclynx/apidom-datamodel": "^6.1.0",
62
62
  "@speclynx/apidom-error": "^6.1.0",
@@ -72,9 +72,9 @@
72
72
  "@swaggerexpert/cookie": "^2.0.2",
73
73
  "@swaggerexpert/json-pointer": "^3.0.2",
74
74
  "@swaggerexpert/jsonpath": "^4.0.5",
75
- "@swaggerexpert/media-types": "^1.0.1",
76
- "@usearazzo/parser": "1.0.1-alpha.16",
77
- "@usearazzo/resolver": "1.0.1-alpha.16",
75
+ "@swaggerexpert/media-types": "^1.0.2",
76
+ "@usearazzo/parser": "1.0.1-alpha.18",
77
+ "@usearazzo/resolver": "1.0.1-alpha.18",
78
78
  "@xmldom/xmldom": "^0.9.12",
79
79
  "fontoxpath": "^3.34.0",
80
80
  "ramda": "^0.32.0",
@@ -94,5 +94,5 @@
94
94
  "devDependencies": {
95
95
  "swagger-client": "^3.38.4"
96
96
  },
97
- "gitHead": "9ff2a68d209a5d178658f9a568c2d136178d40b8"
97
+ "gitHead": "29cba37b82d02fa4b11e7d8acf3ba9f96dc44e10"
98
98
  }
@@ -45,6 +45,7 @@ class ArazzoRunner {
45
45
  * Loads the entry Arazzo document at `uri` (a file system path or URL) and
46
46
  * returns a runner bound to it.
47
47
  *
48
+ * @throws DocumentLoadError - When the document cannot be read or loaded
48
49
  * @throws UnmatchedProviderError - When no provider can load the document
49
50
  * @throws InvalidEntryDocumentError - When the document is not an Arazzo
50
51
  * document, or the given registry already backs another entry document
@@ -36,6 +36,7 @@ class ArazzoRunner {
36
36
  * Loads the entry Arazzo document at `uri` (a file system path or URL) and
37
37
  * returns a runner bound to it.
38
38
  *
39
+ * @throws DocumentLoadError - When the document cannot be read or loaded
39
40
  * @throws UnmatchedProviderError - When no provider can load the document
40
41
  * @throws InvalidEntryDocumentError - When the document is not an Arazzo
41
42
  * document, or the given registry already backs another entry document
@@ -4,10 +4,19 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ /**
8
+ * A key starting with an `in` value the client builds qualified keys from —
9
+ * the OpenAPI locations, including OpenAPI 2.0's `formData` and `body`. The
10
+ * client qualifies whatever `in` an operation declares, so a non-standard one
11
+ * is not covered.
12
+ */
13
+ const QUALIFIED_KEY = /^(?:path|query|header|cookie|formData|body)\./;
14
+
7
15
  /**
8
16
  * The outcome of claiming a delivery key: `claimed` for a first claim,
9
- * `duplicate` when the same `(name, in)` parameter claims its key again, and
10
- * `collision` when a *different* parameter produces an already-claimed key.
17
+ * `duplicate` when the same `(name, in)` parameter claims its key again,
18
+ * `collision` when a *different* parameter produces an already-claimed key,
19
+ * and `misaddressed` when a bare key has the shape of a qualified one.
11
20
  * @internal
12
21
  */
13
22
 
@@ -47,8 +56,16 @@ exports.default = void 0;
47
56
  * alone identifies the claimant. A bare key and a qualified key sharing one
48
57
  * name are a collision too, even though the keys differ — the client consults
49
58
  * the bare name first for every declared parameter bearing it, so the bare
50
- * entry would silently capture the qualified one. What to do about a
51
- * collision is the caller's policy — the map only reports it.
59
+ * entry would silently capture the qualified one.
60
+ *
61
+ * A bare key that starts with a location the client qualifies keys with
62
+ * (`querystring` named `header.X-Foo`) is misaddressed even when it claims
63
+ * alone: the client matches it as the qualified key of every declared
64
+ * parameter that location and the rest of the name identify, so the value
65
+ * would reach the header `X-Foo` — and `OpenAPIOperationExecutor` reads such
66
+ * a key the same way when it delivers undeclared parameters. What to do about
67
+ * a collision or a misaddressed key is the caller's policy — the map only
68
+ * reports it.
52
69
  *
53
70
  * Diff this against the vendored bundle's lookup when bumping swagger-client.
54
71
  * @internal
@@ -71,10 +88,16 @@ class ParameterDelivery {
71
88
  * Only a `claimed` outcome entitles the caller to {@link ParameterDelivery.set}
72
89
  * the key; a `duplicate` means the same parameter already holds it, and a
73
90
  * `collision` means a different parameter does — on the same key, or across
74
- * a bare/qualified pair sharing the name (see the class doc).
91
+ * a bare/qualified pair sharing the name (see the class doc). A
92
+ * `misaddressed` bare key is never claimed.
75
93
  */
76
94
  claim(location, name) {
77
95
  const key = ParameterDelivery.keyFor(location, name);
96
+ const bare = key === name;
97
+ if (bare && QUALIFIED_KEY.test(name)) return {
98
+ key,
99
+ outcome: 'misaddressed'
100
+ };
78
101
  const claimant = this.#claimedBy.get(key);
79
102
  if (claimant !== undefined) {
80
103
  return {
@@ -82,7 +105,6 @@ class ParameterDelivery {
82
105
  outcome: claimant === location ? 'duplicate' : 'collision'
83
106
  };
84
107
  }
85
- const bare = key === name;
86
108
  if (bare ? this.#qualifiedNames.has(name) : this.#bareNames.has(name)) {
87
109
  return {
88
110
  key,
@@ -1,7 +1,16 @@
1
+ /**
2
+ * A key starting with an `in` value the client builds qualified keys from —
3
+ * the OpenAPI locations, including OpenAPI 2.0's `formData` and `body`. The
4
+ * client qualifies whatever `in` an operation declares, so a non-standard one
5
+ * is not covered.
6
+ */
7
+ const QUALIFIED_KEY = /^(?:path|query|header|cookie|formData|body)\./;
8
+
1
9
  /**
2
10
  * The outcome of claiming a delivery key: `claimed` for a first claim,
3
- * `duplicate` when the same `(name, in)` parameter claims its key again, and
4
- * `collision` when a *different* parameter produces an already-claimed key.
11
+ * `duplicate` when the same `(name, in)` parameter claims its key again,
12
+ * `collision` when a *different* parameter produces an already-claimed key,
13
+ * and `misaddressed` when a bare key has the shape of a qualified one.
5
14
  * @internal
6
15
  */
7
16
 
@@ -41,8 +50,16 @@
41
50
  * alone identifies the claimant. A bare key and a qualified key sharing one
42
51
  * name are a collision too, even though the keys differ — the client consults
43
52
  * the bare name first for every declared parameter bearing it, so the bare
44
- * entry would silently capture the qualified one. What to do about a
45
- * collision is the caller's policy — the map only reports it.
53
+ * entry would silently capture the qualified one.
54
+ *
55
+ * A bare key that starts with a location the client qualifies keys with
56
+ * (`querystring` named `header.X-Foo`) is misaddressed even when it claims
57
+ * alone: the client matches it as the qualified key of every declared
58
+ * parameter that location and the rest of the name identify, so the value
59
+ * would reach the header `X-Foo` — and `OpenAPIOperationExecutor` reads such
60
+ * a key the same way when it delivers undeclared parameters. What to do about
61
+ * a collision or a misaddressed key is the caller's policy — the map only
62
+ * reports it.
46
63
  *
47
64
  * Diff this against the vendored bundle's lookup when bumping swagger-client.
48
65
  * @internal
@@ -65,10 +82,16 @@ class ParameterDelivery {
65
82
  * Only a `claimed` outcome entitles the caller to {@link ParameterDelivery.set}
66
83
  * the key; a `duplicate` means the same parameter already holds it, and a
67
84
  * `collision` means a different parameter does — on the same key, or across
68
- * a bare/qualified pair sharing the name (see the class doc).
85
+ * a bare/qualified pair sharing the name (see the class doc). A
86
+ * `misaddressed` bare key is never claimed.
69
87
  */
70
88
  claim(location, name) {
71
89
  const key = ParameterDelivery.keyFor(location, name);
90
+ const bare = key === name;
91
+ if (bare && QUALIFIED_KEY.test(name)) return {
92
+ key,
93
+ outcome: 'misaddressed'
94
+ };
72
95
  const claimant = this.#claimedBy.get(key);
73
96
  if (claimant !== undefined) {
74
97
  return {
@@ -76,7 +99,6 @@ class ParameterDelivery {
76
99
  outcome: claimant === location ? 'duplicate' : 'collision'
77
100
  };
78
101
  }
79
- const bare = key === name;
80
102
  if (bare ? this.#qualifiedNames.has(name) : this.#bareNames.has(name)) {
81
103
  return {
82
104
  key,
@@ -0,0 +1,11 @@
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 _ArazzoRunnerError = _interopRequireDefault(require("./ArazzoRunnerError.cjs"));
9
+ /** @public */
10
+ class DocumentLoadError extends _ArazzoRunnerError.default {}
11
+ var _default = exports.default = DocumentLoadError;
@@ -0,0 +1,4 @@
1
+ import ArazzoRunnerError from "./ArazzoRunnerError.mjs";
2
+ /** @public */
3
+ class DocumentLoadError extends ArazzoRunnerError {}
4
+ export default DocumentLoadError;
package/src/index.cjs CHANGED
@@ -119,6 +119,12 @@ Object.defineProperty(exports, "CriterionEvaluator", {
119
119
  return _CriterionEvaluator.default;
120
120
  }
121
121
  });
122
+ Object.defineProperty(exports, "DocumentLoadError", {
123
+ enumerable: true,
124
+ get: function () {
125
+ return _DocumentLoadError.default;
126
+ }
127
+ });
122
128
  Object.defineProperty(exports, "DocumentRegistry", {
123
129
  enumerable: true,
124
130
  get: function () {
@@ -420,6 +426,7 @@ var _NormalizationError = _interopRequireDefault(require("./errors/Normalization
420
426
  var _ExtractionError = _interopRequireDefault(require("./errors/ExtractionError.cjs"));
421
427
  var _InvalidEntryDocumentError = _interopRequireDefault(require("./errors/InvalidEntryDocumentError.cjs"));
422
428
  var _UnmatchedProviderError = _interopRequireDefault(require("./errors/UnmatchedProviderError.cjs"));
429
+ var _DocumentLoadError = _interopRequireDefault(require("./errors/DocumentLoadError.cjs"));
423
430
  var _RuntimeExpressionError = _interopRequireDefault(require("./errors/RuntimeExpressionError.cjs"));
424
431
  var _CriterionError = _interopRequireDefault(require("./errors/CriterionError.cjs"));
425
432
  var _ResolverError = _interopRequireDefault(require("./errors/ResolverError.cjs"));
package/src/index.mjs CHANGED
@@ -69,6 +69,7 @@ export { default as NormalizationError } from "./errors/NormalizationError.mjs";
69
69
  export { default as ExtractionError } from "./errors/ExtractionError.mjs";
70
70
  export { default as InvalidEntryDocumentError } from "./errors/InvalidEntryDocumentError.mjs";
71
71
  export { default as UnmatchedProviderError } from "./errors/UnmatchedProviderError.mjs";
72
+ export { default as DocumentLoadError } from "./errors/DocumentLoadError.mjs";
72
73
  export { default as RuntimeExpressionError } from "./errors/RuntimeExpressionError.mjs";
73
74
  export { default as CriterionError } from "./errors/CriterionError.mjs";
74
75
  export { default as ResolverError } from "./errors/ResolverError.mjs";
@@ -10,6 +10,8 @@ var _empty = require("@speclynx/apidom-reference/configuration/empty");
10
10
  var _ArazzoDocument = _interopRequireDefault(require("../document/ArazzoDocument.cjs"));
11
11
  var _ArazzoDocumentRegistryProvider = _interopRequireDefault(require("./providers/ArazzoDocumentRegistryProvider.cjs"));
12
12
  var _OpenAPIDocumentRegistryProvider = _interopRequireDefault(require("./providers/OpenAPIDocumentRegistryProvider.cjs"));
13
+ var _ArazzoRunnerError = _interopRequireDefault(require("../errors/ArazzoRunnerError.cjs"));
14
+ var _DocumentLoadError = _interopRequireDefault(require("../errors/DocumentLoadError.cjs"));
13
15
  var _InvalidEntryDocumentError = _interopRequireDefault(require("../errors/InvalidEntryDocumentError.cjs"));
14
16
  var _UnmatchedProviderError = _interopRequireDefault(require("../errors/UnmatchedProviderError.cjs"));
15
17
  var constants = _interopRequireWildcard(require("../constants.cjs"));
@@ -49,16 +51,29 @@ class DocumentRegistry {
49
51
  * Acquires a document by URI. Returns cached document if available,
50
52
  * otherwise detects the format, loads via the matching provider,
51
53
  * caches, and returns it.
54
+ *
55
+ * @throws DocumentLoadError - When the document cannot be read or loaded
56
+ * @throws UnmatchedProviderError - When no provider can load the document
52
57
  */
53
58
  async acquire(uri) {
54
59
  const canonicalURI = this.#canonicalize(uri);
55
60
  const cachedDocument = this.#get(canonicalURI);
56
61
  if (cachedDocument) return cachedDocument;
57
62
  for (const provider of this.#providers) {
58
- if (await provider.canProvide(canonicalURI)) {
59
- const document = await provider.provide(canonicalURI);
60
- this.#set(canonicalURI, document);
61
- return document;
63
+ try {
64
+ if (await provider.canProvide(canonicalURI)) {
65
+ const document = await provider.provide(canonicalURI);
66
+ this.#set(canonicalURI, document);
67
+ return document;
68
+ }
69
+ } catch (error) {
70
+ // a document that cannot be read or loaded fails in ApiDOM or the parser;
71
+ // wrapping it keeps every load failure inside the runner's error hierarchy.
72
+ if (error instanceof _ArazzoRunnerError.default) throw error;
73
+ throw new _DocumentLoadError.default(`Failed to load the document at "${canonicalURI}"`, {
74
+ uri: canonicalURI,
75
+ cause: error
76
+ });
62
77
  }
63
78
  }
64
79
  throw new _UnmatchedProviderError.default(`No provider can handle the document at "${canonicalURI}"`, {
@@ -97,6 +112,10 @@ class DocumentRegistry {
97
112
  /**
98
113
  * Acquires the entry Arazzo document and marks it with `isEntry: true`.
99
114
  * Validates that the document is an Arazzo specification.
115
+ *
116
+ * @throws DocumentLoadError - When the document cannot be read or loaded
117
+ * @throws UnmatchedProviderError - When no provider can load the document
118
+ * @throws InvalidEntryDocumentError - When the document is not an Arazzo document
100
119
  */
101
120
  async acquireEntryDocument(uri) {
102
121
  const document = await this.acquire(uri);
@@ -2,6 +2,8 @@ import { url } from '@speclynx/apidom-reference/configuration/empty';
2
2
  import ArazzoDocument from "../document/ArazzoDocument.mjs";
3
3
  import ArazzoDocumentRegistryProvider from "./providers/ArazzoDocumentRegistryProvider.mjs";
4
4
  import OpenAPIDocumentRegistryProvider from "./providers/OpenAPIDocumentRegistryProvider.mjs";
5
+ import ArazzoRunnerError from "../errors/ArazzoRunnerError.mjs";
6
+ import DocumentLoadError from "../errors/DocumentLoadError.mjs";
5
7
  import InvalidEntryDocumentError from "../errors/InvalidEntryDocumentError.mjs";
6
8
  import UnmatchedProviderError from "../errors/UnmatchedProviderError.mjs";
7
9
  import * as constants from "../constants.mjs";
@@ -40,16 +42,29 @@ class DocumentRegistry {
40
42
  * Acquires a document by URI. Returns cached document if available,
41
43
  * otherwise detects the format, loads via the matching provider,
42
44
  * caches, and returns it.
45
+ *
46
+ * @throws DocumentLoadError - When the document cannot be read or loaded
47
+ * @throws UnmatchedProviderError - When no provider can load the document
43
48
  */
44
49
  async acquire(uri) {
45
50
  const canonicalURI = this.#canonicalize(uri);
46
51
  const cachedDocument = this.#get(canonicalURI);
47
52
  if (cachedDocument) return cachedDocument;
48
53
  for (const provider of this.#providers) {
49
- if (await provider.canProvide(canonicalURI)) {
50
- const document = await provider.provide(canonicalURI);
51
- this.#set(canonicalURI, document);
52
- return document;
54
+ try {
55
+ if (await provider.canProvide(canonicalURI)) {
56
+ const document = await provider.provide(canonicalURI);
57
+ this.#set(canonicalURI, document);
58
+ return document;
59
+ }
60
+ } catch (error) {
61
+ // a document that cannot be read or loaded fails in ApiDOM or the parser;
62
+ // wrapping it keeps every load failure inside the runner's error hierarchy.
63
+ if (error instanceof ArazzoRunnerError) throw error;
64
+ throw new DocumentLoadError(`Failed to load the document at "${canonicalURI}"`, {
65
+ uri: canonicalURI,
66
+ cause: error
67
+ });
53
68
  }
54
69
  }
55
70
  throw new UnmatchedProviderError(`No provider can handle the document at "${canonicalURI}"`, {
@@ -88,6 +103,10 @@ class DocumentRegistry {
88
103
  /**
89
104
  * Acquires the entry Arazzo document and marks it with `isEntry: true`.
90
105
  * Validates that the document is an Arazzo specification.
106
+ *
107
+ * @throws DocumentLoadError - When the document cannot be read or loaded
108
+ * @throws UnmatchedProviderError - When no provider can load the document
109
+ * @throws InvalidEntryDocumentError - When the document is not an Arazzo document
91
110
  */
92
111
  async acquireEntryDocument(uri) {
93
112
  const document = await this.acquire(uri);
@@ -41,12 +41,15 @@ var _StepParameterResolver = _interopRequireDefault(require("./StepParameterReso
41
41
  * against an OpenAPI 2.0 operation, bypassing the step's `requestBody`, while
42
42
  * an unknown location reaches nothing at all. And two *different* parameters the
43
43
  * map reports as colliding cannot both be delivered, so a collision throws
44
- * rather than silently dropping one — whether they collide on one key (the
45
- * scheme is not injective: a `querystring` parameter named `header.token` and
46
- * a header parameter named `token` both produce `header.token`) or across a
47
- * bare/qualified pair sharing a name (a `querystring` `token` next to a
48
- * header `token`), where the client would consult the bare name first and
49
- * silently capture the qualified value.
44
+ * rather than silently dropping one — a bare/qualified pair sharing a name (a
45
+ * `querystring` `token` next to a header `token`), where the client would
46
+ * consult the bare name first and silently capture the qualified value. A
47
+ * `querystring` parameter whose name starts with a location the client
48
+ * qualifies keys with (`header.X-Foo`) throws even alone: the client would
49
+ * match its bare key as the qualified key of a declared header `X-Foo` and
50
+ * deliver it there. The same check rejects it, in either order, next to a
51
+ * header parameter `X-Foo` producing that very key — the scheme is not
52
+ * injective, so the two could otherwise collide on one key.
50
53
  * @public
51
54
  */
52
55
  class OpenAPIOperationParameterResolver extends _StepParameterResolver.default {
@@ -113,8 +116,9 @@ class OpenAPIOperationParameterResolver extends _StepParameterResolver.default {
113
116
  } = delivery.claim(location, name);
114
117
  // the same parameter declared again: the earlier, more specific one won
115
118
  if (outcome === 'duplicate') continue;
116
- if (outcome === 'collision') {
117
- throw new _ResolverError.default(`Parameter "${name}" (in: ${location}) collides with another parameter on the ` + `delivery key "${key}" and cannot be delivered unambiguously`, {
119
+ if (outcome !== 'claimed') {
120
+ const why = outcome === 'collision' ? `collides with another parameter on the delivery key "${key}"` : 'has a name the client reads as the delivery key of another location';
121
+ throw new _ResolverError.default(`Parameter "${name}" (in: ${location}) ${why} and cannot be delivered unambiguously`, {
118
122
  ...scope,
119
123
  target: name,
120
124
  reason: 'ambiguous-delivery'
@@ -34,12 +34,15 @@ import StepParameterResolver from "./StepParameterResolver.mjs";
34
34
  * against an OpenAPI 2.0 operation, bypassing the step's `requestBody`, while
35
35
  * an unknown location reaches nothing at all. And two *different* parameters the
36
36
  * map reports as colliding cannot both be delivered, so a collision throws
37
- * rather than silently dropping one — whether they collide on one key (the
38
- * scheme is not injective: a `querystring` parameter named `header.token` and
39
- * a header parameter named `token` both produce `header.token`) or across a
40
- * bare/qualified pair sharing a name (a `querystring` `token` next to a
41
- * header `token`), where the client would consult the bare name first and
42
- * silently capture the qualified value.
37
+ * rather than silently dropping one — a bare/qualified pair sharing a name (a
38
+ * `querystring` `token` next to a header `token`), where the client would
39
+ * consult the bare name first and silently capture the qualified value. A
40
+ * `querystring` parameter whose name starts with a location the client
41
+ * qualifies keys with (`header.X-Foo`) throws even alone: the client would
42
+ * match its bare key as the qualified key of a declared header `X-Foo` and
43
+ * deliver it there. The same check rejects it, in either order, next to a
44
+ * header parameter `X-Foo` producing that very key — the scheme is not
45
+ * injective, so the two could otherwise collide on one key.
43
46
  * @public
44
47
  */
45
48
  class OpenAPIOperationParameterResolver extends StepParameterResolver {
@@ -106,8 +109,9 @@ class OpenAPIOperationParameterResolver extends StepParameterResolver {
106
109
  } = delivery.claim(location, name);
107
110
  // the same parameter declared again: the earlier, more specific one won
108
111
  if (outcome === 'duplicate') continue;
109
- if (outcome === 'collision') {
110
- throw new ResolverError(`Parameter "${name}" (in: ${location}) collides with another parameter on the ` + `delivery key "${key}" and cannot be delivered unambiguously`, {
112
+ if (outcome !== 'claimed') {
113
+ const why = outcome === 'collision' ? `collides with another parameter on the delivery key "${key}"` : 'has a name the client reads as the delivery key of another location';
114
+ throw new ResolverError(`Parameter "${name}" (in: ${location}) ${why} and cannot be delivered unambiguously`, {
111
115
  ...scope,
112
116
  target: name,
113
117
  reason: 'ambiguous-delivery'