@bloque/sdk-core 0.0.23 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/errors.d.ts +216 -1
- package/dist/http-client.d.ts +47 -3
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +149 -12
- package/package.json +1 -1
package/dist/errors.d.ts
CHANGED
|
@@ -1,8 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for creating a BloqueAPIError.
|
|
3
|
+
*/
|
|
4
|
+
export interface BloqueAPIErrorOptions {
|
|
5
|
+
/** HTTP status code (e.g., 400, 401, 500) */
|
|
6
|
+
status?: number;
|
|
7
|
+
/** Error code from the API (e.g., 'INVALID_ALIAS', 'INSUFFICIENT_FUNDS') */
|
|
8
|
+
code?: string;
|
|
9
|
+
/** Request ID for tracing (from response headers) */
|
|
10
|
+
requestId?: string;
|
|
11
|
+
/** Original response body for debugging */
|
|
12
|
+
response?: unknown;
|
|
13
|
+
/** Cause of the error (e.g., network error, parse error) */
|
|
14
|
+
cause?: Error;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Base error class for all Bloque API errors.
|
|
18
|
+
*
|
|
19
|
+
* This error is thrown when the API returns an error response
|
|
20
|
+
* or when a network/timeout error occurs during a request.
|
|
21
|
+
*/
|
|
1
22
|
export declare class BloqueAPIError extends Error {
|
|
23
|
+
/** HTTP status code (e.g., 400, 401, 500) */
|
|
2
24
|
readonly status?: number;
|
|
25
|
+
/** Error code from the API (e.g., 'INVALID_ALIAS', 'INSUFFICIENT_FUNDS') */
|
|
3
26
|
readonly code?: string;
|
|
4
|
-
|
|
27
|
+
/** Request ID for tracing (from response headers) */
|
|
28
|
+
readonly requestId?: string;
|
|
29
|
+
/** Timestamp when the error occurred */
|
|
30
|
+
readonly timestamp: Date;
|
|
31
|
+
/** Original response body for debugging */
|
|
32
|
+
readonly response?: unknown;
|
|
33
|
+
/** Cause of the error (e.g., network error, parse error) */
|
|
34
|
+
readonly cause?: Error;
|
|
35
|
+
constructor(message: string, options?: BloqueAPIErrorOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Returns a JSON representation of the error.
|
|
38
|
+
* Useful for logging and debugging.
|
|
39
|
+
*/
|
|
40
|
+
toJSON(): {
|
|
41
|
+
name: string;
|
|
42
|
+
message: string;
|
|
43
|
+
status: number | undefined;
|
|
44
|
+
code: string | undefined;
|
|
45
|
+
requestId: string | undefined;
|
|
46
|
+
timestamp: string;
|
|
47
|
+
response: unknown;
|
|
48
|
+
stack: string | undefined;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Error thrown when the API rate limit is exceeded (HTTP 429).
|
|
53
|
+
*
|
|
54
|
+
* The SDK will automatically retry these requests if retry is enabled.
|
|
55
|
+
* Check the `retryAfter` field to know when to retry manually if needed.
|
|
56
|
+
*/
|
|
57
|
+
export declare class BloqueRateLimitError extends BloqueAPIError {
|
|
58
|
+
/** Number of seconds to wait before retrying (from Retry-After header) */
|
|
59
|
+
readonly retryAfter?: number;
|
|
60
|
+
constructor(message: string, options?: BloqueAPIErrorOptions & {
|
|
61
|
+
retryAfter?: number;
|
|
62
|
+
});
|
|
63
|
+
toJSON(): {
|
|
64
|
+
retryAfter: number | undefined;
|
|
65
|
+
name: string;
|
|
66
|
+
message: string;
|
|
67
|
+
status: number | undefined;
|
|
68
|
+
code: string | undefined;
|
|
69
|
+
requestId: string | undefined;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
response: unknown;
|
|
72
|
+
stack: string | undefined;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Error thrown when authentication fails (HTTP 401 or 403).
|
|
77
|
+
*
|
|
78
|
+
* Possible causes:
|
|
79
|
+
* - Invalid or expired API key
|
|
80
|
+
* - Invalid or expired JWT token
|
|
81
|
+
* - Insufficient permissions for the requested operation
|
|
82
|
+
*/
|
|
83
|
+
export declare class BloqueAuthenticationError extends BloqueAPIError {
|
|
84
|
+
constructor(message: string, options?: BloqueAPIErrorOptions);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Error thrown when request validation fails (HTTP 400).
|
|
88
|
+
*
|
|
89
|
+
* Possible causes:
|
|
90
|
+
* - Missing required fields
|
|
91
|
+
* - Invalid field format
|
|
92
|
+
* - Invalid field values
|
|
93
|
+
* - Business rule validation failures
|
|
94
|
+
*/
|
|
95
|
+
export declare class BloqueValidationError extends BloqueAPIError {
|
|
96
|
+
/** Validation errors by field (if provided by API) */
|
|
97
|
+
readonly validationErrors?: Record<string, string[]>;
|
|
98
|
+
constructor(message: string, options?: BloqueAPIErrorOptions & {
|
|
99
|
+
validationErrors?: Record<string, string[]>;
|
|
100
|
+
});
|
|
101
|
+
toJSON(): {
|
|
102
|
+
validationErrors: Record<string, string[]> | undefined;
|
|
103
|
+
name: string;
|
|
104
|
+
message: string;
|
|
105
|
+
status: number | undefined;
|
|
106
|
+
code: string | undefined;
|
|
107
|
+
requestId: string | undefined;
|
|
108
|
+
timestamp: string;
|
|
109
|
+
response: unknown;
|
|
110
|
+
stack: string | undefined;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Error thrown when a resource is not found (HTTP 404).
|
|
115
|
+
*
|
|
116
|
+
* Possible causes:
|
|
117
|
+
* - Invalid resource ID
|
|
118
|
+
* - Resource was deleted
|
|
119
|
+
* - User doesn't have access to the resource
|
|
120
|
+
*/
|
|
121
|
+
export declare class BloqueNotFoundError extends BloqueAPIError {
|
|
122
|
+
/** Type of resource that was not found (e.g., 'identity', 'account') */
|
|
123
|
+
readonly resourceType?: string;
|
|
124
|
+
/** ID of the resource that was not found */
|
|
125
|
+
readonly resourceId?: string;
|
|
126
|
+
constructor(message: string, options?: BloqueAPIErrorOptions & {
|
|
127
|
+
resourceType?: string;
|
|
128
|
+
resourceId?: string;
|
|
129
|
+
});
|
|
130
|
+
toJSON(): {
|
|
131
|
+
resourceType: string | undefined;
|
|
132
|
+
resourceId: string | undefined;
|
|
133
|
+
name: string;
|
|
134
|
+
message: string;
|
|
135
|
+
status: number | undefined;
|
|
136
|
+
code: string | undefined;
|
|
137
|
+
requestId: string | undefined;
|
|
138
|
+
timestamp: string;
|
|
139
|
+
response: unknown;
|
|
140
|
+
stack: string | undefined;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Error thrown when an operation requires more funds than available.
|
|
145
|
+
*
|
|
146
|
+
* This is a domain-specific error for financial operations.
|
|
147
|
+
*/
|
|
148
|
+
export declare class BloqueInsufficientFundsError extends BloqueAPIError {
|
|
149
|
+
/** Amount requested for the operation */
|
|
150
|
+
readonly requestedAmount?: number;
|
|
151
|
+
/** Available balance */
|
|
152
|
+
readonly availableBalance?: number;
|
|
153
|
+
/** Currency code (e.g., 'USD', 'CLP') */
|
|
154
|
+
readonly currency?: string;
|
|
155
|
+
constructor(message: string, options?: BloqueAPIErrorOptions & {
|
|
156
|
+
requestedAmount?: number;
|
|
157
|
+
availableBalance?: number;
|
|
158
|
+
currency?: string;
|
|
159
|
+
});
|
|
160
|
+
toJSON(): {
|
|
161
|
+
requestedAmount: number | undefined;
|
|
162
|
+
availableBalance: number | undefined;
|
|
163
|
+
currency: string | undefined;
|
|
164
|
+
name: string;
|
|
165
|
+
message: string;
|
|
166
|
+
status: number | undefined;
|
|
167
|
+
code: string | undefined;
|
|
168
|
+
requestId: string | undefined;
|
|
169
|
+
timestamp: string;
|
|
170
|
+
response: unknown;
|
|
171
|
+
stack: string | undefined;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Error thrown when a network error occurs.
|
|
176
|
+
*
|
|
177
|
+
* Possible causes:
|
|
178
|
+
* - No internet connection
|
|
179
|
+
* - DNS resolution failure
|
|
180
|
+
* - Connection refused
|
|
181
|
+
* - SSL/TLS errors
|
|
182
|
+
*/
|
|
183
|
+
export declare class BloqueNetworkError extends BloqueAPIError {
|
|
184
|
+
constructor(message: string, options?: BloqueAPIErrorOptions);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Error thrown when a request times out.
|
|
188
|
+
*
|
|
189
|
+
* The request exceeded the configured timeout duration.
|
|
190
|
+
* The SDK will automatically retry if retry is enabled.
|
|
191
|
+
*/
|
|
192
|
+
export declare class BloqueTimeoutError extends BloqueAPIError {
|
|
193
|
+
/** Timeout duration in milliseconds */
|
|
194
|
+
readonly timeoutMs: number;
|
|
195
|
+
constructor(message: string, options?: BloqueAPIErrorOptions & {
|
|
196
|
+
timeoutMs: number;
|
|
197
|
+
});
|
|
198
|
+
toJSON(): {
|
|
199
|
+
timeoutMs: number;
|
|
200
|
+
name: string;
|
|
201
|
+
message: string;
|
|
202
|
+
status: number | undefined;
|
|
203
|
+
code: string | undefined;
|
|
204
|
+
requestId: string | undefined;
|
|
205
|
+
timestamp: string;
|
|
206
|
+
response: unknown;
|
|
207
|
+
stack: string | undefined;
|
|
208
|
+
};
|
|
5
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Error thrown when the SDK is misconfigured.
|
|
212
|
+
*
|
|
213
|
+
* This error is thrown before making any API requests.
|
|
214
|
+
*/
|
|
6
215
|
export declare class BloqueConfigError extends Error {
|
|
7
216
|
constructor(message: string);
|
|
8
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Factory function to create the appropriate error type based on status code and error code.
|
|
220
|
+
*
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
export declare function createBloqueError(message: string, options?: BloqueAPIErrorOptions): BloqueAPIError;
|
package/dist/http-client.d.ts
CHANGED
|
@@ -1,11 +1,55 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BloqueSDKConfig, RequestOptions } from './types';
|
|
2
2
|
export declare class HttpClient {
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Internal configuration with runtime state.
|
|
5
|
+
* @internal - Do not access directly. Use public getters instead.
|
|
6
|
+
* This field is private to prevent external access and mutations.
|
|
7
|
+
*/
|
|
8
|
+
private readonly _config;
|
|
4
9
|
private readonly baseUrl;
|
|
5
10
|
private readonly publicRoutes;
|
|
6
|
-
constructor(config:
|
|
11
|
+
constructor(config: BloqueSDKConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Get the origin identifier.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
get origin(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get the authentication strategy.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
get auth(): import("./types").AuthStrategy;
|
|
22
|
+
/**
|
|
23
|
+
* Get the URN of the currently connected identity.
|
|
24
|
+
* @public
|
|
25
|
+
* @returns The URN if a session is active, undefined otherwise.
|
|
26
|
+
*/
|
|
27
|
+
get urn(): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Set the access token for authenticated requests.
|
|
30
|
+
* @internal - Called internally after successful authentication.
|
|
31
|
+
*/
|
|
32
|
+
setAccessToken(token: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Set the URN of the connected identity.
|
|
35
|
+
* @internal - Called internally when connecting to a user session.
|
|
36
|
+
*/
|
|
37
|
+
setUrn(urn: string): void;
|
|
7
38
|
private validateConfig;
|
|
8
39
|
private isPublicRoute;
|
|
9
40
|
private buildAuthHeaders;
|
|
41
|
+
/**
|
|
42
|
+
* Determines if an error is retryable.
|
|
43
|
+
*/
|
|
44
|
+
private isRetryableError;
|
|
45
|
+
/**
|
|
46
|
+
* Calculates the delay before the next retry attempt.
|
|
47
|
+
* Respects the Retry-After header if present, otherwise uses exponential backoff.
|
|
48
|
+
*/
|
|
49
|
+
private calculateRetryDelay;
|
|
50
|
+
/**
|
|
51
|
+
* Sleeps for the specified duration in milliseconds.
|
|
52
|
+
*/
|
|
53
|
+
private sleep;
|
|
10
54
|
request<T, U = unknown>(options: RequestOptions<U>): Promise<T>;
|
|
11
55
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,t)=>{for(var r in t)__webpack_require__.o(t,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{API_BASE_URLS:()=>API_BASE_URLS,BaseClient:()=>BaseClient,DEFAULT_HEADERS:()=>DEFAULT_HEADERS,BloqueAPIError:()=>BloqueAPIError,BloqueConfigError:()=>BloqueConfigError,HttpClient:()=>HttpClient});class BaseClient{httpClient;constructor(e){this.httpClient=e}}const API_BASE_URLS={sandbox:"https://dev.bloque.app",production:"https://api.bloque.app"},DEFAULT_HEADERS={"Content-Type":"application/json"};class BloqueAPIError extends Error{status;code;constructor(e,t,r){super(e),this.name="BloqueAPIError",this.status=t,this.code=r,Object.setPrototypeOf(this,BloqueAPIError.prototype)}}class BloqueConfigError extends Error{constructor(e){super(e),this.name="BloqueConfigError",Object.setPrototypeOf(this,BloqueConfigError.prototype)}}const isFrontendPlatform=e=>"browser"===e||"react-native"===e,createLocalStorageAdapter=()=>({get:()=>"u"<typeof localStorage?null:localStorage.getItem("access_token"),set:e=>{"u">typeof localStorage&&localStorage.setItem("access_token",e)},clear:()=>{"u">typeof localStorage&&localStorage.removeItem("access_token")}});class HttpClient{config;baseUrl;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins"];constructor(e){this.validateConfig(e),this.config=e,this.baseUrl=API_BASE_URLS[e.mode??"production"]}validateConfig(e){if(e.mode??="production",e.platform??="node",!["sandbox","production"].includes(e.mode))throw new BloqueConfigError('Mode must be either "sandbox" or "production"');if("apiKey"===e.auth.type){if(!e.auth.apiKey?.trim())throw new BloqueConfigError("API key is required for apiKey authentication");if(isFrontendPlatform(e.platform))throw new BloqueConfigError("API key authentication is not allowed in frontend platforms")}if("jwt"===e.auth.type&&!e.tokenStorage)if("browser"===e.platform)e.tokenStorage=createLocalStorageAdapter();else throw new BloqueConfigError("tokenStorage must be provided when using JWT authentication")}isPublicRoute(e){let t=e.split("?")[0];return this.publicRoutes.some(e=>{let r=e.replace(/\*/g,"[^/]+");return RegExp(`^${r}$`).test(t)})}buildAuthHeaders(e){if(this.isPublicRoute(e))return{};if("apiKey"===this.config.auth.type)return this.config.accessToken?{Authorization:`Bearer ${this.config.accessToken}`}:{Authorization:this.config.auth.apiKey};if("jwt"===this.config.auth.type){let e=this.config.tokenStorage?.get();if(!e)throw new BloqueConfigError("Authentication token is missing");return{Authorization:`Bearer ${e}`}}return{}}async request(e){let{method:t,path:r,body:o,headers:i={}}=e,a=`${this.baseUrl}${r}`,s={...DEFAULT_HEADERS,...this.buildAuthHeaders(r),...i};try{let e=await fetch(a,{method:t,headers:s,body:o?JSON.stringify(o):void 0}),r=await e.json().catch(()=>({}));if(!e.ok)throw new BloqueAPIError(r.message||`HTTP ${e.status}: ${e.statusText}`,e.status,r.code);return r}catch(e){if(e instanceof BloqueAPIError)throw e;if(e instanceof Error)throw new BloqueAPIError(`Request failed: ${e.message}`,void 0,"NETWORK_ERROR");throw new BloqueAPIError("Unknown error occurred",void 0,"UNKNOWN_ERROR")}}}for(var __rspack_i in exports.API_BASE_URLS=__webpack_exports__.API_BASE_URLS,exports.BaseClient=__webpack_exports__.BaseClient,exports.BloqueAPIError=__webpack_exports__.BloqueAPIError,exports.BloqueConfigError=__webpack_exports__.BloqueConfigError,exports.DEFAULT_HEADERS=__webpack_exports__.DEFAULT_HEADERS,exports.HttpClient=__webpack_exports__.HttpClient,__webpack_exports__)-1===["API_BASE_URLS","BaseClient","BloqueAPIError","BloqueConfigError","DEFAULT_HEADERS","HttpClient"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
|
|
1
|
+
"use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,r)=>{for(var t in r)__webpack_require__.o(r,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},__webpack_require__.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{BloqueInsufficientFundsError:()=>BloqueInsufficientFundsError,API_BASE_URLS:()=>API_BASE_URLS,BloqueAuthenticationError:()=>BloqueAuthenticationError,BloqueNetworkError:()=>BloqueNetworkError,BloqueAPIError:()=>BloqueAPIError,BloqueConfigError:()=>BloqueConfigError,BloqueRateLimitError:()=>BloqueRateLimitError,BloqueValidationError:()=>BloqueValidationError,DEFAULT_HEADERS:()=>DEFAULT_HEADERS,BaseClient:()=>BaseClient,BloqueNotFoundError:()=>BloqueNotFoundError,HttpClient:()=>HttpClient,createBloqueError:()=>createBloqueError,BloqueTimeoutError:()=>BloqueTimeoutError});class BaseClient{httpClient;constructor(e){this.httpClient=e}}const API_BASE_URLS={sandbox:"https://dev.bloque.app",production:"https://api.bloque.app"},DEFAULT_HEADERS={"Content-Type":"application/json"};class BloqueAPIError extends Error{status;code;requestId;timestamp;response;cause;constructor(e,r){super(e),this.name="BloqueAPIError",this.status=r?.status,this.code=r?.code,this.requestId=r?.requestId,this.response=r?.response,this.cause=r?.cause,this.timestamp=new Date,Object.setPrototypeOf(this,BloqueAPIError.prototype)}toJSON(){return{name:this.name,message:this.message,status:this.status,code:this.code,requestId:this.requestId,timestamp:this.timestamp.toISOString(),response:this.response,stack:this.stack}}}class BloqueRateLimitError extends BloqueAPIError{retryAfter;constructor(e,r){super(e,{...r,status:429}),this.name="BloqueRateLimitError",this.retryAfter=r?.retryAfter,Object.setPrototypeOf(this,BloqueRateLimitError.prototype)}toJSON(){return{...super.toJSON(),retryAfter:this.retryAfter}}}class BloqueAuthenticationError extends BloqueAPIError{constructor(e,r){super(e,r),this.name="BloqueAuthenticationError",Object.setPrototypeOf(this,BloqueAuthenticationError.prototype)}}class BloqueValidationError extends BloqueAPIError{validationErrors;constructor(e,r){super(e,{...r,status:400}),this.name="BloqueValidationError",this.validationErrors=r?.validationErrors,Object.setPrototypeOf(this,BloqueValidationError.prototype)}toJSON(){return{...super.toJSON(),validationErrors:this.validationErrors}}}class BloqueNotFoundError extends BloqueAPIError{resourceType;resourceId;constructor(e,r){super(e,{...r,status:404}),this.name="BloqueNotFoundError",this.resourceType=r?.resourceType,this.resourceId=r?.resourceId,Object.setPrototypeOf(this,BloqueNotFoundError.prototype)}toJSON(){return{...super.toJSON(),resourceType:this.resourceType,resourceId:this.resourceId}}}class BloqueInsufficientFundsError extends BloqueAPIError{requestedAmount;availableBalance;currency;constructor(e,r){super(e,r),this.name="BloqueInsufficientFundsError",this.requestedAmount=r?.requestedAmount,this.availableBalance=r?.availableBalance,this.currency=r?.currency,Object.setPrototypeOf(this,BloqueInsufficientFundsError.prototype)}toJSON(){return{...super.toJSON(),requestedAmount:this.requestedAmount,availableBalance:this.availableBalance,currency:this.currency}}}class BloqueNetworkError extends BloqueAPIError{constructor(e,r){super(e,{...r,code:r?.code??"NETWORK_ERROR"}),this.name="BloqueNetworkError",Object.setPrototypeOf(this,BloqueNetworkError.prototype)}}class BloqueTimeoutError extends BloqueAPIError{timeoutMs;constructor(e,r){super(e,{...r,code:"TIMEOUT_ERROR"}),this.name="BloqueTimeoutError",this.timeoutMs=r?.timeoutMs??0,Object.setPrototypeOf(this,BloqueTimeoutError.prototype)}toJSON(){return{...super.toJSON(),timeoutMs:this.timeoutMs}}}class BloqueConfigError extends Error{constructor(e){super(e),this.name="BloqueConfigError",Object.setPrototypeOf(this,BloqueConfigError.prototype)}}const ERROR_CODE_MAP={INSUFFICIENT_FUNDS:BloqueInsufficientFundsError,INSUFFICIENT_BALANCE:BloqueInsufficientFundsError};function createBloqueError(e,r){let{status:t,code:o}=r??{};if(o&&ERROR_CODE_MAP[o])return new ERROR_CODE_MAP[o](e,r);switch(t){case 400:return new BloqueValidationError(e,r);case 401:case 403:return new BloqueAuthenticationError(e,r);case 404:return new BloqueNotFoundError(e,r);case 429:return new BloqueRateLimitError(e,r);default:return new BloqueAPIError(e,r)}}const isFrontendPlatform=e=>"browser"===e||"react-native"===e,createLocalStorageAdapter=()=>("u">typeof console&&console.warn&&console.warn("[Bloque SDK Security Warning] Using localStorage for token storage. localStorage is vulnerable to XSS attacks. For production use, provide a custom tokenStorage with httpOnly cookies or secure storage. See: https://owasp.org/www-community/attacks/xss/"),{get:()=>"u"<typeof localStorage?null:localStorage.getItem("access_token"),set:e=>{"u">typeof localStorage&&localStorage.setItem("access_token",e)},clear:()=>{"u">typeof localStorage&&localStorage.removeItem("access_token")}});class HttpClient{_config;baseUrl;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins"];constructor(e){const r={...e};this.validateConfig(r),this._config=r,this.baseUrl=API_BASE_URLS[e.mode??"production"]}get origin(){return this._config.origin}get auth(){return this._config.auth}get urn(){return this._config.urn}setAccessToken(e){this._config.accessToken=e}setUrn(e){this._config.urn=e}validateConfig(e){if(e.mode??="production",e.platform??="node",e.timeout??=3e4,e.retry??={},e.retry.enabled??=!0,e.retry.maxRetries??=3,e.retry.initialDelay??=1e3,e.retry.maxDelay??=3e4,!["sandbox","production"].includes(e.mode))throw new BloqueConfigError('Mode must be either "sandbox" or "production"');if(void 0!==e.timeout&&e.timeout<0)throw new BloqueConfigError("Timeout must be a non-negative number");if(void 0!==e.retry.maxRetries&&e.retry.maxRetries<0)throw new BloqueConfigError("maxRetries must be a non-negative number");if(void 0!==e.retry.initialDelay&&e.retry.initialDelay<0)throw new BloqueConfigError("initialDelay must be a non-negative number");if(void 0!==e.retry.maxDelay&&e.retry.maxDelay<0)throw new BloqueConfigError("maxDelay must be a non-negative number");if("apiKey"===e.auth.type){if(!e.auth.apiKey?.trim())throw new BloqueConfigError("API key is required for apiKey authentication");if(isFrontendPlatform(e.platform))throw new BloqueConfigError("API key authentication is not allowed in frontend platforms")}if("jwt"===e.auth.type&&!e.tokenStorage)if("browser"===e.platform)e.tokenStorage=createLocalStorageAdapter();else throw new BloqueConfigError("tokenStorage must be provided when using JWT authentication")}isPublicRoute(e){let r=e.split("?")[0];return this.publicRoutes.some(e=>{let t=e.replace(/\*/g,"[^/]+");return RegExp(`^${t}$`).test(r)})}buildAuthHeaders(e){if(this.isPublicRoute(e))return{};if("apiKey"===this._config.auth.type)return this._config.accessToken?{Authorization:`Bearer ${this._config.accessToken}`}:{Authorization:this._config.auth.apiKey};if("jwt"===this._config.auth.type){let e=this._config.tokenStorage?.get();if(!e)throw new BloqueConfigError("Authentication token is missing");return{Authorization:`Bearer ${e}`}}return{}}isRetryableError(e){return e instanceof BloqueRateLimitError||e instanceof BloqueNetworkError||e instanceof BloqueTimeoutError||e instanceof Error&&"status"in e&&503===e.status}calculateRetryDelay(e,r){let{initialDelay:t=1e3,maxDelay:o=3e4}=this._config.retry??{};if(r){let e=Number.parseInt(r,10);if(!Number.isNaN(e))return Math.min(1e3*e,o);let t=new Date(r);if(!Number.isNaN(t.getTime()))return Math.min(Math.max(t.getTime()-Date.now(),0),o)}let s=t*2**e,i=.25*s*(2*Math.random()-1);return Math.min(s+i,o)}sleep(e){return new Promise(r=>setTimeout(r,e))}async request(e){let r,{method:t,path:o,body:s,headers:i={},timeout:a}=e,u=`${this.baseUrl}${o}`,n={...DEFAULT_HEADERS,...this.buildAuthHeaders(o),...i},l=void 0!==a?a:this._config.timeout??3e4,{enabled:c=!0,maxRetries:_=3}=this._config.retry??{},p=0;for(;p<=(c?_:0);){let e,o=new AbortController;l>0&&(e=setTimeout(()=>{o.abort()},l));try{let i=await fetch(u,{method:t,headers:n,body:s?JSON.stringify(s):void 0,signal:o.signal});void 0!==e&&clearTimeout(e);let a=await i.json().catch(()=>({}));if(!i.ok){let e=i.headers.get("X-Request-ID")??i.headers.get("Request-ID")??void 0,t=i.headers.get("Retry-After"),o=429===i.status?new BloqueRateLimitError(a.message||"Rate limit exceeded",{status:i.status,code:a.code,requestId:e,response:a,retryAfter:t?Number.parseInt(t,10):void 0}):createBloqueError(a.message||`HTTP ${i.status}: ${i.statusText}`,{status:i.status,code:a.code,requestId:e,response:a});if(c&&p<_&&this.isRetryableError(o)){r=o;let e=this.calculateRetryDelay(p,t??void 0);await this.sleep(e),p++;continue}throw o}return a}catch(o){let t;if(void 0!==e&&clearTimeout(e),o&&"object"==typeof o&&"name"in o&&"string"==typeof o.name&&o.name.startsWith("Bloque")&&!this.isRetryableError(o))throw o;if(t=o instanceof Error&&"AbortError"===o.name?new BloqueTimeoutError(`Request timeout after ${l}ms`,{timeoutMs:l,cause:o}):o instanceof Error?new BloqueNetworkError(`Request failed: ${o.message}`,{cause:o}):createBloqueError("Unknown error occurred",{code:"UNKNOWN_ERROR"}),c&&p<_&&this.isRetryableError(t)){r=t;let e=this.calculateRetryDelay(p);await this.sleep(e),p++;continue}throw t}}throw r||createBloqueError("Request failed after retries",{code:"MAX_RETRIES_EXCEEDED"})}}for(var __rspack_i in exports.API_BASE_URLS=__webpack_exports__.API_BASE_URLS,exports.BaseClient=__webpack_exports__.BaseClient,exports.BloqueAPIError=__webpack_exports__.BloqueAPIError,exports.BloqueAuthenticationError=__webpack_exports__.BloqueAuthenticationError,exports.BloqueConfigError=__webpack_exports__.BloqueConfigError,exports.BloqueInsufficientFundsError=__webpack_exports__.BloqueInsufficientFundsError,exports.BloqueNetworkError=__webpack_exports__.BloqueNetworkError,exports.BloqueNotFoundError=__webpack_exports__.BloqueNotFoundError,exports.BloqueRateLimitError=__webpack_exports__.BloqueRateLimitError,exports.BloqueTimeoutError=__webpack_exports__.BloqueTimeoutError,exports.BloqueValidationError=__webpack_exports__.BloqueValidationError,exports.DEFAULT_HEADERS=__webpack_exports__.DEFAULT_HEADERS,exports.HttpClient=__webpack_exports__.HttpClient,exports.createBloqueError=__webpack_exports__.createBloqueError,__webpack_exports__)-1===["API_BASE_URLS","BaseClient","BloqueAPIError","BloqueAuthenticationError","BloqueConfigError","BloqueInsufficientFundsError","BloqueNetworkError","BloqueNotFoundError","BloqueRateLimitError","BloqueTimeoutError","BloqueValidationError","DEFAULT_HEADERS","HttpClient","createBloqueError"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class e{httpClient;constructor(e){this.httpClient=e}}let t={sandbox:"https://dev.bloque.app",production:"https://api.bloque.app"},r={"Content-Type":"application/json"};class o extends Error{status;code;requestId;timestamp;response;cause;constructor(e,t){super(e),this.name="BloqueAPIError",this.status=t?.status,this.code=t?.code,this.requestId=t?.requestId,this.response=t?.response,this.cause=t?.cause,this.timestamp=new Date,Object.setPrototypeOf(this,o.prototype)}toJSON(){return{name:this.name,message:this.message,status:this.status,code:this.code,requestId:this.requestId,timestamp:this.timestamp.toISOString(),response:this.response,stack:this.stack}}}class s extends o{retryAfter;constructor(e,t){super(e,{...t,status:429}),this.name="BloqueRateLimitError",this.retryAfter=t?.retryAfter,Object.setPrototypeOf(this,s.prototype)}toJSON(){return{...super.toJSON(),retryAfter:this.retryAfter}}}class i extends o{constructor(e,t){super(e,t),this.name="BloqueAuthenticationError",Object.setPrototypeOf(this,i.prototype)}}class a extends o{validationErrors;constructor(e,t){super(e,{...t,status:400}),this.name="BloqueValidationError",this.validationErrors=t?.validationErrors,Object.setPrototypeOf(this,a.prototype)}toJSON(){return{...super.toJSON(),validationErrors:this.validationErrors}}}class n extends o{resourceType;resourceId;constructor(e,t){super(e,{...t,status:404}),this.name="BloqueNotFoundError",this.resourceType=t?.resourceType,this.resourceId=t?.resourceId,Object.setPrototypeOf(this,n.prototype)}toJSON(){return{...super.toJSON(),resourceType:this.resourceType,resourceId:this.resourceId}}}class u extends o{requestedAmount;availableBalance;currency;constructor(e,t){super(e,t),this.name="BloqueInsufficientFundsError",this.requestedAmount=t?.requestedAmount,this.availableBalance=t?.availableBalance,this.currency=t?.currency,Object.setPrototypeOf(this,u.prototype)}toJSON(){return{...super.toJSON(),requestedAmount:this.requestedAmount,availableBalance:this.availableBalance,currency:this.currency}}}class c extends o{constructor(e,t){super(e,{...t,code:t?.code??"NETWORK_ERROR"}),this.name="BloqueNetworkError",Object.setPrototypeOf(this,c.prototype)}}class l extends o{timeoutMs;constructor(e,t){super(e,{...t,code:"TIMEOUT_ERROR"}),this.name="BloqueTimeoutError",this.timeoutMs=t?.timeoutMs??0,Object.setPrototypeOf(this,l.prototype)}toJSON(){return{...super.toJSON(),timeoutMs:this.timeoutMs}}}class h extends Error{constructor(e){super(e),this.name="BloqueConfigError",Object.setPrototypeOf(this,h.prototype)}}let p={INSUFFICIENT_FUNDS:u,INSUFFICIENT_BALANCE:u};function d(e,t){let{status:r,code:u}=t??{};if(u&&p[u])return new p[u](e,t);switch(r){case 400:return new a(e,t);case 401:case 403:return new i(e,t);case 404:return new n(e,t);case 429:return new s(e,t);default:return new o(e,t)}}class m{_config;baseUrl;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins"];constructor(e){let r={...e};this.validateConfig(r),this._config=r,this.baseUrl=t[e.mode??"production"]}get origin(){return this._config.origin}get auth(){return this._config.auth}get urn(){return this._config.urn}setAccessToken(e){this._config.accessToken=e}setUrn(e){this._config.urn=e}validateConfig(e){if(e.mode??="production",e.platform??="node",e.timeout??=3e4,e.retry??={},e.retry.enabled??=!0,e.retry.maxRetries??=3,e.retry.initialDelay??=1e3,e.retry.maxDelay??=3e4,!["sandbox","production"].includes(e.mode))throw new h('Mode must be either "sandbox" or "production"');if(void 0!==e.timeout&&e.timeout<0)throw new h("Timeout must be a non-negative number");if(void 0!==e.retry.maxRetries&&e.retry.maxRetries<0)throw new h("maxRetries must be a non-negative number");if(void 0!==e.retry.initialDelay&&e.retry.initialDelay<0)throw new h("initialDelay must be a non-negative number");if(void 0!==e.retry.maxDelay&&e.retry.maxDelay<0)throw new h("maxDelay must be a non-negative number");if("apiKey"===e.auth.type){let t;if(!e.auth.apiKey?.trim())throw new h("API key is required for apiKey authentication");if("browser"===(t=e.platform)||"react-native"===t)throw new h("API key authentication is not allowed in frontend platforms")}if("jwt"===e.auth.type&&!e.tokenStorage)if("browser"===e.platform)"u">typeof console&&console.warn&&console.warn("[Bloque SDK Security Warning] Using localStorage for token storage. localStorage is vulnerable to XSS attacks. For production use, provide a custom tokenStorage with httpOnly cookies or secure storage. See: https://owasp.org/www-community/attacks/xss/"),e.tokenStorage={get:()=>"u"<typeof localStorage?null:localStorage.getItem("access_token"),set:e=>{"u">typeof localStorage&&localStorage.setItem("access_token",e)},clear:()=>{"u">typeof localStorage&&localStorage.removeItem("access_token")}};else throw new h("tokenStorage must be provided when using JWT authentication")}isPublicRoute(e){let t=e.split("?")[0];return this.publicRoutes.some(e=>{let r=e.replace(/\*/g,"[^/]+");return RegExp(`^${r}$`).test(t)})}buildAuthHeaders(e){if(this.isPublicRoute(e))return{};if("apiKey"===this._config.auth.type)return this._config.accessToken?{Authorization:`Bearer ${this._config.accessToken}`}:{Authorization:this._config.auth.apiKey};if("jwt"===this._config.auth.type){let e=this._config.tokenStorage?.get();if(!e)throw new h("Authentication token is missing");return{Authorization:`Bearer ${e}`}}return{}}isRetryableError(e){return e instanceof s||e instanceof c||e instanceof l||e instanceof Error&&"status"in e&&503===e.status}calculateRetryDelay(e,t){let{initialDelay:r=1e3,maxDelay:o=3e4}=this._config.retry??{};if(t){let e=Number.parseInt(t,10);if(!Number.isNaN(e))return Math.min(1e3*e,o);let r=new Date(t);if(!Number.isNaN(r.getTime()))return Math.min(Math.max(r.getTime()-Date.now(),0),o)}let s=r*2**e,i=.25*s*(2*Math.random()-1);return Math.min(s+i,o)}sleep(e){return new Promise(t=>setTimeout(t,e))}async request(e){let t,{method:o,path:i,body:a,headers:n={},timeout:u}=e,h=`${this.baseUrl}${i}`,p={...r,...this.buildAuthHeaders(i),...n},m=void 0!==u?u:this._config.timeout??3e4,{enabled:y=!0,maxRetries:f=3}=this._config.retry??{},g=0;for(;g<=(y?f:0);){let e,r=new AbortController;m>0&&(e=setTimeout(()=>{r.abort()},m));try{let i=await fetch(h,{method:o,headers:p,body:a?JSON.stringify(a):void 0,signal:r.signal});void 0!==e&&clearTimeout(e);let n=await i.json().catch(()=>({}));if(!i.ok){let e=i.headers.get("X-Request-ID")??i.headers.get("Request-ID")??void 0,r=i.headers.get("Retry-After"),o=429===i.status?new s(n.message||"Rate limit exceeded",{status:i.status,code:n.code,requestId:e,response:n,retryAfter:r?Number.parseInt(r,10):void 0}):d(n.message||`HTTP ${i.status}: ${i.statusText}`,{status:i.status,code:n.code,requestId:e,response:n});if(y&&g<f&&this.isRetryableError(o)){t=o;let e=this.calculateRetryDelay(g,r??void 0);await this.sleep(e),g++;continue}throw o}return n}catch(o){let r;if(void 0!==e&&clearTimeout(e),o&&"object"==typeof o&&"name"in o&&"string"==typeof o.name&&o.name.startsWith("Bloque")&&!this.isRetryableError(o))throw o;if(r=o instanceof Error&&"AbortError"===o.name?new l(`Request timeout after ${m}ms`,{timeoutMs:m,cause:o}):o instanceof Error?new c(`Request failed: ${o.message}`,{cause:o}):d("Unknown error occurred",{code:"UNKNOWN_ERROR"}),y&&g<f&&this.isRetryableError(r)){t=r;let e=this.calculateRetryDelay(g);await this.sleep(e),g++;continue}throw r}}throw t||d("Request failed after retries",{code:"MAX_RETRIES_EXCEEDED"})}}export{t as API_BASE_URLS,e as BaseClient,o as BloqueAPIError,i as BloqueAuthenticationError,h as BloqueConfigError,u as BloqueInsufficientFundsError,c as BloqueNetworkError,n as BloqueNotFoundError,s as BloqueRateLimitError,l as BloqueTimeoutError,a as BloqueValidationError,r as DEFAULT_HEADERS,m as HttpClient,d as createBloqueError};
|
package/dist/types.d.ts
CHANGED
|
@@ -80,6 +80,52 @@ export type Platform =
|
|
|
80
80
|
*
|
|
81
81
|
* This allows consumers to customize the storage mechanism
|
|
82
82
|
* (localStorage, sessionStorage, cookies, in-memory, etc.).
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ SECURITY CONSIDERATIONS:
|
|
85
|
+
*
|
|
86
|
+
* **localStorage / sessionStorage (INSECURE)**:
|
|
87
|
+
* - Vulnerable to XSS attacks - any malicious script can read the token
|
|
88
|
+
* - Should ONLY be used for development/testing or non-sensitive apps
|
|
89
|
+
* - NOT recommended for production applications handling sensitive data
|
|
90
|
+
*
|
|
91
|
+
* **httpOnly Cookies (RECOMMENDED)**:
|
|
92
|
+
* - Best security practice - not accessible to JavaScript
|
|
93
|
+
* - Protects against XSS attacks
|
|
94
|
+
* - Requires server-side cooperation to set cookies
|
|
95
|
+
*
|
|
96
|
+
* **Secure Storage (RECOMMENDED for mobile)**:
|
|
97
|
+
* - Use platform-specific secure storage (e.g., Keychain on iOS, Keystore on Android)
|
|
98
|
+
* - Libraries: @react-native-async-storage/async-storage, expo-secure-store
|
|
99
|
+
*
|
|
100
|
+
* **In-Memory Storage**:
|
|
101
|
+
* - Most secure against XSS
|
|
102
|
+
* - Token lost on page refresh/app restart
|
|
103
|
+
* - Good for short-lived sessions
|
|
104
|
+
*
|
|
105
|
+
* Example implementation using httpOnly cookies:
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const cookieStorage: TokenStorage = {
|
|
108
|
+
* get: () => {
|
|
109
|
+
* // Token is automatically sent in httpOnly cookie
|
|
110
|
+
* // You may need to fetch from server or return null
|
|
111
|
+
* return null;
|
|
112
|
+
* },
|
|
113
|
+
* set: (token) => {
|
|
114
|
+
* // Send to server to set httpOnly cookie
|
|
115
|
+
* fetch('/api/auth/set-token', {
|
|
116
|
+
* method: 'POST',
|
|
117
|
+
* body: JSON.stringify({ token })
|
|
118
|
+
* });
|
|
119
|
+
* },
|
|
120
|
+
* clear: () => {
|
|
121
|
+
* // Call server to clear cookie
|
|
122
|
+
* fetch('/api/auth/logout', { method: 'POST' });
|
|
123
|
+
* }
|
|
124
|
+
* };
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @see https://owasp.org/www-community/attacks/xss/
|
|
128
|
+
* @see https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html#local-storage
|
|
83
129
|
*/
|
|
84
130
|
export interface TokenStorage {
|
|
85
131
|
/**
|
|
@@ -91,6 +137,9 @@ export interface TokenStorage {
|
|
|
91
137
|
/**
|
|
92
138
|
* Persists a JWT token.
|
|
93
139
|
*
|
|
140
|
+
* ⚠️ WARNING: If using localStorage/sessionStorage, this token will be
|
|
141
|
+
* accessible to any JavaScript code on the page, including malicious scripts.
|
|
142
|
+
*
|
|
94
143
|
* @param token The JWT token to store.
|
|
95
144
|
*/
|
|
96
145
|
set(token: string): void;
|
|
@@ -106,12 +155,14 @@ export type AuthStrategy = {
|
|
|
106
155
|
type: 'jwt';
|
|
107
156
|
};
|
|
108
157
|
/**
|
|
109
|
-
*
|
|
158
|
+
* Public configuration object for the Bloque SDK.
|
|
110
159
|
*
|
|
111
|
-
* This configuration
|
|
112
|
-
* the SDK (via `new SDK()` or `init()` in the modular API).
|
|
160
|
+
* This is the configuration interface that users should use when
|
|
161
|
+
* initializing the SDK (via `new SDK()` or `init()` in the modular API).
|
|
162
|
+
*
|
|
163
|
+
* @public
|
|
113
164
|
*/
|
|
114
|
-
export interface
|
|
165
|
+
export interface BloqueSDKConfig {
|
|
115
166
|
/**
|
|
116
167
|
* Origin identifier for the SDK.
|
|
117
168
|
*
|
|
@@ -160,23 +211,97 @@ export interface BloqueConfig {
|
|
|
160
211
|
/**
|
|
161
212
|
* JWT token storage strategy.
|
|
162
213
|
*
|
|
163
|
-
* Only applies when `
|
|
214
|
+
* Only applies when `platform` is set to `browser` or `react-native`.
|
|
215
|
+
*
|
|
216
|
+
* **Default behavior (browser only)**:
|
|
217
|
+
* - The SDK uses `localStorage` by default for browser platform
|
|
218
|
+
* - ⚠️ localStorage is INSECURE and vulnerable to XSS attacks
|
|
219
|
+
* - A security warning will be logged to the console
|
|
164
220
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
221
|
+
* **For production applications**, provide a custom `tokenStorage` using:
|
|
222
|
+
* - httpOnly cookies (recommended - immune to XSS)
|
|
223
|
+
* - Secure storage libraries (for React Native)
|
|
224
|
+
* - sessionStorage (slightly better than localStorage, but still vulnerable)
|
|
225
|
+
* - In-memory storage (most secure, but lost on refresh)
|
|
226
|
+
*
|
|
227
|
+
* @see TokenStorage for security considerations and examples
|
|
168
228
|
*/
|
|
169
229
|
tokenStorage?: TokenStorage;
|
|
170
230
|
/**
|
|
171
|
-
*
|
|
231
|
+
* Default timeout for HTTP requests in milliseconds.
|
|
232
|
+
*
|
|
233
|
+
* If a request takes longer than this timeout, it will be aborted
|
|
234
|
+
* and throw a BloqueAPIError with code 'TIMEOUT_ERROR'.
|
|
235
|
+
*
|
|
236
|
+
* Can be overridden per-request using the `timeout` option in RequestOptions.
|
|
237
|
+
*
|
|
238
|
+
* Set to 0 to disable timeouts globally (not recommended for production).
|
|
239
|
+
*
|
|
240
|
+
* @default 30000 (30 seconds)
|
|
241
|
+
*/
|
|
242
|
+
timeout?: number;
|
|
243
|
+
/**
|
|
244
|
+
* Retry configuration for failed requests.
|
|
245
|
+
*
|
|
246
|
+
* When enabled, the SDK will automatically retry failed requests with
|
|
247
|
+
* exponential backoff for the following scenarios:
|
|
248
|
+
* - 429 (Too Many Requests)
|
|
249
|
+
* - 503 (Service Unavailable)
|
|
250
|
+
* - Network errors (timeouts, connection failures)
|
|
251
|
+
*
|
|
252
|
+
* The SDK respects the `Retry-After` header when present.
|
|
253
|
+
*
|
|
254
|
+
* @default { enabled: true, maxRetries: 3, initialDelay: 1000 }
|
|
255
|
+
*/
|
|
256
|
+
retry?: {
|
|
257
|
+
/**
|
|
258
|
+
* Whether to enable automatic retries.
|
|
259
|
+
* @default true
|
|
260
|
+
*/
|
|
261
|
+
enabled?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Maximum number of retry attempts.
|
|
264
|
+
* @default 3
|
|
265
|
+
*/
|
|
266
|
+
maxRetries?: number;
|
|
267
|
+
/**
|
|
268
|
+
* Initial delay in milliseconds before the first retry.
|
|
269
|
+
* Subsequent retries use exponential backoff: delay * (2 ^ attempt).
|
|
270
|
+
* @default 1000 (1 second)
|
|
271
|
+
*/
|
|
272
|
+
initialDelay?: number;
|
|
273
|
+
/**
|
|
274
|
+
* Maximum delay in milliseconds between retries.
|
|
275
|
+
* Prevents exponential backoff from growing too large.
|
|
276
|
+
* @default 30000 (30 seconds)
|
|
277
|
+
*/
|
|
278
|
+
maxDelay?: number;
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Internal configuration object with runtime state.
|
|
283
|
+
*
|
|
284
|
+
* Extends the public configuration with internal fields that are
|
|
285
|
+
* managed by the SDK at runtime (access tokens, session state, etc.).
|
|
286
|
+
*
|
|
287
|
+
* @internal - This interface is not part of the public API.
|
|
288
|
+
* Users should not depend on these fields as they may change without notice.
|
|
289
|
+
*/
|
|
290
|
+
export interface BloqueInternalConfig extends BloqueSDKConfig {
|
|
291
|
+
/**
|
|
292
|
+
* Access token for authenticated requests.
|
|
172
293
|
*
|
|
173
|
-
*
|
|
294
|
+
* @internal
|
|
295
|
+
* Set internally after successful authentication (register/connect).
|
|
296
|
+
* Should not be accessed or modified by SDK users.
|
|
174
297
|
*/
|
|
175
298
|
accessToken?: string;
|
|
176
299
|
/**
|
|
177
|
-
*
|
|
300
|
+
* URN of the currently connected identity.
|
|
178
301
|
*
|
|
179
|
-
*
|
|
302
|
+
* @internal
|
|
303
|
+
* Set internally when connecting to a user session.
|
|
304
|
+
* Should not be accessed or modified by SDK users.
|
|
180
305
|
*/
|
|
181
306
|
urn?: string;
|
|
182
307
|
}
|
|
@@ -185,6 +310,18 @@ export interface RequestOptions<U = unknown> {
|
|
|
185
310
|
path: string;
|
|
186
311
|
body?: U;
|
|
187
312
|
headers?: Record<string, string>;
|
|
313
|
+
/**
|
|
314
|
+
* Request timeout in milliseconds.
|
|
315
|
+
*
|
|
316
|
+
* If not specified, uses the default timeout from SDK config.
|
|
317
|
+
* Set to 0 to disable timeout for this specific request.
|
|
318
|
+
*
|
|
319
|
+
* When a request exceeds the timeout, it will be aborted and
|
|
320
|
+
* throw a BloqueAPIError with code 'TIMEOUT_ERROR'.
|
|
321
|
+
*
|
|
322
|
+
* @default 30000 (30 seconds)
|
|
323
|
+
*/
|
|
324
|
+
timeout?: number;
|
|
188
325
|
}
|
|
189
326
|
export interface BloqueResponse<T> {
|
|
190
327
|
data?: T;
|