@pipelex/sdk 0.32.0 → 0.34.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/README.md +40 -5
- package/dist/artifacts.js +23 -9
- package/dist/artifacts.js.map +1 -1
- package/dist/cli/args.d.ts +41 -0
- package/dist/cli/args.js +96 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bundle.d.ts +45 -0
- package/dist/cli/bundle.js +148 -0
- package/dist/cli/bundle.js.map +1 -0
- package/dist/cli/environment.d.ts +36 -0
- package/dist/cli/environment.js +72 -0
- package/dist/cli/environment.js.map +1 -0
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.js +56 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/inputs.d.ts +13 -0
- package/dist/cli/inputs.js +70 -0
- package/dist/cli/inputs.js.map +1 -0
- package/dist/cli/io.d.ts +79 -0
- package/dist/cli/io.js +96 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/main.d.ts +18 -0
- package/dist/cli/main.js +58 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/method.d.ts +56 -0
- package/dist/cli/method.js +108 -0
- package/dist/cli/method.js.map +1 -0
- package/dist/cli/present.d.ts +43 -0
- package/dist/cli/present.js +191 -0
- package/dist/cli/present.js.map +1 -0
- package/dist/cli/run.d.ts +17 -0
- package/dist/cli/run.js +290 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/script.d.ts +23 -0
- package/dist/cli/script.js +212 -0
- package/dist/cli/script.js.map +1 -0
- package/dist/cli/source.d.ts +49 -0
- package/dist/cli/source.js +71 -0
- package/dist/cli/source.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +125 -17
- package/dist/client.js +383 -109
- package/dist/client.js.map +1 -1
- package/dist/codegen-check.d.ts +9 -0
- package/dist/codegen-check.js +12 -6
- package/dist/codegen-check.js.map +1 -1
- package/dist/error-models.d.ts +60 -2
- package/dist/error-models.js +169 -3
- package/dist/error-models.js.map +1 -1
- package/dist/error-verdicts.d.ts +34 -0
- package/dist/error-verdicts.js +69 -0
- package/dist/error-verdicts.js.map +1 -0
- package/dist/errors.d.ts +250 -48
- package/dist/errors.js +330 -64
- package/dist/errors.js.map +1 -1
- package/dist/hooks/validate-client.d.ts +3 -3
- package/dist/hooks/validate-client.js +3 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/prepare-inputs.d.ts +6 -0
- package/dist/prepare-inputs.js +46 -14
- package/dist/prepare-inputs.js.map +1 -1
- package/dist/runs.d.ts +28 -1
- package/dist/runs.js +2 -1
- package/dist/runs.js.map +1 -1
- package/dist/upload-grant.d.ts +15 -10
- package/dist/upload-grant.js +36 -17
- package/dist/upload-grant.js.map +1 -1
- package/dist/upload.d.ts +6 -0
- package/dist/upload.js +25 -7
- package/dist/upload.js.map +1 -1
- package/dist/user-agent.d.ts +3 -2
- package/dist/user-agent.js +3 -2
- package/dist/user-agent.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +15 -9
- package/docs/artifact-download.md +3 -3
- package/docs/cli.md +123 -0
- package/docs/client-identification.md +1 -1
- package/docs/errors.md +96 -11
- package/docs/input-preparation.md +16 -8
- package/docs/run-results.md +20 -0
- package/package.json +5 -1
package/dist/errors.js
CHANGED
|
@@ -1,25 +1,97 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pipelex SDK errors — transport
|
|
3
|
-
* `PipelexApiClient
|
|
4
|
-
* (re-exported from `mthds/protocol`),
|
|
2
|
+
* Pipelex SDK errors — the argument, transport, run-lifecycle, input-preparation and artifact
|
|
3
|
+
* errors `PipelexApiClient` raises. Every one derives from `PipelexRequestError`, itself a
|
|
4
|
+
* `PipelineRequestError` (the protocol base, re-exported from `mthds/protocol`), and so carries
|
|
5
|
+
* a verdict: `retryable` and `errorDomain`, always decided. The one exported error outside the
|
|
6
|
+
* family, `CodegenLockError`, declares the same two members itself.
|
|
5
7
|
*/
|
|
6
8
|
import { PipelineRequestError } from "mthds/protocol";
|
|
9
|
+
import { fallbackVerdict } from "./error-verdicts.js";
|
|
7
10
|
export { PipelineRequestError };
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const ERROR_DOMAINS = new Set(["input", "config", "runtime"]);
|
|
12
|
+
/** Whether a value is one of the three error domains. */
|
|
13
|
+
export function isErrorDomain(value) {
|
|
14
|
+
return typeof value === "string" && ERROR_DOMAINS.has(value);
|
|
15
|
+
}
|
|
16
|
+
/** Build a verdict; each class below declares its own with it. */
|
|
17
|
+
function makeVerdict(errorDomain, retryable) {
|
|
18
|
+
return Object.freeze({ errorDomain, retryable });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The base of every error this SDK raises over a request, carrying the verdict as two readonly
|
|
22
|
+
* own properties: `retryable` and `errorDomain`. Each subclass passes its verdict to this
|
|
23
|
+
* constructor, so a class that forgets one does not compile, and every class says its verdict
|
|
24
|
+
* where it is defined.
|
|
25
|
+
*
|
|
26
|
+
* It refines the standard's `PipelineRequestError`, so `instanceof PipelineRequestError` still
|
|
27
|
+
* matches every SDK error. To read a verdict from anything a `catch` holds, use
|
|
28
|
+
* `errorVerdictOf`, which also reads it off an error raised by another copy of this SDK.
|
|
29
|
+
*/
|
|
30
|
+
export class PipelexRequestError extends PipelineRequestError {
|
|
31
|
+
/** Whether asking again can plausibly succeed. Always decided. */
|
|
32
|
+
retryable;
|
|
33
|
+
/** Who can fix the failure: `input`, `config` or `runtime`. Always decided. */
|
|
34
|
+
errorDomain;
|
|
35
|
+
constructor(message, verdict, options) {
|
|
36
|
+
super(message, options);
|
|
37
|
+
this.name = "PipelexRequestError";
|
|
38
|
+
this.retryable = verdict.retryable;
|
|
39
|
+
this.errorDomain = verdict.errorDomain;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The verdict of anything a `catch` holds: the pair from an `Error` carrying a boolean
|
|
44
|
+
* `retryable` and a known `errorDomain`, and `undefined` otherwise.
|
|
45
|
+
*
|
|
46
|
+
* The check is structural rather than `instanceof`, so it reads every error of this SDK, of
|
|
47
|
+
* another copy of it installed beside this one, of a consumer's own subclass, and an `mthds`
|
|
48
|
+
* `ApiResponseError` whose runner sent both members. `undefined` means the error carries no
|
|
49
|
+
* verdict: the `RangeError` or `TypeError` the SDK raises for an argument of the wrong type or
|
|
50
|
+
* range before any request is sent (a bug in the calling code), the caller's own abort, which the
|
|
51
|
+
* client rethrows untouched, or anything else a `try` block threw. An argument the client refuses
|
|
52
|
+
* for what it asks is a `RequestArgumentError`, which carries one.
|
|
53
|
+
*/
|
|
54
|
+
export function errorVerdictOf(err) {
|
|
55
|
+
if (!(err instanceof Error))
|
|
56
|
+
return undefined;
|
|
57
|
+
const { retryable, errorDomain } = err;
|
|
58
|
+
if (typeof retryable !== "boolean" || !isErrorDomain(errorDomain))
|
|
59
|
+
return undefined;
|
|
60
|
+
return { retryable, errorDomain };
|
|
61
|
+
}
|
|
62
|
+
// ── Argument refusals ────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* The SDK refused a call's arguments before sending any request: no run source given to
|
|
65
|
+
* `execute()` or `start()`, run sources or method selectors that exclude each other, a selector
|
|
66
|
+
* rule of `validate()`, an empty `validateFiles()`, a reserved key in `extra`, or a base URL that
|
|
67
|
+
* is not host-only. Nothing reached the API, so the message says what to change.
|
|
68
|
+
*
|
|
69
|
+
* Its verdict is `input`, not retryable — the caller must change the arguments — unless
|
|
70
|
+
* `options.verdict` declares another: the client declares `config` for a base URL that is not
|
|
71
|
+
* host-only, since it typically comes from `PIPELEX_BASE_URL`. A refusal the standard's own
|
|
72
|
+
* check raises (`assertExclusiveRunSources`) is rethrown as this class with the same message, the
|
|
73
|
+
* standard's error as `cause`.
|
|
74
|
+
*/
|
|
75
|
+
export class RequestArgumentError extends PipelexRequestError {
|
|
76
|
+
constructor(message, options) {
|
|
77
|
+
super(message, options?.verdict ?? makeVerdict("input", false), options);
|
|
78
|
+
this.name = "RequestArgumentError";
|
|
12
79
|
}
|
|
13
80
|
}
|
|
81
|
+
// ── Input preparation ────────────────────────────────────────────────
|
|
14
82
|
/**
|
|
15
83
|
* Base class for every failure raised by input preparation (`uploadFile` /
|
|
16
84
|
* `prepareInputs`). Catch this to handle any preparation failure; catch a
|
|
17
85
|
* subclass to branch on the semantic category. All preparation failures are
|
|
18
86
|
* raised BEFORE any run is created — a run never triggers a hidden upload.
|
|
87
|
+
*
|
|
88
|
+
* Its verdict is `input`, not retryable — the asset, the inputs or the method selector must
|
|
89
|
+
* change — unless `options.verdict` declares another: the SDK's own subclasses do, and so can a
|
|
90
|
+
* consumer's, such as a form reporting a missing input in its own words.
|
|
19
91
|
*/
|
|
20
|
-
export class InputPreparationError extends
|
|
92
|
+
export class InputPreparationError extends PipelexRequestError {
|
|
21
93
|
constructor(message, options) {
|
|
22
|
-
super(message, options);
|
|
94
|
+
super(message, options?.verdict ?? makeVerdict("input", false), options);
|
|
23
95
|
this.name = "InputPreparationError";
|
|
24
96
|
}
|
|
25
97
|
}
|
|
@@ -56,9 +128,43 @@ export class InvalidLocalSourceError extends InputPreparationError {
|
|
|
56
128
|
this.source = source;
|
|
57
129
|
}
|
|
58
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* A value the caller gave at a file input cannot be turned into a file to upload: a `data:` URL
|
|
133
|
+
* that does not decode (no comma, bad base64, bad percent-encoding), or a value of a type no file
|
|
134
|
+
* input takes, neither a path or URL string, nor bytes, nor `{url}` content. `prepareInputs`
|
|
135
|
+
* raises it while it reads the inputs, before anything is uploaded. As with
|
|
136
|
+
* `InvalidLocalSourceError`, the inputs are what must change, which is how a consumer tells it
|
|
137
|
+
* apart from `MethodLoadError`, a method that does not load, raised by the same preparation.
|
|
138
|
+
*/
|
|
139
|
+
export class InvalidInputValueError extends InputPreparationError {
|
|
140
|
+
constructor(message, options) {
|
|
141
|
+
super(message, options);
|
|
142
|
+
this.name = "InvalidInputValueError";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The pipe I/O answer said the method does not load (`is_valid: false`), so its signature cannot
|
|
147
|
+
* be read and no input can be prepared: the method, not the inputs, must change.
|
|
148
|
+
* `validationErrors` holds the answer's items, each kept when it carries a string `category` and
|
|
149
|
+
* `message`, possibly none; `serverMessage` is the answer's own `message` when it is a string. The
|
|
150
|
+
* error's message names the first item's message, else the answer's own.
|
|
151
|
+
*/
|
|
152
|
+
export class MethodLoadError extends InputPreparationError {
|
|
153
|
+
validationErrors;
|
|
154
|
+
serverMessage;
|
|
155
|
+
constructor(message, details, options) {
|
|
156
|
+
super(message, options);
|
|
157
|
+
this.name = "MethodLoadError";
|
|
158
|
+
this.validationErrors = details.validationErrors;
|
|
159
|
+
this.serverMessage = details.serverMessage;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
59
162
|
/**
|
|
60
163
|
* The server or storage refused the asset — most commonly a `413` past the
|
|
61
|
-
* service-defined size cap, or storage refusing an upload with a grant.
|
|
164
|
+
* service-defined size cap, or storage refusing an upload with a grant. A storage
|
|
165
|
+
* `4xx` that says nothing about the file is not among them: a `408` or a `429`, which
|
|
166
|
+
* time out or throttle the request, a `400 RequestTimeout` and a `409
|
|
167
|
+
* ConditionalRequestConflict` are an `UploadTransportError`. The SDK
|
|
62
168
|
* does not impose a client-side cap; it surfaces the refusal. `filename` and
|
|
63
169
|
* `status` locate it, and `code` says why: the SDK sets it on every one it raises,
|
|
64
170
|
* so it is undefined only on one a caller constructs without it.
|
|
@@ -78,41 +184,96 @@ export class RejectedAssetError extends InputPreparationError {
|
|
|
78
184
|
/**
|
|
79
185
|
* The configured deployment does not support upload (no `/v1/upload` route, seen
|
|
80
186
|
* as a `404`). Upload is a hosted Pipelex-product capability even though the SDK
|
|
81
|
-
* can be pointed at other base URLs.
|
|
187
|
+
* can be pointed at other base URLs. Its verdict is `config`, not retryable: the base URL
|
|
188
|
+
* must point at a deployment that serves upload. `filename` is the file whose upload met it, as
|
|
189
|
+
* `uploadFile` sent it.
|
|
82
190
|
*/
|
|
83
191
|
export class UnsupportedUploadCapabilityError extends InputPreparationError {
|
|
192
|
+
filename;
|
|
84
193
|
constructor(message, options) {
|
|
85
|
-
super(message, options);
|
|
194
|
+
super(message, { ...options, verdict: makeVerdict("config", false) });
|
|
86
195
|
this.name = "UnsupportedUploadCapabilityError";
|
|
196
|
+
this.filename = options?.filename;
|
|
87
197
|
}
|
|
88
198
|
}
|
|
89
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* Upload was not authorized — a `401`/`403` from the upload route. Its verdict is `config`, not
|
|
201
|
+
* retryable: the credential must change. `filename` is the file whose upload was refused, as
|
|
202
|
+
* `uploadFile` sent it.
|
|
203
|
+
*/
|
|
90
204
|
export class UploadAuthenticationError extends InputPreparationError {
|
|
91
205
|
status;
|
|
206
|
+
filename;
|
|
92
207
|
constructor(message, status, options) {
|
|
93
|
-
super(message, options);
|
|
208
|
+
super(message, { ...options, verdict: makeVerdict("config", false) });
|
|
94
209
|
this.name = "UploadAuthenticationError";
|
|
95
210
|
this.status = status;
|
|
211
|
+
this.filename = options?.filename;
|
|
96
212
|
}
|
|
97
213
|
}
|
|
98
214
|
/**
|
|
99
215
|
* A network or server fault reaching the upload route or storage — an unreachable
|
|
100
|
-
* host, a timeout, a `5xx`, a refused redirect, storage timing out on the body,
|
|
101
|
-
*
|
|
216
|
+
* host, a timeout, a `5xx`, a refused redirect, storage timing out on the body,
|
|
217
|
+
* storage timing out or throttling the request (a `408` or a `429`), or any other
|
|
218
|
+
* unexpected `upload()` failure. `code` says which: the SDK sets it on
|
|
102
219
|
* every one it raises, so it is undefined only on one a caller constructs without
|
|
103
220
|
* it. `status` is the HTTP status when a response produced it, and undefined when
|
|
104
221
|
* none did. From `uploadFile` the wrapped `ApiResponseError` is also reachable via
|
|
105
|
-
* `cause
|
|
106
|
-
* echo the grant's credential.
|
|
222
|
+
* `cause`, and `filename` is the file it was sending; `uploadWithGrant` wraps no response,
|
|
223
|
+
* because storage's error body can echo the grant's credential, and leaves `filename` undefined.
|
|
224
|
+
*
|
|
225
|
+
* Its verdict is the wrapped error's when `cause` carries one: `uploadFile` wraps the
|
|
226
|
+
* `ApiResponseError` or `ApiUnreachableError` the client's `upload()` threw, and `code` is too
|
|
227
|
+
* coarse to judge it by (a `402` plan limit is `unexpected`, like a malformed answer). Otherwise
|
|
228
|
+
* — always the case from `uploadWithGrant`, which wraps no response — `code` decides: `timeout`,
|
|
229
|
+
* `storage_timeout` and `conflict` are `runtime` and retryable, `conflict` because storage
|
|
230
|
+
* documents its `409 ConditionalRequestConflict` as retryable and the grant is not spent by it (a
|
|
231
|
+
* spent grant is the `412` of a `RejectedAssetError` with code `grant_used`); `server_error` is
|
|
232
|
+
* `runtime`, and retryable when the fallback table a refused API request reads would call its
|
|
233
|
+
* `status` retryable — any `5xx` but a `501`, which storage answers for a request it does not
|
|
234
|
+
* implement — or when it carries no status; `unexpected` is `runtime`, and retryable when that
|
|
235
|
+
* table would call its `status` retryable — storage's `408` or `429`, refused for its timing —
|
|
236
|
+
* and not retryable for any other status or none; `unreachable` is `config` and retryable, like
|
|
237
|
+
* `ApiUnreachableError`; `redirected` is `config` and not retryable; `invalid_grant_url` and no
|
|
238
|
+
* code at all are `runtime` and not retryable.
|
|
107
239
|
*/
|
|
108
240
|
export class UploadTransportError extends InputPreparationError {
|
|
109
241
|
status;
|
|
110
242
|
code;
|
|
243
|
+
filename;
|
|
111
244
|
constructor(message, options) {
|
|
112
|
-
super(message,
|
|
245
|
+
super(message, {
|
|
246
|
+
...options,
|
|
247
|
+
verdict: errorVerdictOf(options?.cause) ?? uploadTransportVerdict(options?.code, options?.status),
|
|
248
|
+
});
|
|
113
249
|
this.name = "UploadTransportError";
|
|
114
250
|
this.status = options?.status;
|
|
115
251
|
this.code = options?.code;
|
|
252
|
+
this.filename = options?.filename;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** The verdict of an upload transport failure that wraps no error carrying one. */
|
|
256
|
+
function uploadTransportVerdict(code, status) {
|
|
257
|
+
switch (code) {
|
|
258
|
+
case "timeout":
|
|
259
|
+
case "storage_timeout":
|
|
260
|
+
case "conflict":
|
|
261
|
+
return makeVerdict("runtime", true);
|
|
262
|
+
case "server_error":
|
|
263
|
+
// A fault in storage may pass, but a 501 says storage does not implement the request, and
|
|
264
|
+
// sending it again will not change that. The status reads as an API's would.
|
|
265
|
+
return makeVerdict("runtime", status === undefined || fallbackVerdict(status, undefined, true).retryable);
|
|
266
|
+
case "unexpected":
|
|
267
|
+
// Storage's 408 or 429 refused the request for its timing, which a later attempt can pass;
|
|
268
|
+
// any other status, or none, says nothing of the kind.
|
|
269
|
+
return makeVerdict("runtime", status !== undefined && fallbackVerdict(status, undefined, true).retryable);
|
|
270
|
+
case "unreachable":
|
|
271
|
+
return makeVerdict("config", true);
|
|
272
|
+
case "redirected":
|
|
273
|
+
return makeVerdict("config", false);
|
|
274
|
+
default:
|
|
275
|
+
// `invalid_grant_url`, no code, and a code this version does not know.
|
|
276
|
+
return makeVerdict("runtime", false);
|
|
116
277
|
}
|
|
117
278
|
}
|
|
118
279
|
/**
|
|
@@ -124,10 +285,15 @@ export class UploadTransportError extends InputPreparationError {
|
|
|
124
285
|
* operation throws only when it can produce no verdict at all. Transport
|
|
125
286
|
* failures on the resolve route (`ApiResponseError`, `ApiUnreachableError`)
|
|
126
287
|
* and the run-lifecycle errors propagate unchanged, so they are not subclasses.
|
|
288
|
+
*
|
|
289
|
+
* Its verdict is `runtime`, not retryable, unless `options.verdict` declares another: the SDK
|
|
290
|
+
* declares `input` where it refuses an argument (a `scope`, a bound, a location) and `config`
|
|
291
|
+
* where the environment refuses the download (a runtime with no filesystem, a directory that
|
|
292
|
+
* cannot be created).
|
|
127
293
|
*/
|
|
128
|
-
export class ArtifactOperationError extends
|
|
294
|
+
export class ArtifactOperationError extends PipelexRequestError {
|
|
129
295
|
constructor(message, options) {
|
|
130
|
-
super(message, options);
|
|
296
|
+
super(message, options?.verdict ?? makeVerdict("runtime", false), options);
|
|
131
297
|
this.name = "ArtifactOperationError";
|
|
132
298
|
}
|
|
133
299
|
}
|
|
@@ -159,32 +325,65 @@ export class ScopeUnavailableError extends ArtifactOperationError {
|
|
|
159
325
|
* (any other non-2xx), `too_large`, `timeout`, `network`. `status` is the
|
|
160
326
|
* store's HTTP status when one was received. `downloadArtifacts` never lets
|
|
161
327
|
* this escape: it becomes the item's `error`.
|
|
328
|
+
*
|
|
329
|
+
* Its verdict follows `code`: `invalid_storage_uri`, `forbidden`, `unsupported_url`, `not_found`
|
|
330
|
+
* and `too_large` are `input` and not retryable, since the reference or the bound must change;
|
|
331
|
+
* `plain_http_refused` is `config` and not retryable; `redirect_refused` and `store_refused` are
|
|
332
|
+
* `runtime` and not retryable; `timeout` and `network` are `runtime` and retryable; `store_error`
|
|
333
|
+
* is `runtime`, and retryable when the fallback table a refused API request reads would call its
|
|
334
|
+
* `status` retryable — a `408`, a `429` or a `5xx` other than `501` — and not retryable for any
|
|
335
|
+
* other status or none. A code this version does not know is `runtime` and not retryable.
|
|
162
336
|
*/
|
|
163
337
|
export class ArtifactFetchError extends ArtifactOperationError {
|
|
164
338
|
uri;
|
|
165
339
|
code;
|
|
166
340
|
status;
|
|
167
341
|
constructor(message, uri, code, status, options) {
|
|
168
|
-
super(message, options);
|
|
342
|
+
super(message, { ...options, verdict: artifactFetchVerdict(code, status) });
|
|
169
343
|
this.name = "ArtifactFetchError";
|
|
170
344
|
this.uri = uri;
|
|
171
345
|
this.code = code;
|
|
172
346
|
this.status = status;
|
|
173
347
|
}
|
|
174
348
|
}
|
|
349
|
+
/** The verdict of one reference's fetch failure, by its code and the store's status. */
|
|
350
|
+
function artifactFetchVerdict(code, status) {
|
|
351
|
+
switch (code) {
|
|
352
|
+
case "invalid_storage_uri":
|
|
353
|
+
case "forbidden":
|
|
354
|
+
case "unsupported_url":
|
|
355
|
+
case "not_found":
|
|
356
|
+
case "too_large":
|
|
357
|
+
return makeVerdict("input", false);
|
|
358
|
+
case "plain_http_refused":
|
|
359
|
+
return makeVerdict("config", false);
|
|
360
|
+
case "timeout":
|
|
361
|
+
case "network":
|
|
362
|
+
return makeVerdict("runtime", true);
|
|
363
|
+
case "store_error":
|
|
364
|
+
// The store's status reads as an API's would: refused for its timing (408, 429) or a fault
|
|
365
|
+
// that may pass (a 5xx) can succeed on a retry, while a 501 or a 4xx will not.
|
|
366
|
+
return makeVerdict("runtime", status !== undefined && fallbackVerdict(status, undefined, true).retryable);
|
|
367
|
+
default:
|
|
368
|
+
// `redirect_refused`, `store_refused`, and a code this version does not know.
|
|
369
|
+
return makeVerdict("runtime", false);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
175
372
|
/**
|
|
176
373
|
* The resolve route refused the caller's credential (`401` / `403`) during a
|
|
177
374
|
* `downloadArtifacts` call. No further reference can be resolved with it, so
|
|
178
375
|
* the download stops — but the files already saved are real, and `verdict`
|
|
179
376
|
* carries the result as it stood: every item saved before the refusal, and
|
|
180
377
|
* the rest marked `aborted`. `status` is the route's status; the wrapped
|
|
181
|
-
* `ApiResponseError` is reachable via `cause`.
|
|
378
|
+
* `ApiResponseError` is reachable via `cause`. (`verdict` is the download's result, not the
|
|
379
|
+
* error's own: that is `retryable` and `errorDomain`, `config` and not retryable, since the
|
|
380
|
+
* credential must change.)
|
|
182
381
|
*/
|
|
183
382
|
export class ArtifactAuthenticationError extends ArtifactOperationError {
|
|
184
383
|
status;
|
|
185
384
|
verdict;
|
|
186
385
|
constructor(message, status, verdict, options) {
|
|
187
|
-
super(message, options);
|
|
386
|
+
super(message, { ...options, verdict: makeVerdict("config", false) });
|
|
188
387
|
this.name = "ArtifactAuthenticationError";
|
|
189
388
|
this.status = status;
|
|
190
389
|
this.verdict = verdict;
|
|
@@ -198,12 +397,17 @@ export class ArtifactAuthenticationError extends ArtifactOperationError {
|
|
|
198
397
|
*
|
|
199
398
|
* `code` is the underlying network error code when available
|
|
200
399
|
* (`ECONNREFUSED`, `ENOTFOUND`, `ETIMEDOUT`, `EAI_AGAIN`, `ABORT_TIMEOUT`).
|
|
400
|
+
*
|
|
401
|
+
* Its verdict is `config` and retryable: the address or the network must be checked, and a later
|
|
402
|
+
* attempt can get through. With the code `ABORT_TIMEOUT`, the SDK's own request timeout, it is
|
|
403
|
+
* `runtime` and retryable instead: the API took the request and did not answer in time, which is
|
|
404
|
+
* no fault of the base URL.
|
|
201
405
|
*/
|
|
202
|
-
export class ApiUnreachableError extends
|
|
406
|
+
export class ApiUnreachableError extends PipelexRequestError {
|
|
203
407
|
apiUrl;
|
|
204
408
|
code;
|
|
205
409
|
constructor(message, apiUrl, code, options) {
|
|
206
|
-
super(message, options);
|
|
410
|
+
super(message, makeVerdict(code === "ABORT_TIMEOUT" ? "runtime" : "config", true), options);
|
|
207
411
|
this.name = "ApiUnreachableError";
|
|
208
412
|
this.apiUrl = apiUrl;
|
|
209
413
|
this.code = code;
|
|
@@ -214,14 +418,17 @@ export class ApiUnreachableError extends PipelineRequestError {
|
|
|
214
418
|
* hosted gateway's ~30s synchronous-request limit. The blocking path cannot
|
|
215
419
|
* run methods longer than 30s behind the hosted gateway — use the durable run
|
|
216
420
|
* lifecycle (start + poll) instead.
|
|
421
|
+
*
|
|
422
|
+
* Its verdict is `input`, not retryable: asking again meets the same limit, and the caller fixes
|
|
423
|
+
* it by starting the run and polling.
|
|
217
424
|
*/
|
|
218
|
-
export class PipelineExecuteTimeoutError extends
|
|
425
|
+
export class PipelineExecuteTimeoutError extends PipelexRequestError {
|
|
219
426
|
elapsedMs;
|
|
220
427
|
constructor(elapsedMs, options) {
|
|
221
428
|
const seconds = Math.round(elapsedMs / 1000);
|
|
222
429
|
super(`The Pipelex Hosted API times out synchronous requests after ~30s — this run took ${seconds}s. ` +
|
|
223
430
|
"The blocking execute path can't run methods longer than 30s behind the gateway. " +
|
|
224
|
-
"Start the run and poll for its result instead: `start()` then `waitForResult(runId)`.", options);
|
|
431
|
+
"Start the run and poll for its result instead: `start()` then `waitForResult(runId)`.", makeVerdict("input", false), options);
|
|
225
432
|
this.name = "PipelineExecuteTimeoutError";
|
|
226
433
|
this.elapsedMs = elapsedMs;
|
|
227
434
|
}
|
|
@@ -247,19 +454,31 @@ export class PipelineExecuteTimeoutError extends PipelineRequestError {
|
|
|
247
454
|
* report's message (`Run finished with status FAILED: <message>`), so printing the error
|
|
248
455
|
* already tells the reason.
|
|
249
456
|
* - `runId` locates the run, for a status read or a support request.
|
|
457
|
+
*
|
|
458
|
+
* Its verdict comes from the report. `errorDomain` is the report's `error_domain` when it is one
|
|
459
|
+
* of the three domains, and `runtime` otherwise, including a run with no report. `retryable` is
|
|
460
|
+
* true only when the report's `retryable` is `true`: a report that says nothing, and a run with
|
|
461
|
+
* none, read as not retryable, since starting the run again spends credit and nothing says it
|
|
462
|
+
* would succeed. The report keeps its own `retryable` as written, so a consumer that words the
|
|
463
|
+
* unknown differently still can.
|
|
250
464
|
*/
|
|
251
|
-
export class RunFailedError extends
|
|
465
|
+
export class RunFailedError extends PipelexRequestError {
|
|
252
466
|
runId;
|
|
253
467
|
status;
|
|
254
468
|
error;
|
|
255
469
|
constructor(message, runId, status, options) {
|
|
256
|
-
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
470
|
+
super(message, runFailureVerdict(options?.error ?? null), options?.cause === undefined ? undefined : { cause: options.cause });
|
|
257
471
|
this.name = "RunFailedError";
|
|
258
472
|
this.runId = runId;
|
|
259
473
|
this.status = status;
|
|
260
474
|
this.error = options?.error ?? null;
|
|
261
475
|
}
|
|
262
476
|
}
|
|
477
|
+
/** The verdict of a failed run, read from its stored report. */
|
|
478
|
+
function runFailureVerdict(report) {
|
|
479
|
+
const domain = report?.error_domain;
|
|
480
|
+
return makeVerdict(isErrorDomain(domain) ? domain : "runtime", report?.retryable === true);
|
|
481
|
+
}
|
|
263
482
|
/**
|
|
264
483
|
* Thrown when a completed run cannot deliver its main stuff.
|
|
265
484
|
*
|
|
@@ -269,11 +488,13 @@ export class RunFailedError extends PipelineRequestError {
|
|
|
269
488
|
* `execute` response named a `main_stuff_name` whose stuff is absent from the returned working
|
|
270
489
|
* memory. `runId` locates the run. (An empty-but-present main stuff — `{ items: [] }`, `{ text:
|
|
271
490
|
* "" }` — is a valid output and does NOT throw; only a genuinely absent one does.)
|
|
491
|
+
*
|
|
492
|
+
* Its verdict is `runtime`, not retryable: the API broke its own contract on a completed run.
|
|
272
493
|
*/
|
|
273
|
-
export class MissingMainStuffError extends
|
|
494
|
+
export class MissingMainStuffError extends PipelexRequestError {
|
|
274
495
|
runId;
|
|
275
496
|
constructor(message, runId) {
|
|
276
|
-
super(message);
|
|
497
|
+
super(message, makeVerdict("runtime", false));
|
|
277
498
|
this.name = "MissingMainStuffError";
|
|
278
499
|
this.runId = runId;
|
|
279
500
|
}
|
|
@@ -282,12 +503,14 @@ export class MissingMainStuffError extends PipelineRequestError {
|
|
|
282
503
|
* Thrown when `waitForResult` exceeds its `timeoutMs` before the run reaches a
|
|
283
504
|
* terminal state. The run is NOT cancelled — it keeps executing server-side and
|
|
284
505
|
* can be resumed later by `runId` (the poll loop just stopped waiting).
|
|
506
|
+
*
|
|
507
|
+
* Its verdict is `runtime` and retryable: the run is still going, and waiting again can succeed.
|
|
285
508
|
*/
|
|
286
|
-
export class RunTimeoutError extends
|
|
509
|
+
export class RunTimeoutError extends PipelexRequestError {
|
|
287
510
|
runId;
|
|
288
511
|
timeoutMs;
|
|
289
512
|
constructor(message, runId, timeoutMs) {
|
|
290
|
-
super(message);
|
|
513
|
+
super(message, makeVerdict("runtime", true));
|
|
291
514
|
this.name = "RunTimeoutError";
|
|
292
515
|
this.runId = runId;
|
|
293
516
|
this.timeoutMs = timeoutMs;
|
|
@@ -301,13 +524,15 @@ export class RunTimeoutError extends PipelineRequestError {
|
|
|
301
524
|
* when it cannot hold the connection open. The run keeps executing
|
|
302
525
|
* server-side — resume by `runId` (`getRunResult` / `waitForResult` on a
|
|
303
526
|
* hosted deployment, or the `location` status resource when provided).
|
|
527
|
+
*
|
|
528
|
+
* Its verdict is `runtime` and retryable: the run is still going, and is resumed by its id.
|
|
304
529
|
*/
|
|
305
|
-
export class RunStillRunningError extends
|
|
530
|
+
export class RunStillRunningError extends PipelexRequestError {
|
|
306
531
|
runId;
|
|
307
532
|
retryAfterSeconds;
|
|
308
533
|
location;
|
|
309
534
|
constructor(message, runId, retryAfterSeconds = null, location = null, options) {
|
|
310
|
-
super(message, options);
|
|
535
|
+
super(message, makeVerdict("runtime", true), options);
|
|
311
536
|
this.name = "RunStillRunningError";
|
|
312
537
|
this.runId = runId;
|
|
313
538
|
this.retryAfterSeconds = retryAfterSeconds;
|
|
@@ -323,26 +548,61 @@ export class RunStillRunningError extends PipelineRequestError {
|
|
|
323
548
|
* it 404s those routes; only a deployment that includes the platform block
|
|
324
549
|
* (the Pipelex Hosted API) serves status/results. Distinguished from a genuine
|
|
325
550
|
* run-not-found 404, which carries the server's structured error envelope.
|
|
551
|
+
*
|
|
552
|
+
* Its verdict is `config`, not retryable: the base URL points at a bare runner without the run
|
|
553
|
+
* lifecycle.
|
|
326
554
|
*/
|
|
327
|
-
export class RunLifecycleUnavailableError extends
|
|
555
|
+
export class RunLifecycleUnavailableError extends PipelexRequestError {
|
|
328
556
|
apiUrl;
|
|
329
557
|
constructor(message, apiUrl, options) {
|
|
330
|
-
super(message, options);
|
|
558
|
+
super(message, makeVerdict("config", false), options);
|
|
331
559
|
this.name = "RunLifecycleUnavailableError";
|
|
332
560
|
this.apiUrl = apiUrl;
|
|
333
561
|
}
|
|
334
562
|
}
|
|
335
563
|
/**
|
|
336
|
-
*
|
|
564
|
+
* Thrown when a paged-list iterator (`iterateMethods`) refuses to keep following cursors.
|
|
565
|
+
*
|
|
566
|
+
* The ceiling, `pageLimit` pages, sits far beyond any real catalog, so reaching it is a
|
|
567
|
+
* server-side fault — an endpoint minting a fresh cursor forever — not a coverage limit the caller
|
|
568
|
+
* can raise. Throwing beats returning, because a silently truncated list is exactly the bug
|
|
569
|
+
* paging was introduced to remove. Its verdict is `runtime`, not retryable.
|
|
570
|
+
*/
|
|
571
|
+
export class PagingNotTerminatingError extends PipelexRequestError {
|
|
572
|
+
pageLimit;
|
|
573
|
+
constructor(message, pageLimit) {
|
|
574
|
+
super(message, makeVerdict("runtime", false));
|
|
575
|
+
this.name = "PagingNotTerminatingError";
|
|
576
|
+
this.pageLimit = pageLimit;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* A response that DID come back from the API and that the SDK cannot hand back as a result: a
|
|
581
|
+
* non-2xx refusal, with its problem document parsed, or a 2xx answer the SDK could not read —
|
|
582
|
+
* a body that is not JSON, JSON that is not the object the route answers, or a stored method
|
|
583
|
+
* whose `python` field is not the serialized file list. The second carries the answer's status
|
|
584
|
+
* and raw text, no problem member, what made it unreadable as `cause` (the parse failure, or
|
|
585
|
+
* `mthds`'s refusal of the `python` field), and the verdict the fallback gives a 2xx: `runtime`,
|
|
586
|
+
* not retryable.
|
|
337
587
|
*
|
|
338
588
|
* Every error the hosted API answers is an RFC 9457 `application/problem+json` document, and
|
|
339
589
|
* this error carries its members as typed fields, each `undefined` when the document did not
|
|
340
|
-
* carry it (a member of the wrong type reads as absent rather than as a wrong value
|
|
590
|
+
* carry it (a member of the wrong type reads as absent rather than as a wrong value, and the
|
|
591
|
+
* nested members `providerMetadata`, `migration`, `validationErrors` and `errors` are checked
|
|
592
|
+
* field by field as a failed run's stored report is) — except the verdict, which is always
|
|
593
|
+
* decided:
|
|
341
594
|
*
|
|
342
|
-
* - **The
|
|
343
|
-
* `config` (a configuration change), `runtime` (nobody beforehand) — and `
|
|
344
|
-
*
|
|
345
|
-
*
|
|
595
|
+
* - **The verdict.** `errorDomain` says who can fix the failure — `input` (the caller),
|
|
596
|
+
* `config` (a configuration change), `runtime` (nobody beforehand) — and `retryable` whether
|
|
597
|
+
* asking again can succeed. Each is the document's member when the server sent a valid one,
|
|
598
|
+
* and otherwise the SDK's fallback, read from the status, the platform `code` and whether the
|
|
599
|
+
* body names what it refused. A runner often sends `error_domain` without `retryable`, and
|
|
600
|
+
* then the domain it sent decides `retryable`: `input` and `config` are not retryable, as a
|
|
601
|
+
* stored report that says nothing is not, since the caller or the environment must change,
|
|
602
|
+
* while `runtime` takes the fallback's. Nothing on the error says which a value came from:
|
|
603
|
+
* `problemDocument` keeps what the server sent.
|
|
604
|
+
* - **The branch fields.** `errorDomain`, and `type`, the stable URI naming the error class.
|
|
605
|
+
* Branch on these, never on the HTTP status or on the wording of a message.
|
|
346
606
|
* - **The native codes.** `code` is the platform's own closed code (`conflict`, `not_found`,
|
|
347
607
|
* `pipelex_api_key_limit_reached`, …) and `errorType` the runner's open exception class
|
|
348
608
|
* name. Each is finer than `errorDomain` and specific to the surface that emits it; the
|
|
@@ -360,11 +620,11 @@ export class RunLifecycleUnavailableError extends PipelineRequestError {
|
|
|
360
620
|
* `problemDocument` is the decoded document whole, so a member this SDK does not name stays
|
|
361
621
|
* reachable without re-parsing `responseBody`, which is the raw text; it is `undefined` when
|
|
362
622
|
* the body was not a JSON object. The members `mthds`'s own `ApiResponseError` carries have the
|
|
363
|
-
* same names and types
|
|
364
|
-
* `
|
|
365
|
-
* leaves to this SDK.
|
|
623
|
+
* same names here, and the same types except the verdict, which this SDK narrows from optional
|
|
624
|
+
* to decided; the rest (`code`, `errorCategory`, `model`, `provider`, `providerMetadata`,
|
|
625
|
+
* `migration`, `errors`) are the Pipelex members the standard's client leaves to this SDK.
|
|
366
626
|
*/
|
|
367
|
-
export class ApiResponseError extends
|
|
627
|
+
export class ApiResponseError extends PipelexRequestError {
|
|
368
628
|
apiUrl;
|
|
369
629
|
status;
|
|
370
630
|
statusText;
|
|
@@ -393,24 +653,11 @@ export class ApiResponseError extends PipelineRequestError {
|
|
|
393
653
|
* lines for this request.
|
|
394
654
|
*/
|
|
395
655
|
requestId;
|
|
396
|
-
/**
|
|
397
|
-
* The body's `error_domain`: who can fix the failure. `input` — the caller (a malformed
|
|
398
|
-
* bundle, a bad argument, a missing input); `config` — a configuration change (a missing
|
|
399
|
-
* secret, a model the backend does not serve); `runtime` — nobody beforehand (a provider
|
|
400
|
-
* outage during execution). Typed open, as the server owns the vocabulary; `undefined` when
|
|
401
|
-
* the server did not classify it.
|
|
402
|
-
*/
|
|
403
|
-
errorDomain;
|
|
404
656
|
/**
|
|
405
657
|
* The body's `error_category`: the finer classification of an inference failure — known
|
|
406
658
|
* values `transient`, `configuration`, `content`, `capacity`, `ambiguous`, `unknown`.
|
|
407
659
|
*/
|
|
408
660
|
errorCategory;
|
|
409
|
-
/**
|
|
410
|
-
* The body's `retryable`: whether retrying the same request can plausibly succeed.
|
|
411
|
-
* `undefined` means unknown, which is not the same as `false`.
|
|
412
|
-
*/
|
|
413
|
-
retryable;
|
|
414
661
|
/** The body's `user_action`: what the caller should do next, when the server can say. */
|
|
415
662
|
userAction;
|
|
416
663
|
/** The body's `model`: the model an inference failure used. */
|
|
@@ -444,7 +691,7 @@ export class ApiResponseError extends PipelineRequestError {
|
|
|
444
691
|
*/
|
|
445
692
|
validationErrors;
|
|
446
693
|
constructor(message, apiUrl, status, statusText, responseBody, errorType, serverMessage, validationErrors, code, options) {
|
|
447
|
-
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
694
|
+
super(message, responseVerdict(status, errorType, code, options?.problem), options?.cause === undefined ? undefined : { cause: options.cause });
|
|
448
695
|
this.name = "ApiResponseError";
|
|
449
696
|
this.apiUrl = apiUrl;
|
|
450
697
|
this.status = status;
|
|
@@ -459,9 +706,7 @@ export class ApiResponseError extends PipelineRequestError {
|
|
|
459
706
|
this.title = problem?.title;
|
|
460
707
|
this.instance = problem?.instance;
|
|
461
708
|
this.requestId = problem?.requestId;
|
|
462
|
-
this.errorDomain = problem?.errorDomain;
|
|
463
709
|
this.errorCategory = problem?.errorCategory;
|
|
464
|
-
this.retryable = problem?.retryable;
|
|
465
710
|
this.userAction = problem?.userAction;
|
|
466
711
|
this.model = problem?.model;
|
|
467
712
|
this.provider = problem?.provider;
|
|
@@ -471,4 +716,25 @@ export class ApiResponseError extends PipelineRequestError {
|
|
|
471
716
|
this.problemDocument = options?.problemDocument;
|
|
472
717
|
}
|
|
473
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* The verdict of a refused request: each member the document's own when it is valid, and the
|
|
721
|
+
* fallback's otherwise, except that a sent `input` or `config` domain with no sent `retryable` is
|
|
722
|
+
* not retryable. A `404` is named when the body carries a platform `code` or a runner
|
|
723
|
+
* `error_type`, read from the constructor's arguments, so an `ApiResponseError` a consumer builds
|
|
724
|
+
* by hand gets the verdict the client would give it.
|
|
725
|
+
*/
|
|
726
|
+
function responseVerdict(status, errorType, code, problem) {
|
|
727
|
+
const fallback = fallbackVerdict(status, code, code !== undefined || errorType !== undefined);
|
|
728
|
+
const sentDomain = problem?.errorDomain;
|
|
729
|
+
const sentRetryable = problem?.retryable;
|
|
730
|
+
if (!isErrorDomain(sentDomain)) {
|
|
731
|
+
return makeVerdict(fallback.errorDomain, typeof sentRetryable === "boolean" ? sentRetryable : fallback.retryable);
|
|
732
|
+
}
|
|
733
|
+
if (typeof sentRetryable === "boolean")
|
|
734
|
+
return makeVerdict(sentDomain, sentRetryable);
|
|
735
|
+
// The server said who fixes the failure and nothing of a retry. When the caller or the
|
|
736
|
+
// environment must change, asking again unchanged meets the same answer, whatever the status
|
|
737
|
+
// would suggest; only a `runtime` fault keeps the status's reading.
|
|
738
|
+
return makeVerdict(sentDomain, sentDomain === "runtime" ? fallback.retryable : false);
|
|
739
|
+
}
|
|
474
740
|
//# sourceMappingURL=errors.js.map
|