@unofficialbox/box-open-sdk 0.5.0 → 0.6.1
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/dist/cjs/errors.js +47 -0
- package/dist/cjs/runtime.js +3 -0
- package/dist/esm/errors.js +46 -0
- package/dist/esm/runtime.js +4 -1
- package/dist/types/errors.d.ts +21 -0
- package/docs/errors.md +39 -6
- package/package.json +1 -1
package/dist/cjs/errors.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Code generated by box-gantry (vendored from runtimes/typescript/gantryruntime/src/errors.ts). DO NOT EDIT.
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.BoxApiError = void 0;
|
|
5
|
+
exports.apiErrorFromResponse = apiErrorFromResponse;
|
|
5
6
|
/**
|
|
6
7
|
* A runtime error: a failed request, auth acquisition, or body decode. The
|
|
7
8
|
* generated SDK's exceptions model (TR-TS.3) surfaces every failure as a thrown
|
|
@@ -11,10 +12,56 @@ exports.BoxApiError = void 0;
|
|
|
11
12
|
class BoxApiError extends Error {
|
|
12
13
|
/** The HTTP status, when the error carries a response. */
|
|
13
14
|
status;
|
|
15
|
+
/** Box's error `code` (e.g. `not_found`), when the body was a Box error. */
|
|
16
|
+
code;
|
|
17
|
+
/** Box's error `message`, when the body was a Box error. */
|
|
18
|
+
boxMessage;
|
|
19
|
+
/** Box's `request_id`, when the body was a Box error. */
|
|
20
|
+
requestId;
|
|
21
|
+
/** The raw response body of a non-2xx response. */
|
|
22
|
+
body;
|
|
14
23
|
constructor(message, options = {}) {
|
|
15
24
|
super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
|
|
16
25
|
this.name = 'BoxApiError';
|
|
17
26
|
this.status = options.status;
|
|
27
|
+
this.code = options.code;
|
|
28
|
+
this.boxMessage = options.boxMessage;
|
|
29
|
+
this.requestId = options.requestId;
|
|
30
|
+
this.body = options.body;
|
|
18
31
|
}
|
|
19
32
|
}
|
|
20
33
|
exports.BoxApiError = BoxApiError;
|
|
34
|
+
/** The error for a non-2xx response: `Client.fetch` throws it once the retry
|
|
35
|
+
* budget and the single 401 refresh are spent, so a caller never mistakes an
|
|
36
|
+
* error body for a result. Box's error fields are read from the body when it
|
|
37
|
+
* is a Box error object; the raw body is kept either way. */
|
|
38
|
+
function apiErrorFromResponse(status, body) {
|
|
39
|
+
let payload;
|
|
40
|
+
try {
|
|
41
|
+
payload = JSON.parse(new TextDecoder().decode(body));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
payload = undefined;
|
|
45
|
+
}
|
|
46
|
+
const field = (key) => {
|
|
47
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const value = payload[key];
|
|
51
|
+
return typeof value === 'string' ? value : undefined;
|
|
52
|
+
};
|
|
53
|
+
const code = field('code');
|
|
54
|
+
const boxMessage = field('message');
|
|
55
|
+
const requestId = field('request_id');
|
|
56
|
+
let message = `box: status ${status}`;
|
|
57
|
+
if (code !== undefined) {
|
|
58
|
+
message += ` ${code}`;
|
|
59
|
+
}
|
|
60
|
+
if (boxMessage !== undefined) {
|
|
61
|
+
message += `: ${boxMessage}`;
|
|
62
|
+
}
|
|
63
|
+
if (requestId !== undefined) {
|
|
64
|
+
message += ` (request_id ${requestId})`;
|
|
65
|
+
}
|
|
66
|
+
return new BoxApiError(message, { status, code, boxMessage, requestId, body });
|
|
67
|
+
}
|
package/dist/cjs/runtime.js
CHANGED
|
@@ -168,6 +168,9 @@ class Client {
|
|
|
168
168
|
await sleep(retryDelay(response, attempt), signal);
|
|
169
169
|
continue;
|
|
170
170
|
}
|
|
171
|
+
if (response.status < 200 || response.status >= 300) {
|
|
172
|
+
throw (0, errors_js_1.apiErrorFromResponse)(response.status, bodyBytes);
|
|
173
|
+
}
|
|
171
174
|
return response;
|
|
172
175
|
}
|
|
173
176
|
throw new errors_js_1.BoxApiError(`request failed after ${this.maxRetries + 1} attempts`, {
|
package/dist/esm/errors.js
CHANGED
|
@@ -8,9 +8,55 @@
|
|
|
8
8
|
export class BoxApiError extends Error {
|
|
9
9
|
/** The HTTP status, when the error carries a response. */
|
|
10
10
|
status;
|
|
11
|
+
/** Box's error `code` (e.g. `not_found`), when the body was a Box error. */
|
|
12
|
+
code;
|
|
13
|
+
/** Box's error `message`, when the body was a Box error. */
|
|
14
|
+
boxMessage;
|
|
15
|
+
/** Box's `request_id`, when the body was a Box error. */
|
|
16
|
+
requestId;
|
|
17
|
+
/** The raw response body of a non-2xx response. */
|
|
18
|
+
body;
|
|
11
19
|
constructor(message, options = {}) {
|
|
12
20
|
super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
|
|
13
21
|
this.name = 'BoxApiError';
|
|
14
22
|
this.status = options.status;
|
|
23
|
+
this.code = options.code;
|
|
24
|
+
this.boxMessage = options.boxMessage;
|
|
25
|
+
this.requestId = options.requestId;
|
|
26
|
+
this.body = options.body;
|
|
15
27
|
}
|
|
16
28
|
}
|
|
29
|
+
/** The error for a non-2xx response: `Client.fetch` throws it once the retry
|
|
30
|
+
* budget and the single 401 refresh are spent, so a caller never mistakes an
|
|
31
|
+
* error body for a result. Box's error fields are read from the body when it
|
|
32
|
+
* is a Box error object; the raw body is kept either way. */
|
|
33
|
+
export function apiErrorFromResponse(status, body) {
|
|
34
|
+
let payload;
|
|
35
|
+
try {
|
|
36
|
+
payload = JSON.parse(new TextDecoder().decode(body));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
payload = undefined;
|
|
40
|
+
}
|
|
41
|
+
const field = (key) => {
|
|
42
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const value = payload[key];
|
|
46
|
+
return typeof value === 'string' ? value : undefined;
|
|
47
|
+
};
|
|
48
|
+
const code = field('code');
|
|
49
|
+
const boxMessage = field('message');
|
|
50
|
+
const requestId = field('request_id');
|
|
51
|
+
let message = `box: status ${status}`;
|
|
52
|
+
if (code !== undefined) {
|
|
53
|
+
message += ` ${code}`;
|
|
54
|
+
}
|
|
55
|
+
if (boxMessage !== undefined) {
|
|
56
|
+
message += `: ${boxMessage}`;
|
|
57
|
+
}
|
|
58
|
+
if (requestId !== undefined) {
|
|
59
|
+
message += ` (request_id ${requestId})`;
|
|
60
|
+
}
|
|
61
|
+
return new BoxApiError(message, { status, code, boxMessage, requestId, body });
|
|
62
|
+
}
|
package/dist/esm/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by box-gantry (vendored from runtimes/typescript/gantryruntime/src/runtime.ts). DO NOT EDIT.
|
|
2
|
-
import { BoxApiError } from './errors.js';
|
|
2
|
+
import { BoxApiError, apiErrorFromResponse } from './errors.js';
|
|
3
3
|
export { BoxApiError } from './errors.js';
|
|
4
4
|
export * from './auth.js';
|
|
5
5
|
/** The default base URLs for the D-106 URL classes. */
|
|
@@ -137,6 +137,9 @@ export class Client {
|
|
|
137
137
|
await sleep(retryDelay(response, attempt), signal);
|
|
138
138
|
continue;
|
|
139
139
|
}
|
|
140
|
+
if (response.status < 200 || response.status >= 300) {
|
|
141
|
+
throw apiErrorFromResponse(response.status, bodyBytes);
|
|
142
|
+
}
|
|
140
143
|
return response;
|
|
141
144
|
}
|
|
142
145
|
throw new BoxApiError(`request failed after ${this.maxRetries + 1} attempts`, {
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
export interface BoxApiErrorOptions {
|
|
3
3
|
/** The HTTP status, when the error carries a response. */
|
|
4
4
|
status?: number;
|
|
5
|
+
/** Box's error `code` (e.g. `not_found`), from a Box error body. */
|
|
6
|
+
code?: string;
|
|
7
|
+
/** Box's error `message`, from a Box error body. */
|
|
8
|
+
boxMessage?: string;
|
|
9
|
+
/** Box's `request_id`, from a Box error body — quote it to Box support. */
|
|
10
|
+
requestId?: string;
|
|
11
|
+
/** The raw response body of a non-2xx response. */
|
|
12
|
+
body?: Uint8Array;
|
|
5
13
|
/** The underlying failure (a transport error, a decode error), preserved for
|
|
6
14
|
* diagnostics. */
|
|
7
15
|
cause?: unknown;
|
|
@@ -15,5 +23,18 @@ export interface BoxApiErrorOptions {
|
|
|
15
23
|
export declare class BoxApiError extends Error {
|
|
16
24
|
/** The HTTP status, when the error carries a response. */
|
|
17
25
|
readonly status?: number;
|
|
26
|
+
/** Box's error `code` (e.g. `not_found`), when the body was a Box error. */
|
|
27
|
+
readonly code?: string;
|
|
28
|
+
/** Box's error `message`, when the body was a Box error. */
|
|
29
|
+
readonly boxMessage?: string;
|
|
30
|
+
/** Box's `request_id`, when the body was a Box error. */
|
|
31
|
+
readonly requestId?: string;
|
|
32
|
+
/** The raw response body of a non-2xx response. */
|
|
33
|
+
readonly body?: Uint8Array;
|
|
18
34
|
constructor(message: string, options?: BoxApiErrorOptions);
|
|
19
35
|
}
|
|
36
|
+
/** The error for a non-2xx response: `Client.fetch` throws it once the retry
|
|
37
|
+
* budget and the single 401 refresh are spent, so a caller never mistakes an
|
|
38
|
+
* error body for a result. Box's error fields are read from the body when it
|
|
39
|
+
* is a Box error object; the raw body is kept either way. */
|
|
40
|
+
export declare function apiErrorFromResponse(status: number, body: Uint8Array): BoxApiError;
|
package/docs/errors.md
CHANGED
|
@@ -4,17 +4,50 @@
|
|
|
4
4
|
Methods are `async` and surface failure by throwing — the return type only
|
|
5
5
|
describes the success value. A thrown `BoxApiError` is either a transport
|
|
6
6
|
failure or a non-2xx response after the network layer's retries (exponential
|
|
7
|
-
backoff + jitter, `401` refresh, `Retry-After`).
|
|
8
|
-
|
|
9
|
-
transport
|
|
7
|
+
backoff + jitter, `401` refresh, `Retry-After`). For a non-2xx response it
|
|
8
|
+
carries the HTTP `status`, Box's error `code`, `boxMessage`, and `requestId`,
|
|
9
|
+
and the raw `body`; transport and decode failures carry the underlying
|
|
10
|
+
`cause` instead:
|
|
10
11
|
|
|
11
12
|
```ts
|
|
12
13
|
import { runtime } from '@unofficialbox/box-open-sdk';
|
|
13
14
|
try {
|
|
14
|
-
await client.files.
|
|
15
|
+
await client.files.get(fileId);
|
|
15
16
|
} catch (err) {
|
|
16
|
-
if (err instanceof runtime.BoxApiError) {
|
|
17
|
-
// err.status
|
|
17
|
+
if (err instanceof runtime.BoxApiError) {
|
|
18
|
+
// err.status, err.code, err.boxMessage, err.requestId, err.body, err.cause
|
|
19
|
+
}
|
|
18
20
|
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Etags: `If-None-Match` and `If-Match`
|
|
24
|
+
|
|
25
|
+
Files and folders carry an `etag`. Send it back to stay in sync with Box
|
|
26
|
+
(Box's [Ensure consistency](https://developer.box.com/guides/api-calls/ensure-consistency/)
|
|
27
|
+
guide):
|
|
28
|
+
|
|
29
|
+
- **Reads** — pass the stored etag as `if-none-match` on a file or folder
|
|
30
|
+
`get` (or a shared-item lookup). Box answers `304 Not Modified` when
|
|
31
|
+
nothing changed, and the SDK raises it as the error above with status
|
|
32
|
+
`304`: keep your cached copy.
|
|
33
|
+
- **Writes** — pass it as `if-match` on a file or folder update or delete,
|
|
34
|
+
or a new file version upload. Box answers `412 Precondition Failed`
|
|
35
|
+
(code `precondition_failed`) when the item changed since you read it:
|
|
36
|
+
read it again, reapply your change, retry. `If-Match` can't block a move.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
try {
|
|
40
|
+
await client.files.get(fileId, { 'if-none-match': etag });
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (err instanceof runtime.BoxApiError && err.status === 304) {
|
|
43
|
+
// unchanged since etag
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await client.files.update(fileId, body, { 'if-match': etag });
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof runtime.BoxApiError && err.status === 412) {
|
|
50
|
+
// changed since etag: re-read and retry
|
|
51
|
+
}
|
|
19
52
|
}
|
|
20
53
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unofficialbox/box-open-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Box API client for TypeScript (open source, community, punk rock) — typed models, async managers, and a fetch runtime with retry, backoff, and token refresh.",
|
|
5
5
|
"keywords": ["box", "box-api", "sdk", "api-client", "typescript", "unofficial"],
|
|
6
6
|
"license": "MIT",
|