@unofficialbox/box-open-sdk 0.5.0 → 0.6.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.
@@ -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
+ }
@@ -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`, {
@@ -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
+ }
@@ -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`, {
@@ -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,9 +4,10 @@
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`). It carries the HTTP `status`
8
- when the failure came with a response, and the underlying `cause` for
9
- transport or decode failures:
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';
@@ -14,7 +15,7 @@ try {
14
15
  await client.files.getFileById(fileId);
15
16
  } catch (err) {
16
17
  if (err instanceof runtime.BoxApiError) {
17
- // err.status (number | undefined), err.cause
18
+ // err.status, err.code, err.boxMessage, err.requestId, err.body, err.cause
18
19
  }
19
20
  }
20
21
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unofficialbox/box-open-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
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",