@adobe/spacecat-shared-project-engine-client 1.13.0 → 1.14.0
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 +1 -1
- package/src/errors.js +48 -0
- package/src/index.d.ts +25 -0
- package/src/index.js +1 -0
- package/src/rest-transport.js +26 -17
package/package.json
CHANGED
package/src/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// @ts-check
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The single typed error the transport facade throws from its `unwrap` seam. It carries the
|
|
17
|
+
* failing HTTP `method`, the response `status` (or `undefined` when there was no HTTP response —
|
|
18
|
+
* i.e. an exhausted-network / per-attempt-timeout failure), and the normalized parsed error
|
|
19
|
+
* `body`. On the network/timeout path the original thrown error is preserved as `cause`.
|
|
20
|
+
*
|
|
21
|
+
* This class does NOT translate the failure into an HTTP status for the consumer, redact the
|
|
22
|
+
* body, or map it onto a domain error — those are consumer-owned per ADR-0001. It exists only to
|
|
23
|
+
* give consumers a single `instanceof`-checkable type with the raw `{ status, method, body }`.
|
|
24
|
+
*/
|
|
25
|
+
export class ProjectEngineApiError extends Error {
|
|
26
|
+
/**
|
|
27
|
+
* @param {number | undefined} status the HTTP response status, or `undefined` when there was no
|
|
28
|
+
* HTTP response (network / timeout failure)
|
|
29
|
+
* @param {string} method the HTTP method of the failing request
|
|
30
|
+
* @param {unknown} body the normalized parsed error body (`null` when empty/absent)
|
|
31
|
+
* @param {{ cause?: unknown }} [options] optional `{ cause }` forwarded to `super`, so a wrapped
|
|
32
|
+
* network/timeout error keeps its original as `.cause`
|
|
33
|
+
*/
|
|
34
|
+
constructor(status, method, body, options) {
|
|
35
|
+
const message = status === undefined
|
|
36
|
+
? `Project Engine ${method} request failed`
|
|
37
|
+
: `Project Engine ${method} request failed with status ${status}`;
|
|
38
|
+
super(message, options);
|
|
39
|
+
/** @type {string} */
|
|
40
|
+
this.name = 'ProjectEngineApiError';
|
|
41
|
+
/** @type {number | undefined} */
|
|
42
|
+
this.status = status;
|
|
43
|
+
/** @type {string} */
|
|
44
|
+
this.method = method;
|
|
45
|
+
/** @type {unknown} */
|
|
46
|
+
this.body = body;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -353,5 +353,30 @@ export declare function createSerenityProjectEngineTransport(
|
|
|
353
353
|
options: SerenityProjectEngineApiClientOptions,
|
|
354
354
|
): SerenityProjectEngineTransport;
|
|
355
355
|
|
|
356
|
+
/**
|
|
357
|
+
* The single typed error the transport facade throws from its `unwrap` seam. It carries the
|
|
358
|
+
* failing HTTP `method`, the response `status` (or `undefined` when there was no HTTP response —
|
|
359
|
+
* an exhausted-network / per-attempt-timeout failure), and the normalized parsed error `body`.
|
|
360
|
+
* On the network/timeout path the original thrown error is preserved as `cause`. No error→HTTP
|
|
361
|
+
* translation or redaction — consumer-owned per ADR-0001.
|
|
362
|
+
*
|
|
363
|
+
* NOTE: the `readonly` modifiers below are a type-system-only guarantee; the runtime class
|
|
364
|
+
* (`errors.js`) sets these fields with plain assignment in the constructor.
|
|
365
|
+
*/
|
|
366
|
+
export declare class ProjectEngineApiError extends Error {
|
|
367
|
+
/** The HTTP response status, or `undefined` when there was no HTTP response. */
|
|
368
|
+
readonly status: number | undefined;
|
|
369
|
+
/** The HTTP method of the failing request. */
|
|
370
|
+
readonly method: string;
|
|
371
|
+
/** The normalized parsed error body (`null` when empty/absent). */
|
|
372
|
+
readonly body: unknown;
|
|
373
|
+
constructor(
|
|
374
|
+
status: number | undefined,
|
|
375
|
+
method: string,
|
|
376
|
+
body: unknown,
|
|
377
|
+
options?: { cause?: unknown },
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
356
381
|
// Re-export the generated contract types for consumers that want them directly.
|
|
357
382
|
export type { paths, components };
|
package/src/index.js
CHANGED
package/src/rest-transport.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// @ts-check
|
|
14
14
|
|
|
15
15
|
import { createSerenityProjectEngineApiClient } from './client.js';
|
|
16
|
+
import { ProjectEngineApiError } from './errors.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @typedef {import('./client.js').SerenityProjectEngineApiClientOptions}
|
|
@@ -40,14 +41,17 @@ export function createSerenityProjectEngineTransport(options) {
|
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* The SINGLE seam where a failed call becomes a throw. It awaits the openapi-fetch result
|
|
43
|
-
* promise here (so a network/timeout rejection also flows through this one point), and
|
|
44
|
-
*
|
|
45
|
-
* `{ data, error, response }` with the parsed error body in `error` — so a non-2xx is turned
|
|
46
|
-
* into a throw here; a 2xx returns the parsed body (or null for an empty body).
|
|
44
|
+
* promise here (so a network/timeout rejection also flows through this one point), and turns
|
|
45
|
+
* both failure paths into a {@link ProjectEngineApiError}:
|
|
47
46
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
* - a non-2xx HTTP response: the typed client never throws on an HTTP error — it resolves to
|
|
48
|
+
* `{ data, error, response }` with the parsed error body in `error` — so the non-2xx is turned
|
|
49
|
+
* into a throw here carrying `response.status` + the normalized `body`;
|
|
50
|
+
* - an exhausted-network / per-attempt-timeout failure: `createRetryingFetch` rethrew the last
|
|
51
|
+
* raw error after the retry budget, so the awaited promise rejects. There is no HTTP response
|
|
52
|
+
* ⇒ `status` is `undefined`; the original error is preserved as `cause`.
|
|
53
|
+
*
|
|
54
|
+
* A 2xx returns the parsed body (or null for an empty body).
|
|
51
55
|
*
|
|
52
56
|
* @template T
|
|
53
57
|
* @param {string} method the HTTP method, for the error message
|
|
@@ -55,21 +59,26 @@ export function createSerenityProjectEngineTransport(options) {
|
|
|
55
59
|
* openapi-fetch result
|
|
56
60
|
* @returns {Promise<NonNullable<T> | null>} the parsed success body, or null for an empty body
|
|
57
61
|
* (an empty-body operation resolves with null, never undefined)
|
|
62
|
+
* @throws {import('./errors.js').ProjectEngineApiError} on a non-2xx response or an
|
|
63
|
+
* exhausted-network / per-attempt-timeout failure
|
|
58
64
|
*/
|
|
59
65
|
async function unwrap(method, resultPromise) {
|
|
60
|
-
|
|
66
|
+
let result;
|
|
67
|
+
try {
|
|
68
|
+
result = await resultPromise;
|
|
69
|
+
} catch (cause) {
|
|
70
|
+
// exhausted-network / per-attempt-timeout path: createRetryingFetch rethrew the last raw
|
|
71
|
+
// error after the retry budget. No HTTP response ⇒ status undefined; preserve the original
|
|
72
|
+
// as `cause`.
|
|
73
|
+
throw new ProjectEngineApiError(undefined, method, null, { cause });
|
|
74
|
+
}
|
|
75
|
+
const { data, error, response } = result;
|
|
61
76
|
if (!response.ok) {
|
|
62
|
-
|
|
63
|
-
// openapi-fetch surfaces an empty error body as '' (not undefined); normalise it to null.
|
|
77
|
+
// openapi-fetch puts the parsed error body in `error`; fall back to `data`, then null.
|
|
64
78
|
const rawBody = error ?? data ?? null;
|
|
65
|
-
//
|
|
66
|
-
// the `new Error(...)` line for `new ProjectEngineApiError(status, method, body)` — status,
|
|
67
|
-
// method, and this normalized body are all already in scope, so nothing else here changes.
|
|
68
|
-
// eslint-disable-next-line no-unused-vars
|
|
79
|
+
// openapi-fetch surfaces an empty error body as '' (not undefined); normalise it to null.
|
|
69
80
|
const body = rawBody === '' ? null : rawBody;
|
|
70
|
-
|
|
71
|
-
// project/etc.) that error-reporter and log consumers should not receive by default.
|
|
72
|
-
throw new Error(`Project Engine ${method} failed: ${status}`);
|
|
81
|
+
throw new ProjectEngineApiError(response.status, method, body);
|
|
73
82
|
}
|
|
74
83
|
return data ?? null;
|
|
75
84
|
}
|