@commercelayer/sdk 6.0.0-alfa.4 → 6.0.0-alfa.5

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/lib/esm/error.js CHANGED
@@ -1,4 +1,4 @@
1
- import axios from 'axios';
1
+ import { FetchError } from './fetch';
2
2
  var ErrorType;
3
3
  (function (ErrorType) {
4
4
  ErrorType["CLIENT"] = "client";
@@ -6,7 +6,7 @@ var ErrorType;
6
6
  ErrorType["RESPONSE"] = "response";
7
7
  ErrorType["CANCEL"] = "cancel";
8
8
  ErrorType["PARSE"] = "parse";
9
- ErrorType["GENERIC"] = "generic";
9
+ ErrorType["TIMEOUT"] = "timeout"; // Timeout error
10
10
  })(ErrorType || (ErrorType = {}));
11
11
  class SdkError extends Error {
12
12
  static NAME = 'SdkError';
@@ -16,11 +16,10 @@ class SdkError extends Error {
16
16
  type;
17
17
  code;
18
18
  source;
19
- request;
20
19
  constructor(error) {
21
20
  super(error.message);
22
- this.name = SdkError.NAME; // this.constructor.name
23
- this.type = error.type || ErrorType.GENERIC;
21
+ this.name = SdkError.NAME;
22
+ this.type = error.type || ErrorType.CLIENT;
24
23
  }
25
24
  }
26
25
  class ApiError extends SdkError {
@@ -33,42 +32,52 @@ class ApiError extends SdkError {
33
32
  statusText;
34
33
  constructor(error) {
35
34
  super({ ...error, type: ErrorType.RESPONSE });
36
- this.name = ApiError.NAME; // this.constructor.name
35
+ this.name = ApiError.NAME;
37
36
  }
38
37
  first() {
39
38
  return (this.errors?.length > 0) ? this.errors[0] : undefined;
40
39
  }
41
40
  }
41
+ const isRequestError = (error) => {
42
+ return error instanceof TypeError;
43
+ };
44
+ const isCancelError = (error) => {
45
+ return (error instanceof DOMException) && (error.name === 'AbortError');
46
+ };
47
+ const isTimeoutError = (error) => {
48
+ return (error instanceof DOMException) && (error.name === 'TimeoutError');
49
+ };
42
50
  const handleError = (error) => {
43
51
  let sdkError = new SdkError({ message: error.message });
44
- if (axios.isAxiosError(error)) {
45
- if (error.response) {
46
- // The request was made and the server responded with a status code that falls out of the range of 2xx
47
- const apiError = new ApiError(sdkError);
48
- apiError.type = ErrorType.RESPONSE;
49
- apiError.status = error.response.status;
50
- apiError.statusText = error.response.statusText;
51
- apiError.code = String(apiError.status);
52
- apiError.errors = error.response.data.errors;
53
- if (!apiError.message && apiError.statusText)
54
- apiError.message = apiError.statusText;
55
- sdkError = apiError;
56
- }
57
- else if (error.request) {
58
- // The request was made but no response was received
59
- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of http.ClientRequest in node.js
60
- sdkError.type = ErrorType.REQUEST;
61
- sdkError.request = error.request;
62
- }
63
- else {
64
- // Something happened in setting up the request that triggered an Error
65
- sdkError.type = ErrorType.CLIENT;
66
- }
52
+ if (FetchError.isFetchError(error)) {
53
+ // console.log('********** FetchError')
54
+ const apiError = new ApiError(sdkError);
55
+ apiError.type = ErrorType.RESPONSE;
56
+ apiError.status = error.status;
57
+ apiError.statusText = error.statusText;
58
+ apiError.code = String(apiError.status);
59
+ apiError.errors = error.errors || [];
60
+ if (!apiError.message && apiError.statusText)
61
+ apiError.message = apiError.statusText;
62
+ sdkError = apiError;
63
+ }
64
+ else if (isRequestError(error)) {
65
+ // console.log('********** RequestError')
66
+ sdkError.type = ErrorType.REQUEST;
67
67
  }
68
- else if (axios.isCancel(error))
68
+ else if (isCancelError(error)) {
69
+ // console.log('********** CancelError')
69
70
  sdkError.type = ErrorType.CANCEL;
70
- else
71
+ }
72
+ else if (isTimeoutError(error)) {
73
+ // console.log('********** TimeoutError')
74
+ sdkError.type = ErrorType.TIMEOUT;
75
+ }
76
+ else {
77
+ // console.log('********** ClientError')
78
+ sdkError.type = ErrorType.CLIENT;
71
79
  sdkError.source = error;
80
+ }
72
81
  throw sdkError;
73
82
  };
74
83
  export { SdkError, ApiError, ErrorType, handleError };
@@ -0,0 +1,13 @@
1
+ import type { DocWithData } from 'jsonapi-typescript';
2
+ import type { InterceptorManager } from './interceptor';
3
+ export type FetchResponse = DocWithData;
4
+ export type FetchOptions = RequestInit;
5
+ export declare class FetchError extends Error {
6
+ #private;
7
+ static isFetchError: (error: any) => error is FetchError;
8
+ constructor(status: number, statusText: string, body?: any);
9
+ get errors(): any[] | undefined;
10
+ get status(): number;
11
+ get statusText(): string;
12
+ }
13
+ export declare const fetchURL: (url: URL, options: FetchOptions, interceptors?: InterceptorManager) => Promise<FetchResponse>;
@@ -0,0 +1,46 @@
1
+ import Debug from './debug';
2
+ const debug = Debug('fetch');
3
+ export class FetchError extends Error {
4
+ static isFetchError = (error) => {
5
+ return error instanceof FetchError;
6
+ };
7
+ #errors;
8
+ #status;
9
+ #statusText;
10
+ constructor(status, statusText, body) {
11
+ super(statusText);
12
+ this.#status = status;
13
+ this.#statusText = statusText;
14
+ if (body)
15
+ this.#errors = body.errors;
16
+ }
17
+ get errors() { return this.#errors; }
18
+ get status() { return this.#status; }
19
+ get statusText() { return this.#statusText; }
20
+ }
21
+ export const fetchURL = async (url, options, interceptors) => {
22
+ debug('fetch: %s, %O', url, options || {});
23
+ if (interceptors?.request?.onSuccess)
24
+ ({ url, options } = await interceptors.request.onSuccess({ url, options }));
25
+ // const request: Request = new Request(url, options)
26
+ let response = await fetch(url, options);
27
+ if (response.ok) {
28
+ if (interceptors?.rawReader?.onSuccess)
29
+ await interceptors.rawReader.onSuccess(response);
30
+ if (interceptors?.response?.onSuccess)
31
+ response = await interceptors.response.onSuccess(response);
32
+ }
33
+ else {
34
+ if (interceptors?.rawReader?.onFailure)
35
+ await interceptors.rawReader.onFailure(response);
36
+ }
37
+ const responseBody = await response.json().catch(() => { });
38
+ if (!response.ok) {
39
+ let error = new FetchError(response.status, response.statusText, responseBody);
40
+ if (interceptors?.response?.onFailure)
41
+ error = await interceptors.response.onFailure(error);
42
+ if (error)
43
+ throw error;
44
+ }
45
+ return responseBody;
46
+ };
@@ -1,25 +1,37 @@
1
- import type { AxiosError, AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse, AxiosResponseHeaders, RawAxiosResponseHeaders } from 'axios';
1
+ import type { FetchError, FetchOptions } from "./fetch";
2
+ type InterceptorEventManager<S extends (RequestInterceptor | ResponseInterceptor), F extends (ErrorInterceptor | ResponseInterceptor)> = {
3
+ onSuccess?: S;
4
+ onFailure?: F;
5
+ };
6
+ type RequestEventManager = InterceptorEventManager<RequestInterceptor, ErrorInterceptor>;
7
+ type ResponseEventManager = InterceptorEventManager<ResponseInterceptor, ErrorInterceptor>;
8
+ type ErrorEventManager = InterceptorEventManager<ResponseInterceptor, ResponseInterceptor>;
2
9
  type InterceptorManager = {
3
- request: AxiosInterceptorManager<AxiosRequestConfig>;
4
- response: AxiosInterceptorManager<any>;
10
+ request?: RequestEventManager;
11
+ response?: ResponseEventManager;
12
+ rawReader?: ErrorEventManager;
13
+ };
14
+ type RequestObj = {
15
+ url: URL;
16
+ options: FetchOptions;
5
17
  };
6
- type RequestObj = AxiosRequestConfig;
7
18
  type RequestInterceptor = (request: RequestObj) => RequestObj | Promise<RequestObj>;
8
- type ResponseObj = AxiosResponse;
9
- type ResponseInterceptor = (response: ResponseObj) => ResponseObj;
19
+ type ResponseObj = Response;
20
+ type ResponseInterceptor = (response: ResponseObj) => ResponseObj | Promise<ResponseObj>;
10
21
  type ApiHeadersList = 'x-ratelimit-limit' | 'x-ratelimit-count' | 'x-ratelimit-period' | 'x-ratelimit-interval' | 'x-ratelimit-remaining';
11
22
  type ApiHeaders = {
12
23
  [key in ApiHeadersList]: string | number | boolean;
13
24
  };
14
- type HeadersObj = (AxiosResponseHeaders | RawAxiosResponseHeaders) & ApiHeaders;
15
- type ErrorObj = AxiosError;
16
- type ErrorInterceptor = (error: ErrorObj) => ErrorObj;
25
+ type HeadersObj = Record<string, string> | ApiHeaders;
26
+ type ErrorObj = FetchError;
27
+ type ErrorInterceptor = (error: ErrorObj) => ErrorObj | Promise<ErrorObj>;
17
28
  type InterceptorType = 'request' | 'response';
18
29
  export type { InterceptorManager, RequestInterceptor, ResponseInterceptor, ErrorInterceptor, InterceptorType };
19
30
  export type { RequestObj, ResponseObj, ErrorObj, HeadersObj };
20
31
  type RawResponseReader = {
21
- id: number | undefined;
22
- rawResponse: ResponseObj | undefined;
23
- headers: HeadersObj | undefined;
32
+ id?: number;
33
+ rawResponse?: any;
34
+ headers?: HeadersObj;
35
+ ok: boolean;
24
36
  };
25
37
  export type { RawResponseReader };
@@ -14,4 +14,5 @@ type QueryParams = QueryParamsRetrieve | QueryParamsList;
14
14
  export type { QueryParamsRetrieve, QueryParamsList, QueryParams, QueryFilter };
15
15
  declare const isParamsList: (params: any) => params is QueryParamsList;
16
16
  declare const generateQueryStringParams: (params: QueryParamsRetrieve | QueryParamsList | undefined, res: string | ResourceType) => Record<string, string>;
17
- export { generateQueryStringParams, isParamsList };
17
+ declare const generateSearchString: (params?: QueryParams, questionMark?: boolean) => string;
18
+ export { generateQueryStringParams, isParamsList, generateSearchString };
package/lib/esm/query.js CHANGED
@@ -1,5 +1,5 @@
1
- import Debug from './debug';
2
1
  import { ErrorType, SdkError } from "./error";
2
+ import Debug from './debug';
3
3
  const debug = Debug('query');
4
4
  const arrayFilters = ['_any', '_all', '_in'];
5
5
  const objectFilters = ['_jcont'];
@@ -59,4 +59,9 @@ const generateQueryStringParams = (params, res) => {
59
59
  debug('query string params: %O', qp);
60
60
  return qp;
61
61
  };
62
- export { generateQueryStringParams, isParamsList };
62
+ const generateSearchString = (params, questionMark = true) => {
63
+ if (!params || (Object.keys(params).length === 0))
64
+ return '';
65
+ return `${questionMark ? '?' : ''}${Object.entries(params).map(([key, val]) => `${key}=${String(val)}`).join('&')}`;
66
+ };
67
+ export { generateQueryStringParams, isParamsList, generateSearchString };
@@ -1,7 +1,6 @@
1
1
  import ApiClient, { type ApiClientInitConfig } from './client';
2
2
  import type { QueryParamsRetrieve, QueryParamsList, QueryFilter, QueryParams } from './query';
3
3
  import type { ResourceTypeLock } from './api';
4
- import type { InterceptorManager } from './interceptor';
5
4
  type ResourceNull = {
6
5
  id: null;
7
6
  } & ResourceType;
@@ -53,7 +52,6 @@ type ResourcesConfig = Partial<ResourcesInitConfig>;
53
52
  declare class ResourceAdapter {
54
53
  #private;
55
54
  constructor(config: ResourcesInitConfig);
56
- get interceptors(): InterceptorManager;
57
55
  private localConfig;
58
56
  config(config: ResourcesConfig): ResourceAdapter;
59
57
  get client(): Readonly<ApiClient>;
@@ -73,9 +71,6 @@ declare abstract class ApiResourceBase<R extends Resource> {
73
71
  protected relationshipOneToOne<RR extends ResourceRel>(id: string | ResourceId | null): RR;
74
72
  protected relationshipOneToMany<RR extends ResourceRel>(...ids: string[]): RR[];
75
73
  abstract type(): ResourceTypeLock;
76
- parse(resource: string, options?: {
77
- ignoreSlug?: boolean;
78
- }): R | R[];
79
74
  update(resource: ResourceUpdate, params?: QueryParamsRetrieve, options?: ResourcesConfig): Promise<R>;
80
75
  }
81
76
  declare abstract class ApiResource<R extends Resource> extends ApiResourceBase<R> {
@@ -2,7 +2,6 @@ import ApiClient from './client';
2
2
  import { denormalize, normalize } from './jsonapi';
3
3
  import { generateQueryStringParams, isParamsList } from './query';
4
4
  import config from './config';
5
- import { ErrorType, SdkError } from './error';
6
5
  import Debug from './debug';
7
6
  const debug = Debug('resource');
8
7
  class ListResponse extends Array {
@@ -28,7 +27,6 @@ class ResourceAdapter {
28
27
  this.#client = ApiClient.create(config);
29
28
  this.localConfig(config);
30
29
  }
31
- get interceptors() { return this.#client.interceptors; }
32
30
  localConfig(config) {
33
31
  // if (typeof config.xyz !== 'undefined') this.#config.xyz = config.xyz
34
32
  }
@@ -48,7 +46,7 @@ class ResourceAdapter {
48
46
  const queryParams = generateQueryStringParams(params, resource);
49
47
  if (options?.params)
50
48
  Object.assign(queryParams, options?.params);
51
- const res = await this.#client.request('get', `${resource.type}`, undefined, { ...options, params: queryParams });
49
+ const res = await this.#client.request('GET', `${resource.type}`, undefined, { ...options, params: queryParams });
52
50
  const r = denormalize(res);
53
51
  return r;
54
52
  }
@@ -57,7 +55,7 @@ class ResourceAdapter {
57
55
  const queryParams = generateQueryStringParams(params, resource);
58
56
  if (options?.params)
59
57
  Object.assign(queryParams, options?.params);
60
- const res = await this.#client.request('get', `${resource.type}/${resource.id}`, undefined, { ...options, params: queryParams });
58
+ const res = await this.#client.request('GET', `${resource.type}/${resource.id}`, undefined, { ...options, params: queryParams });
61
59
  const r = denormalize(res);
62
60
  return r;
63
61
  }
@@ -66,7 +64,7 @@ class ResourceAdapter {
66
64
  const queryParams = generateQueryStringParams(params, resource);
67
65
  if (options?.params)
68
66
  Object.assign(queryParams, options?.params);
69
- const res = await this.#client.request('get', `${resource.type}`, undefined, { ...options, params: queryParams });
67
+ const res = await this.#client.request('GET', `${resource.type}`, undefined, { ...options, params: queryParams });
70
68
  const r = denormalize(res);
71
69
  const meta = {
72
70
  pageCount: Number(res.meta?.page_count),
@@ -82,7 +80,7 @@ class ResourceAdapter {
82
80
  if (options?.params)
83
81
  Object.assign(queryParams, options?.params);
84
82
  const data = normalize(resource);
85
- const res = await this.#client.request('post', resource.type, data, { ...options, params: queryParams });
83
+ const res = await this.#client.request('POST', resource.type, data, { ...options, params: queryParams });
86
84
  const r = denormalize(res);
87
85
  return r;
88
86
  }
@@ -92,20 +90,20 @@ class ResourceAdapter {
92
90
  if (options?.params)
93
91
  Object.assign(queryParams, options?.params);
94
92
  const data = normalize(resource);
95
- const res = await this.#client.request('patch', `${resource.type}/${resource.id}`, data, { ...options, params: queryParams });
93
+ const res = await this.#client.request('PATCH', `${resource.type}/${resource.id}`, data, { ...options, params: queryParams });
96
94
  const r = denormalize(res);
97
95
  return r;
98
96
  }
99
97
  async delete(resource, options) {
100
98
  debug('delete: %o, %O', resource, options || {});
101
- await this.#client.request('delete', `${resource.type}/${resource.id}`, undefined, options);
99
+ await this.#client.request('DELETE', `${resource.type}/${resource.id}`, undefined, options);
102
100
  }
103
101
  async fetch(resource, path, params, options) {
104
102
  debug('fetch: %o, %O, %O', path, params || {}, options || {});
105
103
  const queryParams = generateQueryStringParams(params, resource);
106
104
  if (options?.params)
107
105
  Object.assign(queryParams, options?.params);
108
- const res = await this.#client.request('get', path, undefined, { ...options, params: queryParams });
106
+ const res = await this.#client.request('GET', path, undefined, { ...options, params: queryParams });
109
107
  const r = denormalize(res);
110
108
  if (Array.isArray(r)) {
111
109
  const p = params;
@@ -127,6 +125,7 @@ class ApiResourceBase {
127
125
  constructor(adapter) {
128
126
  debug('new resource instance: %s', this.type());
129
127
  this.resources = adapter;
128
+ console.log('CONSTRUCTOR ' + this.type());
130
129
  }
131
130
  relationshipOneToOne(id) {
132
131
  return (((id === null) || (typeof id === 'string')) ? { id, type: this.type() } : { id: id.id, type: this.type() });
@@ -134,29 +133,36 @@ class ApiResourceBase {
134
133
  relationshipOneToMany(...ids) {
135
134
  return (((ids === null) || (ids.length === 0) || (ids[0] === null)) ? [{ id: null, type: this.type() }] : ids.map(id => { return { id, type: this.type() }; }));
136
135
  }
137
- parse(resource, options) {
138
- try {
139
- const res = JSON.parse(resource);
140
- // Resource type always checked
141
- const rtype = res.data?.type;
142
- if (rtype !== this.type())
143
- throw new SdkError({ message: `Invalid resource type [${rtype}]`, type: ErrorType.PARSE });
144
- // Parse options
145
- const { ignoreSlug } = options || {};
146
- if (!ignoreSlug) {
147
- const links = res.data.links.self;
148
- if (!links || !String(links).match(`^${this.resources.client.baseUrl}/${this.type()}/*`))
149
- throw new SdkError({ message: `Resource contains invalid links [${links}]`, type: ErrorType.PARSE });
136
+ /*
137
+ parse(resource: string, options?: { ignoreSlug?: boolean }): R | R[] {
138
+
139
+ try {
140
+
141
+ const res = JSON.parse(resource)
142
+
143
+ // Resource type always checked
144
+ const rtype = res.data?.type
145
+ if (rtype !== this.type()) throw new SdkError({ message: `Invalid resource type [${rtype}]`, type: ErrorType.PARSE })
146
+
147
+ // Parse options
148
+ const { ignoreSlug } = options || {}
149
+
150
+ if (!ignoreSlug) {
151
+ const links = res.data.links.self
152
+ if (!links || !String(links).match(`^${this.resources.client.baseUrl}/${this.type()}/*`))
153
+ throw new SdkError({ message: `Resource contains invalid links [${links}]`, type: ErrorType.PARSE })
154
+ }
155
+
156
+
157
+ return denormalize<R>(res as DocWithData)
158
+
159
+ } catch (error: any) {
160
+ if (SdkError.isSdkError(error)) throw error
161
+ else throw new SdkError({ message: `Payload parse error [${error.message}]`, type: ErrorType.PARSE })
150
162
  }
151
- return denormalize(res);
163
+
152
164
  }
153
- catch (error) {
154
- if (SdkError.isSdkError(error))
155
- throw error;
156
- else
157
- throw new SdkError({ message: `Payload parse error [${error.message}]`, type: ErrorType.PARSE });
158
- }
159
- }
165
+ */
160
166
  // reference, reference_origin and metadata attributes are always updatable
161
167
  async update(resource, params, options) {
162
168
  return this.resources.update({ ...resource, type: this.type() }, params, options);
package/lib/esm/util.js CHANGED
@@ -15,11 +15,11 @@ const nestedField = (obj: any, field: string): { key: string, val: any } => {
15
15
  let fp = field
16
16
  if (fp.endsWith('.')) fp = fp.substring(0, fp.length-1)
17
17
 
18
- const dots = field.split(".")
18
+ const dots = field.split(".")
19
19
 
20
20
  const key = dots[dots.length-1]
21
21
  let val = obj
22
- while (dots.length && (val = val[dots.shift() || '']));
22
+ while (dots.length && (val = val[dots.shift() || '']))
23
23
 
24
24
  return { key, val }
25
25
  }
@@ -28,9 +28,9 @@ const nestedField = (obj: any, field: string): { key: string, val: any } => {
28
28
  const packageInfo = (fields?: string | string[], options?: any): Record<string, any> => {
29
29
  const pjson = require(path.resolve('./', 'package.json'))
30
30
  return fields? (Array.isArray(fields)? fields : [ fields ]).reduce((info: any, field) => {
31
- const nf = nestedField(pjson, field)
32
- info[options?.nestedName? nf.key : field] = nf.val
33
- return info
31
+ const nf = nestedField(pjson, field)
32
+ info[options?.nestedName? nf.key : field] = nf.val
33
+ return info
34
34
  }, {}) : pjson
35
35
  }
36
36
  */