@seamapi/http 0.3.0 → 0.4.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.
@@ -3,6 +3,8 @@ import { AxiosRetry } from 'axios-retry';
3
3
  import { RouteRequestBody, RouteResponse, RouteRequestParams } from '@seamapi/types/connect';
4
4
  import { SetNonNullable } from 'type-fest';
5
5
 
6
+ declare const errorInterceptor: (err: unknown) => Promise<void>;
7
+
6
8
  type Client = AxiosInstance;
7
9
  interface ClientOptions {
8
10
  axiosOptions?: AxiosRequestConfig;
@@ -527,9 +529,35 @@ declare class SeamHttp {
527
529
  get workspaces(): SeamHttpWorkspaces;
528
530
  }
529
531
 
532
+ interface ApiError {
533
+ type: string;
534
+ message: string;
535
+ data?: unknown;
536
+ }
537
+
538
+ declare class SeamHttpApiError extends Error {
539
+ code: string;
540
+ statusCode: number;
541
+ requestId: string;
542
+ data?: unknown;
543
+ constructor(error: ApiError, statusCode: number, requestId: string);
544
+ }
545
+ declare const isSeamHttpApiError: (error: unknown) => error is SeamHttpApiError;
546
+ declare class SeamHttpUnauthorizedError extends SeamHttpApiError {
547
+ code: 'unauthorized';
548
+ statusCode: 401;
549
+ constructor(requestId: string);
550
+ }
551
+ declare const isSeamHttpUnauthorizedError: (error: unknown) => error is SeamHttpUnauthorizedError;
552
+ declare class SeamHttpInvalidInputError extends SeamHttpApiError {
553
+ code: 'invalid_input';
554
+ constructor(error: ApiError, statusCode: number, requestId: string);
555
+ }
556
+ declare const isSeamHttpInvalidInputError: (error: unknown) => error is SeamHttpInvalidInputError;
557
+
530
558
  declare const paramsSerializer: CustomParamsSerializer;
531
559
  declare class UnserializableParamError extends Error {
532
560
  constructor(name: string, message: string);
533
561
  }
534
562
 
535
- export { AccessCodesCreateBody, AccessCodesCreateMultipleBody, AccessCodesCreateMultipleResponse, AccessCodesCreateResponse, AccessCodesDeleteBody, AccessCodesDeleteResponse, AccessCodesGenerateCodeBody, AccessCodesGenerateCodeResponse, AccessCodesGetParams, AccessCodesGetResponse, AccessCodesListParams, AccessCodesListResponse, AccessCodesPullBackupAccessCodeBody, AccessCodesPullBackupAccessCodeResponse, AccessCodesUnmanagedConvertToManagedBody, AccessCodesUnmanagedConvertToManagedResponse, AccessCodesUnmanagedDeleteBody, AccessCodesUnmanagedDeleteResponse, AccessCodesUnmanagedGetParams, AccessCodesUnmanagedGetResponse, AccessCodesUnmanagedListParams, AccessCodesUnmanagedListResponse, AccessCodesUnmanagedUpdateBody, AccessCodesUnmanagedUpdateResponse, AccessCodesUpdateBody, AccessCodesUpdateResponse, AcsAccessGroupsAddUserBody, AcsAccessGroupsAddUserResponse, AcsAccessGroupsCreateBody, AcsAccessGroupsCreateResponse, AcsAccessGroupsDeleteBody, AcsAccessGroupsDeleteResponse, AcsAccessGroupsGetParams, AcsAccessGroupsGetResponse, AcsAccessGroupsListParams, AcsAccessGroupsListResponse, AcsAccessGroupsListUsersParams, AcsAccessGroupsListUsersResponse, AcsAccessGroupsRemoveUserBody, AcsAccessGroupsRemoveUserResponse, AcsAccessGroupsUpdateBody, AcsAccessGroupsUpdateResponse, AcsCredentialsCreateBody, AcsCredentialsCreateResponse, AcsCredentialsDeleteBody, AcsCredentialsDeleteResponse, AcsCredentialsGetParams, AcsCredentialsGetResponse, AcsCredentialsListParams, AcsCredentialsListResponse, AcsSystemsGetParams, AcsSystemsGetResponse, AcsSystemsListParams, AcsSystemsListResponse, AcsUsersAddToAccessGroupBody, AcsUsersAddToAccessGroupResponse, AcsUsersCreateBody, AcsUsersCreateResponse, AcsUsersDeleteBody, AcsUsersDeleteResponse, AcsUsersGetParams, AcsUsersGetResponse, AcsUsersListParams, AcsUsersListResponse, AcsUsersRemoveFromAccessGroupBody, AcsUsersRemoveFromAccessGroupResponse, AcsUsersSuspendBody, AcsUsersSuspendResponse, AcsUsersUnsuspendBody, AcsUsersUnsuspendResponse, AcsUsersUpdateBody, AcsUsersUpdateResponse, ActionAttemptsGetParams, ActionAttemptsGetResponse, ActionAttemptsListParams, ActionAttemptsListResponse, ClientSessionsCreateBody, ClientSessionsCreateResponse, ClientSessionsDeleteBody, ClientSessionsDeleteResponse, ClientSessionsGetOrCreateBody, ClientSessionsGetOrCreateResponse, ClientSessionsGetParams, ClientSessionsGetResponse, ClientSessionsGrantAccessBody, ClientSessionsGrantAccessResponse, ClientSessionsListParams, ClientSessionsListResponse, ConnectWebviewsCreateBody, ConnectWebviewsCreateResponse, ConnectWebviewsDeleteBody, ConnectWebviewsDeleteResponse, ConnectWebviewsGetParams, ConnectWebviewsGetResponse, ConnectWebviewsListParams, ConnectWebviewsListResponse, ConnectWebviewsViewParams, ConnectWebviewsViewResponse, ConnectedAccountsDeleteBody, ConnectedAccountsDeleteResponse, ConnectedAccountsGetParams, ConnectedAccountsGetResponse, ConnectedAccountsListParams, ConnectedAccountsListResponse, DevicesDeleteBody, DevicesDeleteResponse, DevicesGetParams, DevicesGetResponse, DevicesListDeviceProvidersParams, DevicesListDeviceProvidersResponse, DevicesListParams, DevicesListResponse, DevicesUnmanagedGetParams, DevicesUnmanagedGetResponse, DevicesUnmanagedListParams, DevicesUnmanagedListResponse, DevicesUnmanagedUpdateBody, DevicesUnmanagedUpdateResponse, DevicesUpdateBody, DevicesUpdateResponse, EventsGetParams, EventsGetResponse, EventsListParams, EventsListResponse, LocksGetParams, LocksGetResponse, LocksListParams, LocksListResponse, LocksLockDoorBody, LocksLockDoorResponse, LocksUnlockDoorBody, LocksUnlockDoorResponse, NoiseSensorsNoiseThresholdsCreateBody, NoiseSensorsNoiseThresholdsCreateResponse, NoiseSensorsNoiseThresholdsDeleteBody, NoiseSensorsNoiseThresholdsDeleteResponse, NoiseSensorsNoiseThresholdsGetParams, NoiseSensorsNoiseThresholdsGetResponse, NoiseSensorsNoiseThresholdsListParams, NoiseSensorsNoiseThresholdsListResponse, NoiseSensorsNoiseThresholdsUpdateBody, NoiseSensorsNoiseThresholdsUpdateResponse, SeamHttp, SeamHttpAccessCodes, SeamHttpAccessCodesUnmanaged, SeamHttpAcs, SeamHttpAcsAccessGroups, SeamHttpAcsCredentials, SeamHttpAcsSystems, SeamHttpAcsUsers, SeamHttpActionAttempts, SeamHttpClientSessions, SeamHttpConnectWebviews, SeamHttpConnectedAccounts, SeamHttpDevices, SeamHttpDevicesUnmanaged, SeamHttpEvents, SeamHttpFromPublishableKeyOptions, SeamHttpInvalidOptionsError, SeamHttpLocks, SeamHttpNoiseSensors, SeamHttpNoiseSensorsNoiseThresholds, SeamHttpOptions, SeamHttpOptionsFromEnv, SeamHttpOptionsWithApiKey, SeamHttpOptionsWithClient, SeamHttpOptionsWithClientSessionToken, SeamHttpThermostats, SeamHttpThermostatsClimateSettingSchedules, SeamHttpWebhooks, SeamHttpWorkspaces, ThermostatsClimateSettingSchedulesCreateBody, ThermostatsClimateSettingSchedulesCreateResponse, ThermostatsClimateSettingSchedulesDeleteBody, ThermostatsClimateSettingSchedulesDeleteResponse, ThermostatsClimateSettingSchedulesGetParams, ThermostatsClimateSettingSchedulesGetResponse, ThermostatsClimateSettingSchedulesListParams, ThermostatsClimateSettingSchedulesListResponse, ThermostatsClimateSettingSchedulesUpdateBody, ThermostatsClimateSettingSchedulesUpdateResponse, ThermostatsCoolBody, ThermostatsCoolResponse, ThermostatsGetParams, ThermostatsGetResponse, ThermostatsHeatBody, ThermostatsHeatCoolBody, ThermostatsHeatCoolResponse, ThermostatsHeatResponse, ThermostatsListParams, ThermostatsListResponse, ThermostatsOffBody, ThermostatsOffResponse, ThermostatsSetFanModeBody, ThermostatsSetFanModeResponse, ThermostatsUpdateBody, ThermostatsUpdateResponse, UnserializableParamError, WebhooksCreateBody, WebhooksCreateResponse, WebhooksDeleteBody, WebhooksDeleteResponse, WebhooksGetParams, WebhooksGetResponse, WebhooksListParams, WebhooksListResponse, WorkspacesGetParams, WorkspacesGetResponse, WorkspacesListParams, WorkspacesListResponse, WorkspacesResetSandboxBody, WorkspacesResetSandboxResponse, isSeamHttpOptionsWithApiKey, isSeamHttpOptionsWithClient, isSeamHttpOptionsWithClientSessionToken, paramsSerializer };
563
+ export { AccessCodesCreateBody, AccessCodesCreateMultipleBody, AccessCodesCreateMultipleResponse, AccessCodesCreateResponse, AccessCodesDeleteBody, AccessCodesDeleteResponse, AccessCodesGenerateCodeBody, AccessCodesGenerateCodeResponse, AccessCodesGetParams, AccessCodesGetResponse, AccessCodesListParams, AccessCodesListResponse, AccessCodesPullBackupAccessCodeBody, AccessCodesPullBackupAccessCodeResponse, AccessCodesUnmanagedConvertToManagedBody, AccessCodesUnmanagedConvertToManagedResponse, AccessCodesUnmanagedDeleteBody, AccessCodesUnmanagedDeleteResponse, AccessCodesUnmanagedGetParams, AccessCodesUnmanagedGetResponse, AccessCodesUnmanagedListParams, AccessCodesUnmanagedListResponse, AccessCodesUnmanagedUpdateBody, AccessCodesUnmanagedUpdateResponse, AccessCodesUpdateBody, AccessCodesUpdateResponse, AcsAccessGroupsAddUserBody, AcsAccessGroupsAddUserResponse, AcsAccessGroupsCreateBody, AcsAccessGroupsCreateResponse, AcsAccessGroupsDeleteBody, AcsAccessGroupsDeleteResponse, AcsAccessGroupsGetParams, AcsAccessGroupsGetResponse, AcsAccessGroupsListParams, AcsAccessGroupsListResponse, AcsAccessGroupsListUsersParams, AcsAccessGroupsListUsersResponse, AcsAccessGroupsRemoveUserBody, AcsAccessGroupsRemoveUserResponse, AcsAccessGroupsUpdateBody, AcsAccessGroupsUpdateResponse, AcsCredentialsCreateBody, AcsCredentialsCreateResponse, AcsCredentialsDeleteBody, AcsCredentialsDeleteResponse, AcsCredentialsGetParams, AcsCredentialsGetResponse, AcsCredentialsListParams, AcsCredentialsListResponse, AcsSystemsGetParams, AcsSystemsGetResponse, AcsSystemsListParams, AcsSystemsListResponse, AcsUsersAddToAccessGroupBody, AcsUsersAddToAccessGroupResponse, AcsUsersCreateBody, AcsUsersCreateResponse, AcsUsersDeleteBody, AcsUsersDeleteResponse, AcsUsersGetParams, AcsUsersGetResponse, AcsUsersListParams, AcsUsersListResponse, AcsUsersRemoveFromAccessGroupBody, AcsUsersRemoveFromAccessGroupResponse, AcsUsersSuspendBody, AcsUsersSuspendResponse, AcsUsersUnsuspendBody, AcsUsersUnsuspendResponse, AcsUsersUpdateBody, AcsUsersUpdateResponse, ActionAttemptsGetParams, ActionAttemptsGetResponse, ActionAttemptsListParams, ActionAttemptsListResponse, ClientSessionsCreateBody, ClientSessionsCreateResponse, ClientSessionsDeleteBody, ClientSessionsDeleteResponse, ClientSessionsGetOrCreateBody, ClientSessionsGetOrCreateResponse, ClientSessionsGetParams, ClientSessionsGetResponse, ClientSessionsGrantAccessBody, ClientSessionsGrantAccessResponse, ClientSessionsListParams, ClientSessionsListResponse, ConnectWebviewsCreateBody, ConnectWebviewsCreateResponse, ConnectWebviewsDeleteBody, ConnectWebviewsDeleteResponse, ConnectWebviewsGetParams, ConnectWebviewsGetResponse, ConnectWebviewsListParams, ConnectWebviewsListResponse, ConnectWebviewsViewParams, ConnectWebviewsViewResponse, ConnectedAccountsDeleteBody, ConnectedAccountsDeleteResponse, ConnectedAccountsGetParams, ConnectedAccountsGetResponse, ConnectedAccountsListParams, ConnectedAccountsListResponse, DevicesDeleteBody, DevicesDeleteResponse, DevicesGetParams, DevicesGetResponse, DevicesListDeviceProvidersParams, DevicesListDeviceProvidersResponse, DevicesListParams, DevicesListResponse, DevicesUnmanagedGetParams, DevicesUnmanagedGetResponse, DevicesUnmanagedListParams, DevicesUnmanagedListResponse, DevicesUnmanagedUpdateBody, DevicesUnmanagedUpdateResponse, DevicesUpdateBody, DevicesUpdateResponse, EventsGetParams, EventsGetResponse, EventsListParams, EventsListResponse, LocksGetParams, LocksGetResponse, LocksListParams, LocksListResponse, LocksLockDoorBody, LocksLockDoorResponse, LocksUnlockDoorBody, LocksUnlockDoorResponse, NoiseSensorsNoiseThresholdsCreateBody, NoiseSensorsNoiseThresholdsCreateResponse, NoiseSensorsNoiseThresholdsDeleteBody, NoiseSensorsNoiseThresholdsDeleteResponse, NoiseSensorsNoiseThresholdsGetParams, NoiseSensorsNoiseThresholdsGetResponse, NoiseSensorsNoiseThresholdsListParams, NoiseSensorsNoiseThresholdsListResponse, NoiseSensorsNoiseThresholdsUpdateBody, NoiseSensorsNoiseThresholdsUpdateResponse, SeamHttp, SeamHttpAccessCodes, SeamHttpAccessCodesUnmanaged, SeamHttpAcs, SeamHttpAcsAccessGroups, SeamHttpAcsCredentials, SeamHttpAcsSystems, SeamHttpAcsUsers, SeamHttpActionAttempts, SeamHttpApiError, SeamHttpClientSessions, SeamHttpConnectWebviews, SeamHttpConnectedAccounts, SeamHttpDevices, SeamHttpDevicesUnmanaged, SeamHttpEvents, SeamHttpFromPublishableKeyOptions, SeamHttpInvalidInputError, SeamHttpInvalidOptionsError, SeamHttpLocks, SeamHttpNoiseSensors, SeamHttpNoiseSensorsNoiseThresholds, SeamHttpOptions, SeamHttpOptionsFromEnv, SeamHttpOptionsWithApiKey, SeamHttpOptionsWithClient, SeamHttpOptionsWithClientSessionToken, SeamHttpThermostats, SeamHttpThermostatsClimateSettingSchedules, SeamHttpUnauthorizedError, SeamHttpWebhooks, SeamHttpWorkspaces, ThermostatsClimateSettingSchedulesCreateBody, ThermostatsClimateSettingSchedulesCreateResponse, ThermostatsClimateSettingSchedulesDeleteBody, ThermostatsClimateSettingSchedulesDeleteResponse, ThermostatsClimateSettingSchedulesGetParams, ThermostatsClimateSettingSchedulesGetResponse, ThermostatsClimateSettingSchedulesListParams, ThermostatsClimateSettingSchedulesListResponse, ThermostatsClimateSettingSchedulesUpdateBody, ThermostatsClimateSettingSchedulesUpdateResponse, ThermostatsCoolBody, ThermostatsCoolResponse, ThermostatsGetParams, ThermostatsGetResponse, ThermostatsHeatBody, ThermostatsHeatCoolBody, ThermostatsHeatCoolResponse, ThermostatsHeatResponse, ThermostatsListParams, ThermostatsListResponse, ThermostatsOffBody, ThermostatsOffResponse, ThermostatsSetFanModeBody, ThermostatsSetFanModeResponse, ThermostatsUpdateBody, ThermostatsUpdateResponse, UnserializableParamError, WebhooksCreateBody, WebhooksCreateResponse, WebhooksDeleteBody, WebhooksDeleteResponse, WebhooksGetParams, WebhooksGetResponse, WebhooksListParams, WebhooksListResponse, WorkspacesGetParams, WorkspacesGetResponse, WorkspacesListParams, WorkspacesListResponse, WorkspacesResetSandboxBody, WorkspacesResetSandboxResponse, errorInterceptor, isSeamHttpApiError, isSeamHttpInvalidInputError, isSeamHttpOptionsWithApiKey, isSeamHttpOptionsWithClient, isSeamHttpOptionsWithClientSessionToken, isSeamHttpUnauthorizedError, paramsSerializer };
@@ -0,0 +1,8 @@
1
+ export interface ApiErrorResponse {
2
+ error: ApiError;
3
+ }
4
+ export interface ApiError {
5
+ type: string;
6
+ message: string;
7
+ data?: unknown;
8
+ }
@@ -0,0 +1,3 @@
1
+ // UPSTREAM: These types should be provided by @seamapi/types/connect.
2
+ export {};
3
+ //# sourceMappingURL=api-error-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-error-type.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/api-error-type.ts"],"names":[],"mappings":"AAAA,sEAAsE"}
@@ -1,6 +1,9 @@
1
1
  import axios, {} from 'axios';
2
+ // @ts-expect-error https://github.com/svsool/axios-better-stacktrace/issues/12
3
+ import axiosBetterStacktrace from 'axios-better-stacktrace';
2
4
  import axiosRetry, { exponentialDelay } from 'axios-retry';
3
5
  import { paramsSerializer } from '../../../lib/params-serializer.js';
6
+ import { errorInterceptor } from './error-interceptor.js';
4
7
  export const createClient = (options) => {
5
8
  if (options.client != null)
6
9
  return options.client;
@@ -8,12 +11,14 @@ export const createClient = (options) => {
8
11
  paramsSerializer,
9
12
  ...options.axiosOptions,
10
13
  });
14
+ axiosBetterStacktrace(axios);
11
15
  // @ts-expect-error https://github.com/softonic/axios-retry/issues/159
12
16
  axiosRetry(client, {
13
17
  retries: 2,
14
18
  retryDelay: exponentialDelay,
15
19
  ...options.axiosRetryOptions,
16
20
  });
21
+ client.interceptors.response.use(undefined, errorInterceptor);
17
22
  return client;
18
23
  };
19
24
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAA+C,MAAM,OAAO,CAAA;AAC1E,OAAO,UAAU,EAAE,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAY3D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAsB,EAAiB,EAAE;IACpE,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC,MAAM,CAAA;IAEjD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,gBAAgB;QAChB,GAAG,OAAO,CAAC,YAAY;KACxB,CAAC,CAAA;IAEF,sEAAsE;IACtE,UAAU,CAAC,MAAM,EAAE;QACjB,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,gBAAgB;QAC5B,GAAG,OAAO,CAAC,iBAAiB;KAC7B,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAA+C,MAAM,OAAO,CAAA;AAC1E,+EAA+E;AAC/E,OAAO,qBAAqB,MAAM,yBAAyB,CAAA;AAC3D,OAAO,UAAU,EAAE,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAYzD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAsB,EAAiB,EAAE;IACpE,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,OAAO,CAAC,MAAM,CAAA;IAEjD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,gBAAgB;QAChB,GAAG,OAAO,CAAC,YAAY;KACxB,CAAC,CAAA;IAEF,qBAAqB,CAAC,KAAK,CAAC,CAAA;IAE5B,sEAAsE;IACtE,UAAU,CAAC,MAAM,EAAE;QACjB,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,gBAAgB;QAC5B,GAAG,OAAO,CAAC,iBAAiB;KAC7B,CAAC,CAAA;IAEF,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;IAE7D,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export declare const errorInterceptor: (err: unknown) => Promise<void>;
@@ -0,0 +1,43 @@
1
+ import { isAxiosError } from 'axios';
2
+ import { SeamHttpApiError, SeamHttpInvalidInputError, SeamHttpUnauthorizedError, } from './seam-http-error.js';
3
+ export const errorInterceptor = async (err) => {
4
+ if (!isAxiosError(err))
5
+ throw err;
6
+ const { response } = err;
7
+ const status = response?.status;
8
+ const headers = response?.headers;
9
+ const requestId = headers?.['seam-request-id'] ?? '';
10
+ if (status == null)
11
+ throw err;
12
+ if (status === 401) {
13
+ throw new SeamHttpUnauthorizedError(requestId);
14
+ }
15
+ if (!isApiErrorResponse(response))
16
+ throw err;
17
+ const { type } = response.data.error;
18
+ const args = [response.data.error, status, requestId];
19
+ if (type === 'invalid_input')
20
+ throw new SeamHttpInvalidInputError(...args);
21
+ throw new SeamHttpApiError(...args);
22
+ };
23
+ const isApiErrorResponse = (response) => {
24
+ if (response == null)
25
+ return false;
26
+ const { headers, data } = response;
27
+ if (headers == null)
28
+ return false;
29
+ const contentType = headers['content-type'];
30
+ if (typeof contentType === 'string' &&
31
+ !contentType.startsWith('application/json')) {
32
+ return false;
33
+ }
34
+ if (typeof data === 'object' && data != null) {
35
+ return ('error' in data &&
36
+ typeof data.error === 'object' &&
37
+ data.error != null &&
38
+ 'type' in data.error &&
39
+ typeof data.error.type === 'string');
40
+ }
41
+ return false;
42
+ };
43
+ //# sourceMappingURL=error-interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-interceptor.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/error-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,YAAY,EAAE,MAAM,OAAO,CAAA;AAGrD,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAA;AAE7B,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,GAAY,EAAiB,EAAE;IACpE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;QAAE,MAAM,GAAG,CAAA;IAEjC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IACxB,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,CAAA;IAC/B,MAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,CAAA;IACjC,MAAM,SAAS,GAAG,OAAO,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAA;IAEpD,IAAI,MAAM,IAAI,IAAI;QAAE,MAAM,GAAG,CAAA;IAE7B,IAAI,MAAM,KAAK,GAAG,EAAE;QAClB,MAAM,IAAI,yBAAyB,CAAC,SAAS,CAAC,CAAA;KAC/C;IAED,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QAAE,MAAM,GAAG,CAAA;IAE5C,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAA;IAEpC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAU,CAAA;IAE9D,IAAI,IAAI,KAAK,eAAe;QAAE,MAAM,IAAI,yBAAyB,CAAC,GAAG,IAAI,CAAC,CAAA;IAC1E,MAAM,IAAI,gBAAgB,CAAC,GAAG,IAAI,CAAC,CAAA;AACrC,CAAC,CAAA;AAED,MAAM,kBAAkB,GAAG,CACzB,QAAgC,EACmC,EAAE;IACrE,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IAClC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAA;IAElC,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IAEjC,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IAC3C,IACE,OAAO,WAAW,KAAK,QAAQ;QAC/B,CAAC,WAAW,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAC3C;QACA,OAAO,KAAK,CAAA;KACb;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,IAAI,EAAE;QAC5C,OAAO,CACL,OAAO,IAAI,IAAI;YACf,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;YAC9B,IAAI,CAAC,KAAK,IAAI,IAAI;YAClB,MAAM,IAAI,IAAI,CAAC,KAAK;YACpB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CACpC,CAAA;KACF;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
@@ -1,4 +1,6 @@
1
+ export * from './error-interceptor.js';
1
2
  export * from './options.js';
2
- export * from './routes//index.js';
3
+ export * from './routes/index.js';
3
4
  export * from './seam-http.js';
5
+ export * from './seam-http-error.js';
4
6
  export * from '../../../lib/params-serializer.js';
@@ -1,5 +1,7 @@
1
+ export * from './error-interceptor.js';
1
2
  export * from './options.js';
2
- export * from './routes//index.js';
3
+ export * from './routes/index.js';
3
4
  export * from './seam-http.js';
5
+ export * from './seam-http-error.js';
4
6
  export * from '../../../lib/params-serializer.js';
5
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { ApiError } from './api-error-type.js';
2
+ export declare class SeamHttpApiError extends Error {
3
+ code: string;
4
+ statusCode: number;
5
+ requestId: string;
6
+ data?: unknown;
7
+ constructor(error: ApiError, statusCode: number, requestId: string);
8
+ }
9
+ export declare const isSeamHttpApiError: (error: unknown) => error is SeamHttpApiError;
10
+ export declare class SeamHttpUnauthorizedError extends SeamHttpApiError {
11
+ code: 'unauthorized';
12
+ statusCode: 401;
13
+ constructor(requestId: string);
14
+ }
15
+ export declare const isSeamHttpUnauthorizedError: (error: unknown) => error is SeamHttpUnauthorizedError;
16
+ export declare class SeamHttpInvalidInputError extends SeamHttpApiError {
17
+ code: 'invalid_input';
18
+ constructor(error: ApiError, statusCode: number, requestId: string);
19
+ }
20
+ export declare const isSeamHttpInvalidInputError: (error: unknown) => error is SeamHttpInvalidInputError;
@@ -0,0 +1,43 @@
1
+ export class SeamHttpApiError extends Error {
2
+ constructor(error, statusCode, requestId) {
3
+ const { type, message, data } = error;
4
+ super(message);
5
+ this.name = this.constructor.name;
6
+ Error.captureStackTrace(this, this.constructor);
7
+ this.code = type;
8
+ this.statusCode = statusCode;
9
+ this.requestId = requestId;
10
+ if (data != null)
11
+ this.data = data;
12
+ }
13
+ }
14
+ export const isSeamHttpApiError = (error) => {
15
+ return error instanceof SeamHttpApiError;
16
+ };
17
+ export class SeamHttpUnauthorizedError extends SeamHttpApiError {
18
+ constructor(requestId) {
19
+ const type = 'unauthorized';
20
+ const status = 401;
21
+ super({ type, message: 'Unauthorized' }, status, requestId);
22
+ this.name = this.constructor.name;
23
+ Error.captureStackTrace(this, this.constructor);
24
+ this.code = type;
25
+ this.statusCode = status;
26
+ this.requestId = requestId;
27
+ }
28
+ }
29
+ export const isSeamHttpUnauthorizedError = (error) => {
30
+ return error instanceof SeamHttpUnauthorizedError;
31
+ };
32
+ export class SeamHttpInvalidInputError extends SeamHttpApiError {
33
+ constructor(error, statusCode, requestId) {
34
+ super(error, statusCode, requestId);
35
+ this.name = this.constructor.name;
36
+ Error.captureStackTrace(this, this.constructor);
37
+ this.code = 'invalid_input';
38
+ }
39
+ }
40
+ export const isSeamHttpInvalidInputError = (error) => {
41
+ return error instanceof SeamHttpInvalidInputError;
42
+ };
43
+ //# sourceMappingURL=seam-http-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seam-http-error.js","sourceRoot":"","sources":["../../../src/lib/seam/connect/seam-http-error.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAMzC,YAAY,KAAe,EAAE,UAAkB,EAAE,SAAiB;QAChE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;QACrC,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QACjC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,IAAI,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IACpC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAc,EACa,EAAE;IAC7B,OAAO,KAAK,YAAY,gBAAgB,CAAA;AAC1C,CAAC,CAAA;AAED,MAAM,OAAO,yBAA0B,SAAQ,gBAAgB;IAI7D,YAAY,SAAiB;QAC3B,MAAM,IAAI,GAAG,cAAc,CAAA;QAC3B,MAAM,MAAM,GAAG,GAAG,CAAA;QAClB,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;QAC3D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QACjC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAA;QACxB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,KAAc,EACsB,EAAE;IACtC,OAAO,KAAK,YAAY,yBAAyB,CAAA;AACnD,CAAC,CAAA;AAED,MAAM,OAAO,yBAA0B,SAAQ,gBAAgB;IAG7D,YAAY,KAAe,EAAE,UAAkB,EAAE,SAAiB;QAChE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QACjC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;IAC7B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,KAAc,EACsB,EAAE;IACtC,OAAO,KAAK,YAAY,yBAAyB,CAAA;AACnD,CAAC,CAAA"}
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const seamapiJavascriptHttpVersion = "0.3.0";
1
+ declare const seamapiJavascriptHttpVersion = "0.4.0";
2
2
  export default seamapiJavascriptHttpVersion;
package/lib/version.js CHANGED
@@ -1,3 +1,3 @@
1
- const seamapiJavascriptHttpVersion = '0.3.0';
1
+ const seamapiJavascriptHttpVersion = '0.4.0';
2
2
  export default seamapiJavascriptHttpVersion;
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamapi/http",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "JavaScript HTTP client for the Seam API written in TypeScript.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -87,10 +87,11 @@
87
87
  },
88
88
  "dependencies": {
89
89
  "axios": "^1.5.0",
90
+ "axios-better-stacktrace": "^2.1.5",
90
91
  "axios-retry": "^3.8.0"
91
92
  },
92
93
  "devDependencies": {
93
- "@seamapi/fake-seam-connect": "1.24.0",
94
+ "@seamapi/fake-seam-connect": "^1.43.0",
94
95
  "@seamapi/types": "^1.24.0",
95
96
  "@types/eslint": "^8.44.2",
96
97
  "@types/node": "^18.11.18",
@@ -0,0 +1,11 @@
1
+ // UPSTREAM: These types should be provided by @seamapi/types/connect.
2
+
3
+ export interface ApiErrorResponse {
4
+ error: ApiError
5
+ }
6
+
7
+ export interface ApiError {
8
+ type: string
9
+ message: string
10
+ data?: unknown
11
+ }
@@ -1,8 +1,12 @@
1
1
  import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
2
+ // @ts-expect-error https://github.com/svsool/axios-better-stacktrace/issues/12
3
+ import axiosBetterStacktrace from 'axios-better-stacktrace'
2
4
  import axiosRetry, { type AxiosRetry, exponentialDelay } from 'axios-retry'
3
5
 
4
6
  import { paramsSerializer } from 'lib/params-serializer.js'
5
7
 
8
+ import { errorInterceptor } from './error-interceptor.js'
9
+
6
10
  export type Client = AxiosInstance
7
11
 
8
12
  export interface ClientOptions {
@@ -21,6 +25,8 @@ export const createClient = (options: ClientOptions): AxiosInstance => {
21
25
  ...options.axiosOptions,
22
26
  })
23
27
 
28
+ axiosBetterStacktrace(axios)
29
+
24
30
  // @ts-expect-error https://github.com/softonic/axios-retry/issues/159
25
31
  axiosRetry(client, {
26
32
  retries: 2,
@@ -28,5 +34,7 @@ export const createClient = (options: ClientOptions): AxiosInstance => {
28
34
  ...options.axiosRetryOptions,
29
35
  })
30
36
 
37
+ client.interceptors.response.use(undefined, errorInterceptor)
38
+
31
39
  return client
32
40
  }
@@ -0,0 +1,61 @@
1
+ import { type AxiosError, isAxiosError } from 'axios'
2
+
3
+ import type { ApiErrorResponse } from './api-error-type.js'
4
+ import {
5
+ SeamHttpApiError,
6
+ SeamHttpInvalidInputError,
7
+ SeamHttpUnauthorizedError,
8
+ } from './seam-http-error.js'
9
+
10
+ export const errorInterceptor = async (err: unknown): Promise<void> => {
11
+ if (!isAxiosError(err)) throw err
12
+
13
+ const { response } = err
14
+ const status = response?.status
15
+ const headers = response?.headers
16
+ const requestId = headers?.['seam-request-id'] ?? ''
17
+
18
+ if (status == null) throw err
19
+
20
+ if (status === 401) {
21
+ throw new SeamHttpUnauthorizedError(requestId)
22
+ }
23
+
24
+ if (!isApiErrorResponse(response)) throw err
25
+
26
+ const { type } = response.data.error
27
+
28
+ const args = [response.data.error, status, requestId] as const
29
+
30
+ if (type === 'invalid_input') throw new SeamHttpInvalidInputError(...args)
31
+ throw new SeamHttpApiError(...args)
32
+ }
33
+
34
+ const isApiErrorResponse = (
35
+ response: AxiosError['response'],
36
+ ): response is NonNullable<AxiosError<ApiErrorResponse>['response']> => {
37
+ if (response == null) return false
38
+ const { headers, data } = response
39
+
40
+ if (headers == null) return false
41
+
42
+ const contentType = headers['content-type']
43
+ if (
44
+ typeof contentType === 'string' &&
45
+ !contentType.startsWith('application/json')
46
+ ) {
47
+ return false
48
+ }
49
+
50
+ if (typeof data === 'object' && data != null) {
51
+ return (
52
+ 'error' in data &&
53
+ typeof data.error === 'object' &&
54
+ data.error != null &&
55
+ 'type' in data.error &&
56
+ typeof data.error.type === 'string'
57
+ )
58
+ }
59
+
60
+ return false
61
+ }
@@ -1,4 +1,6 @@
1
+ export * from './error-interceptor.js'
1
2
  export * from './options.js'
2
- export * from './routes//index.js'
3
+ export * from './routes/index.js'
3
4
  export * from './seam-http.js'
5
+ export * from './seam-http-error.js'
4
6
  export * from 'lib/params-serializer.js'
@@ -0,0 +1,64 @@
1
+ import type { ApiError } from './api-error-type.js'
2
+
3
+ export class SeamHttpApiError extends Error {
4
+ code: string
5
+ statusCode: number
6
+ requestId: string
7
+ data?: unknown
8
+
9
+ constructor(error: ApiError, statusCode: number, requestId: string) {
10
+ const { type, message, data } = error
11
+ super(message)
12
+ this.name = this.constructor.name
13
+ Error.captureStackTrace(this, this.constructor)
14
+ this.code = type
15
+ this.statusCode = statusCode
16
+ this.requestId = requestId
17
+ if (data != null) this.data = data
18
+ }
19
+ }
20
+
21
+ export const isSeamHttpApiError = (
22
+ error: unknown,
23
+ ): error is SeamHttpApiError => {
24
+ return error instanceof SeamHttpApiError
25
+ }
26
+
27
+ export class SeamHttpUnauthorizedError extends SeamHttpApiError {
28
+ override code: 'unauthorized'
29
+ override statusCode: 401
30
+
31
+ constructor(requestId: string) {
32
+ const type = 'unauthorized'
33
+ const status = 401
34
+ super({ type, message: 'Unauthorized' }, status, requestId)
35
+ this.name = this.constructor.name
36
+ Error.captureStackTrace(this, this.constructor)
37
+ this.code = type
38
+ this.statusCode = status
39
+ this.requestId = requestId
40
+ }
41
+ }
42
+
43
+ export const isSeamHttpUnauthorizedError = (
44
+ error: unknown,
45
+ ): error is SeamHttpUnauthorizedError => {
46
+ return error instanceof SeamHttpUnauthorizedError
47
+ }
48
+
49
+ export class SeamHttpInvalidInputError extends SeamHttpApiError {
50
+ override code: 'invalid_input'
51
+
52
+ constructor(error: ApiError, statusCode: number, requestId: string) {
53
+ super(error, statusCode, requestId)
54
+ this.name = this.constructor.name
55
+ Error.captureStackTrace(this, this.constructor)
56
+ this.code = 'invalid_input'
57
+ }
58
+ }
59
+
60
+ export const isSeamHttpInvalidInputError = (
61
+ error: unknown,
62
+ ): error is SeamHttpInvalidInputError => {
63
+ return error instanceof SeamHttpInvalidInputError
64
+ }
@@ -1,3 +1,3 @@
1
- const seamapiJavascriptHttpVersion = '0.3.0'
1
+ const seamapiJavascriptHttpVersion = '0.4.0'
2
2
 
3
3
  export default seamapiJavascriptHttpVersion