@ikatec/digisac-api-sdk 2.1.1 → 2.1.3

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.
@@ -39,23 +39,26 @@ var BaseApiClient = class {
39
39
  }
40
40
  async handleResponse(response) {
41
41
  if (!response.ok) {
42
- const contentType = response.headers.get("Content-Type");
43
- const isJson = contentType && contentType.includes("application/json");
42
+ const isJson = response.headers.get("Content-Type")?.includes("application/json") ?? false;
44
43
  let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`;
45
44
  let errorClass;
46
45
  let validationErrors = null;
47
46
  if (isJson) try {
48
- const errorData = await response.json();
49
- errorMessage = errorData.message || errorData.error || errorMessage;
50
- errorClass = errorData.error;
51
- validationErrors = errorData.validationErrors ?? null;
47
+ const errorText = await response.text();
48
+ if (errorText) {
49
+ const errorData = JSON.parse(errorText);
50
+ errorMessage = errorData.message || errorData.error || errorMessage;
51
+ errorClass = errorData.error;
52
+ validationErrors = errorData.validationErrors ?? null;
53
+ }
52
54
  } catch {}
53
55
  throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
54
56
  }
55
- const contentType = response.headers.get("Content-Type");
56
- if (contentType && contentType.includes("application/json")) return await response.json();
57
+ if (response.status === 204) return;
57
58
  const text = await response.text();
58
- if (text) try {
59
+ if (!text) return;
60
+ if (response.headers.get("Content-Type")?.includes("application/json") ?? false) return JSON.parse(text);
61
+ try {
59
62
  return JSON.parse(text);
60
63
  } catch {
61
64
  return text;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType && contentType.includes('application/json')\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorData = await response.json()\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? null\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType && contentType.includes('application/json')\n\n if (isJson) {\n return (await response.json()) as TResponse\n }\n\n const text = await response.text()\n if (text) {\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n return undefined as unknown as TResponse\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;AAQA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,SACA,YACA,QACA,mBAA6C,MAC7C;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,mBAAmB;;;AAI5B,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAE1D,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD,CAAC;AAEF,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;GACxD,MAAM,SAAS,eAAe,YAAY,SAAS,mBAAmB;GAEtE,IAAI,eAAe,cAAc,SAAS,OAAO,IAAI,SAAS;GAC9D,IAAI;GACJ,IAAI,mBAA6C;AAEjD,OAAI,OACF,KAAI;IACF,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,mBAAe,UAAU,WAAW,UAAU,SAAS;AACvD,iBAAa,UAAU;AACvB,uBAAmB,UAAU,oBAAoB;WAC3C;AAKV,SAAM,IAAI,SAAS,cAAc,YAAY,SAAS,QAAQ,iBAAiB;;EAGjF,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;AAGxD,MAFe,eAAe,YAAY,SAAS,mBAAmB,CAGpE,QAAQ,MAAM,SAAS,MAAM;EAG/B,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,KACF,KAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAOb,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
1
+ {"version":3,"file":"BaseApiClient.cjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? null\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";;;AAQA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,SACA,YACA,QACA,mBAA6C,MAC7C;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,mBAAmB;;;AAI5B,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAE1D,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD,CAAC;AAEF,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,IAAI;GAEhB,MAAM,SADc,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI;GAE5D,IAAI,eAAe,cAAc,SAAS,OAAO,IAAI,SAAS;GAC9D,IAAI;GACJ,IAAI,mBAA6C;AAEjD,OAAI,OACF,KAAI;IACF,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,QAAI,WAAW;KACb,MAAM,YAAY,KAAK,MAAM,UAAU;AACvC,oBAAe,UAAU,WAAW,UAAU,SAAS;AACvD,kBAAa,UAAU;AACvB,wBAAmB,UAAU,oBAAoB;;WAE7C;AAKV,SAAM,IAAI,SAAS,cAAc,YAAY,SAAS,QAAQ,iBAAiB;;AAIjF,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/C,SAAgB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,SAAgB,gBAAgB,EAAE,eAAe,EAAE,GAAG,IAAI,CAAA;gBAGxD,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,GAAE,eAAe,EAAE,GAAG,IAAW;CAQpD;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE/B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAKjD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAgBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA0CjF,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
1
+ {"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/C,SAAgB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C,SAAgB,gBAAgB,EAAE,eAAe,EAAE,GAAG,IAAI,CAAA;gBAGxD,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,GAAE,eAAe,EAAE,GAAG,IAAW;CAQpD;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAClC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAO;gBAE/B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;IAKjD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKnC,OAAO,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EAChD,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;cAgBL,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAuDjF,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAIhG,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,GAAG,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,KAAK,CAAC,SAAS,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;IAIrB,MAAM,CAAC,SAAS,GAAG,OAAO,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC;CAGtB"}
@@ -37,23 +37,26 @@ var BaseApiClient = class {
37
37
  }
38
38
  async handleResponse(response) {
39
39
  if (!response.ok) {
40
- const contentType = response.headers.get("Content-Type");
41
- const isJson = contentType && contentType.includes("application/json");
40
+ const isJson = response.headers.get("Content-Type")?.includes("application/json") ?? false;
42
41
  let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`;
43
42
  let errorClass;
44
43
  let validationErrors = null;
45
44
  if (isJson) try {
46
- const errorData = await response.json();
47
- errorMessage = errorData.message || errorData.error || errorMessage;
48
- errorClass = errorData.error;
49
- validationErrors = errorData.validationErrors ?? null;
45
+ const errorText = await response.text();
46
+ if (errorText) {
47
+ const errorData = JSON.parse(errorText);
48
+ errorMessage = errorData.message || errorData.error || errorMessage;
49
+ errorClass = errorData.error;
50
+ validationErrors = errorData.validationErrors ?? null;
51
+ }
50
52
  } catch {}
51
53
  throw new ApiError(errorMessage, errorClass, response.status, validationErrors);
52
54
  }
53
- const contentType = response.headers.get("Content-Type");
54
- if (contentType && contentType.includes("application/json")) return await response.json();
55
+ if (response.status === 204) return;
55
56
  const text = await response.text();
56
- if (text) try {
57
+ if (!text) return;
58
+ if (response.headers.get("Content-Type")?.includes("application/json") ?? false) return JSON.parse(text);
59
+ try {
57
60
  return JSON.parse(text);
58
61
  } catch {
59
62
  return text;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType && contentType.includes('application/json')\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorData = await response.json()\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? null\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType && contentType.includes('application/json')\n\n if (isJson) {\n return (await response.json()) as TResponse\n }\n\n const text = await response.text()\n if (text) {\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n return undefined as unknown as TResponse\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";AAQA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,SACA,YACA,QACA,mBAA6C,MAC7C;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,mBAAmB;;;AAI5B,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAE1D,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD,CAAC;AAEF,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;GACxD,MAAM,SAAS,eAAe,YAAY,SAAS,mBAAmB;GAEtE,IAAI,eAAe,cAAc,SAAS,OAAO,IAAI,SAAS;GAC9D,IAAI;GACJ,IAAI,mBAA6C;AAEjD,OAAI,OACF,KAAI;IACF,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,mBAAe,UAAU,WAAW,UAAU,SAAS;AACvD,iBAAa,UAAU;AACvB,uBAAmB,UAAU,oBAAoB;WAC3C;AAKV,SAAM,IAAI,SAAS,cAAc,YAAY,SAAS,QAAQ,iBAAiB;;EAGjF,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe;AAGxD,MAFe,eAAe,YAAY,SAAS,mBAAmB,CAGpE,QAAQ,MAAM,SAAS,MAAM;EAG/B,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,KACF,KAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAOb,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
1
+ {"version":3,"file":"BaseApiClient.mjs","names":[],"sources":["../../src/core/BaseApiClient.ts"],"sourcesContent":["import type { ApiClient, HttpMethod } from './ApiClient'\n\nexport interface ValidationError {\n path: string\n message: string\n code: string\n}\n\nexport class ApiError extends Error {\n public readonly errorClass?: string | undefined\n public readonly status?: number | undefined\n public readonly validationErrors: ValidationError[] | null\n\n constructor(\n message: string,\n errorClass?: string,\n status?: number,\n validationErrors: ValidationError[] | null = null,\n ) {\n super(message)\n this.name = 'ApiError'\n this.errorClass = errorClass\n this.status = status\n this.validationErrors = validationErrors\n }\n}\n\nexport class BaseApiClient implements ApiClient {\n protected readonly baseUrl: string\n protected accessToken: string | null = null\n\n constructor(baseUrl: string, accessToken?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '')\n this.accessToken = accessToken || null\n }\n\n setAccessToken(accessToken: string): this {\n this.accessToken = accessToken\n return this\n }\n\n async request<TResponse = unknown, TBody = unknown>(\n method: HttpMethod,\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n const url = `${this.baseUrl}/${endpoint.replace(/^\\//, '')}`\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),\n ...headers,\n },\n ...(body !== undefined && { body: JSON.stringify(body) }),\n })\n\n return this.handleResponse<TResponse>(response)\n }\n\n protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {\n if (!response.ok) {\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`\n let errorClass: string | undefined\n let validationErrors: ValidationError[] | null = null\n\n if (isJson) {\n try {\n const errorText = await response.text()\n if (errorText) {\n const errorData = JSON.parse(errorText)\n errorMessage = errorData.message || errorData.error || errorMessage\n errorClass = errorData.error\n validationErrors = errorData.validationErrors ?? null\n }\n } catch {\n // If JSON parsing fails, use the default error message\n }\n }\n\n throw new ApiError(errorMessage, errorClass, response.status, validationErrors)\n }\n\n // 204 No Content never has a body; short-circuit before reading the stream.\n if (response.status === 204) {\n return undefined as unknown as TResponse\n }\n\n // Read body as text once and treat an empty body as \"no content\" regardless of\n // Content-Type. Servers sometimes return 200 with Content-Type: application/json\n // and an empty body (e.g. POST endpoints whose framework keeps the default JSON\n // content-type even when nothing is written). `response.json()` would throw\n // \"Unexpected end of input\" in that case.\n const text = await response.text()\n if (!text) {\n return undefined as unknown as TResponse\n }\n\n const contentType = response.headers.get('Content-Type')\n const isJson = contentType?.includes('application/json') ?? false\n\n if (isJson) {\n return JSON.parse(text) as TResponse\n }\n\n try {\n return JSON.parse(text) as TResponse\n } catch {\n return text as unknown as TResponse\n }\n }\n\n get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {\n return this.request<TResponse>('GET', endpoint, undefined, headers)\n }\n\n post<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('POST', endpoint, body, headers)\n }\n\n put<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PUT', endpoint, body, headers)\n }\n\n patch<TResponse = unknown, TBody = unknown>(\n endpoint: string,\n body?: TBody,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse, TBody>('PATCH', endpoint, body, headers)\n }\n\n delete<TResponse = unknown>(\n endpoint: string,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.request<TResponse>('DELETE', endpoint, undefined, headers)\n }\n}\n"],"mappings":";AAQA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,YACE,SACA,YACA,QACA,mBAA6C,MAC7C;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,mBAAmB;;;AAI5B,IAAa,gBAAb,MAAgD;CAC9C,AAAmB;CACnB,AAAU,cAA6B;CAEvC,YAAY,SAAiB,aAAsB;AACjD,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;AACzC,OAAK,cAAc,eAAe;;CAGpC,eAAe,aAA2B;AACxC,OAAK,cAAc;AACnB,SAAO;;CAGT,MAAM,QACJ,QACA,UACA,MACA,SACoB;EACpB,MAAM,MAAM,GAAG,KAAK,QAAQ,GAAG,SAAS,QAAQ,OAAO,GAAG;EAE1D,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC;GACA,SAAS;IACP,gBAAgB;IAChB,GAAI,KAAK,eAAe,EAAE,eAAe,UAAU,KAAK,eAAe;IACvE,GAAG;IACJ;GACD,GAAI,SAAS,UAAa,EAAE,MAAM,KAAK,UAAU,KAAK,EAAE;GACzD,CAAC;AAEF,SAAO,KAAK,eAA0B,SAAS;;CAGjD,MAAgB,eAA0B,UAAwC;AAChF,MAAI,CAAC,SAAS,IAAI;GAEhB,MAAM,SADc,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI;GAE5D,IAAI,eAAe,cAAc,SAAS,OAAO,IAAI,SAAS;GAC9D,IAAI;GACJ,IAAI,mBAA6C;AAEjD,OAAI,OACF,KAAI;IACF,MAAM,YAAY,MAAM,SAAS,MAAM;AACvC,QAAI,WAAW;KACb,MAAM,YAAY,KAAK,MAAM,UAAU;AACvC,oBAAe,UAAU,WAAW,UAAU,SAAS;AACvD,kBAAa,UAAU;AACvB,wBAAmB,UAAU,oBAAoB;;WAE7C;AAKV,SAAM,IAAI,SAAS,cAAc,YAAY,SAAS,QAAQ,iBAAiB;;AAIjF,MAAI,SAAS,WAAW,IACtB;EAQF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,MAAI,CAAC,KACH;AAMF,MAHoB,SAAS,QAAQ,IAAI,eAAe,EAC5B,SAAS,mBAAmB,IAAI,MAG1D,QAAO,KAAK,MAAM,KAAK;AAGzB,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;CAIX,IAAyB,UAAkB,SAAsD;AAC/F,SAAO,KAAK,QAAmB,OAAO,UAAU,QAAW,QAAQ;;CAGrE,KACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,QAAQ,UAAU,MAAM,QAAQ;;CAGxE,IACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,OAAO,UAAU,MAAM,QAAQ;;CAGvE,MACE,UACA,MACA,SACoB;AACpB,SAAO,KAAK,QAA0B,SAAS,UAAU,MAAM,QAAQ;;CAGzE,OACE,UACA,SACoB;AACpB,SAAO,KAAK,QAAmB,UAAU,UAAU,QAAW,QAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikatec/digisac-api-sdk",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -127,6 +127,33 @@ describe('BaseApiClient', () => {
127
127
  expect(result).toBeUndefined()
128
128
  })
129
129
 
130
+ it('returns undefined for empty body even when Content-Type is application/json', async () => {
131
+ // Regression: some endpoints (e.g. POST /me/takeover) reply 200 with the default
132
+ // application/json Content-Type and an empty body. `response.json()` would throw
133
+ // "SyntaxError: Unexpected end of input".
134
+ mockFetch({
135
+ ok: true,
136
+ status: 200,
137
+ statusText: 'OK',
138
+ headers: { 'Content-Type': 'application/json' },
139
+ body: '',
140
+ })
141
+ const result = await client.post('/me/takeover')
142
+ expect(result).toBeUndefined()
143
+ })
144
+
145
+ it('returns undefined for 204 No Content regardless of Content-Type', async () => {
146
+ mockFetch({
147
+ ok: true,
148
+ status: 204,
149
+ statusText: 'No Content',
150
+ headers: { 'Content-Type': 'application/json' },
151
+ body: '',
152
+ })
153
+ const result = await client.post('/me/takeover')
154
+ expect(result).toBeUndefined()
155
+ })
156
+
130
157
  it('returns plain text when response is not JSON', async () => {
131
158
  mockFetch({ ok: true, status: 200, statusText: 'OK', body: 'OK' })
132
159
  const result = await client.post<string>('/action')
@@ -220,6 +247,20 @@ describe('BaseApiClient', () => {
220
247
  })
221
248
  })
222
249
 
250
+ it('does not throw when error response has Content-Type application/json but empty body', async () => {
251
+ mockFetch({
252
+ ok: false,
253
+ status: 502,
254
+ statusText: 'Bad Gateway',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: '',
257
+ })
258
+ await expect(client.get('/fail')).rejects.toMatchObject({
259
+ message: 'HTTP Error 502: Bad Gateway',
260
+ status: 502,
261
+ })
262
+ })
263
+
223
264
  it('uses error field as message when message is absent', async () => {
224
265
  mockFetch({
225
266
  ok: false,
@@ -63,7 +63,7 @@ export class BaseApiClient implements ApiClient {
63
63
  protected async handleResponse<TResponse>(response: Response): Promise<TResponse> {
64
64
  if (!response.ok) {
65
65
  const contentType = response.headers.get('Content-Type')
66
- const isJson = contentType && contentType.includes('application/json')
66
+ const isJson = contentType?.includes('application/json') ?? false
67
67
 
68
68
  let errorMessage = `HTTP Error ${response.status}: ${response.statusText}`
69
69
  let errorClass: string | undefined
@@ -71,10 +71,13 @@ export class BaseApiClient implements ApiClient {
71
71
 
72
72
  if (isJson) {
73
73
  try {
74
- const errorData = await response.json()
75
- errorMessage = errorData.message || errorData.error || errorMessage
76
- errorClass = errorData.error
77
- validationErrors = errorData.validationErrors ?? null
74
+ const errorText = await response.text()
75
+ if (errorText) {
76
+ const errorData = JSON.parse(errorText)
77
+ errorMessage = errorData.message || errorData.error || errorMessage
78
+ errorClass = errorData.error
79
+ validationErrors = errorData.validationErrors ?? null
80
+ }
78
81
  } catch {
79
82
  // If JSON parsing fails, use the default error message
80
83
  }
@@ -83,23 +86,33 @@ export class BaseApiClient implements ApiClient {
83
86
  throw new ApiError(errorMessage, errorClass, response.status, validationErrors)
84
87
  }
85
88
 
89
+ // 204 No Content never has a body; short-circuit before reading the stream.
90
+ if (response.status === 204) {
91
+ return undefined as unknown as TResponse
92
+ }
93
+
94
+ // Read body as text once and treat an empty body as "no content" regardless of
95
+ // Content-Type. Servers sometimes return 200 with Content-Type: application/json
96
+ // and an empty body (e.g. POST endpoints whose framework keeps the default JSON
97
+ // content-type even when nothing is written). `response.json()` would throw
98
+ // "Unexpected end of input" in that case.
99
+ const text = await response.text()
100
+ if (!text) {
101
+ return undefined as unknown as TResponse
102
+ }
103
+
86
104
  const contentType = response.headers.get('Content-Type')
87
- const isJson = contentType && contentType.includes('application/json')
105
+ const isJson = contentType?.includes('application/json') ?? false
88
106
 
89
107
  if (isJson) {
90
- return (await response.json()) as TResponse
108
+ return JSON.parse(text) as TResponse
91
109
  }
92
110
 
93
- const text = await response.text()
94
- if (text) {
95
- try {
96
- return JSON.parse(text) as TResponse
97
- } catch {
98
- return text as unknown as TResponse
99
- }
111
+ try {
112
+ return JSON.parse(text) as TResponse
113
+ } catch {
114
+ return text as unknown as TResponse
100
115
  }
101
-
102
- return undefined as unknown as TResponse
103
116
  }
104
117
 
105
118
  get<TResponse = unknown>(endpoint: string, headers?: Record<string, string>): Promise<TResponse> {