@bloque/sdk-core 0.0.48 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http-client.d.ts +13 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +51 -6
- package/package.json +1 -1
package/dist/http-client.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class HttpClient {
|
|
|
7
7
|
*/
|
|
8
8
|
private readonly _config;
|
|
9
9
|
private readonly baseUrl;
|
|
10
|
+
private _exchangeExpiry;
|
|
11
|
+
private _exchangePromise;
|
|
10
12
|
private readonly publicRoutes;
|
|
11
13
|
constructor(config: BloqueSDKConfig);
|
|
12
14
|
/**
|
|
@@ -25,6 +27,11 @@ export declare class HttpClient {
|
|
|
25
27
|
* @returns The URN if a session is active, undefined otherwise.
|
|
26
28
|
*/
|
|
27
29
|
get urn(): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Get the current access token (if any).
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
get accessToken(): string | undefined;
|
|
28
35
|
/**
|
|
29
36
|
* Set the access token for authenticated requests.
|
|
30
37
|
* @internal - Called internally after successful authentication.
|
|
@@ -66,5 +73,11 @@ export declare class HttpClient {
|
|
|
66
73
|
* Sleeps for the specified duration in milliseconds.
|
|
67
74
|
*/
|
|
68
75
|
private sleep;
|
|
76
|
+
/**
|
|
77
|
+
* Ensures the SDK has a valid JWT from exchanging the sk_ secret key.
|
|
78
|
+
* Uses promise coalescing to prevent concurrent exchange calls.
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
ensureExchanged(): Promise<void>;
|
|
69
82
|
request<T, U = unknown>(options: RequestOptions<U>): Promise<T>;
|
|
70
83
|
}
|
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,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,BloqueNetworkError:()=>BloqueNetworkError,createBloqueError:()=>createBloqueError,BloqueConfigError:()=>BloqueConfigError,BaseClient:()=>BaseClient,BloqueNotFoundError:()=>BloqueNotFoundError,HttpClient:()=>HttpClient,SUPPORTED_ASSETS:()=>SUPPORTED_ASSETS,BloqueAuthenticationError:()=>BloqueAuthenticationError,BloqueRateLimitError:()=>BloqueRateLimitError,DEFAULT_HEADERS:()=>DEFAULT_HEADERS,BloqueAPIError:()=>BloqueAPIError,BloqueValidationError:()=>BloqueValidationError,isSupportedAsset:()=>isSupportedAsset,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"},SUPPORTED_ASSETS=["DUSD/6","COPB/6","COPM/2","KSM/12"];function isSupportedAsset(e){return SUPPORTED_ASSETS.includes(e)}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;class HttpClient{_config;baseUrl;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins/*/connect","/api/origins"];constructor(e){const r={...e};this.validateConfig(r),this._config=r,this.baseUrl=r.baseUrl??API_BASE_URLS[r.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}setJwtToken(e){if("jwt"!==this._config.auth.type)throw new BloqueConfigError("JWT token can only be set for JWT auth");this._config.tokenStorage?.set(e),this._config.accessToken=e}getJwtToken(){if("jwt"!==this._config.auth.type)throw new BloqueConfigError("JWT token is only available for JWT auth");return this._config.tokenStorage?.get()??null}setUrn(e){this._config.urn=e}setOrigin(e){this._config.origin=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(!e.origin?.trim())throw new BloqueConfigError("Origin 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&&"browser"!==e.platform&&!e.tokenStorage)throw new BloqueConfigError("tokenStorage must be provided when using JWT authentication outside browser platform")}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){if("browser"===this._config.platform)return{};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 i=t*2**e,s=.25*i*(2*Math.random()-1);return Math.min(i+s,o)}sleep(e){return new Promise(r=>setTimeout(r,e))}async request(e){let r,{method:t,path:o,body:i,headers:s={},timeout:n}=e,u=`${this.baseUrl}${o}`,a={...DEFAULT_HEADERS,...this.buildAuthHeaders(o),...s},l=void 0!==n?n: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 s=await fetch(u,{method:t,headers:a,body:i?JSON.stringify(i):void 0,credentials:"jwt"===this._config.auth.type&&"browser"===this._config.platform?"include":void 0,signal:o.signal});void 0!==e&&clearTimeout(e);let n=await s.json().catch(()=>({}));if(!s.ok){let e=s.headers.get("X-Request-ID")??s.headers.get("Request-ID")??void 0,t=s.headers.get("Retry-After"),o=429===s.status?new BloqueRateLimitError(n.message||"Rate limit exceeded",{status:s.status,code:n.code,requestId:e,response:n,retryAfter:t?Number.parseInt(t,10):void 0}):createBloqueError(n.message||`HTTP ${s.status}: ${s.statusText}`,{status:s.status,code:n.code,requestId:e,response:n});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 n}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.SUPPORTED_ASSETS=__webpack_exports__.SUPPORTED_ASSETS,exports.createBloqueError=__webpack_exports__.createBloqueError,exports.isSupportedAsset=__webpack_exports__.isSupportedAsset,__webpack_exports__)-1===["API_BASE_URLS","BaseClient","BloqueAPIError","BloqueAuthenticationError","BloqueConfigError","BloqueInsufficientFundsError","BloqueNetworkError","BloqueNotFoundError","BloqueRateLimitError","BloqueTimeoutError","BloqueValidationError","DEFAULT_HEADERS","HttpClient","SUPPORTED_ASSETS","createBloqueError","isSupportedAsset"].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,BloqueNetworkError:()=>BloqueNetworkError,createBloqueError:()=>createBloqueError,BloqueConfigError:()=>BloqueConfigError,BaseClient:()=>BaseClient,BloqueNotFoundError:()=>BloqueNotFoundError,HttpClient:()=>HttpClient,SUPPORTED_ASSETS:()=>SUPPORTED_ASSETS,BloqueAuthenticationError:()=>BloqueAuthenticationError,BloqueRateLimitError:()=>BloqueRateLimitError,DEFAULT_HEADERS:()=>DEFAULT_HEADERS,BloqueAPIError:()=>BloqueAPIError,BloqueValidationError:()=>BloqueValidationError,isSupportedAsset:()=>isSupportedAsset,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"},SUPPORTED_ASSETS=["DUSD/6","COPB/6","COPM/2","KSM/12"];function isSupportedAsset(e){return SUPPORTED_ASSETS.includes(e)}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 EXCHANGE_REFRESH_BUFFER_MS=6e4,isFrontendPlatform=e=>"browser"===e||"react-native"===e;class HttpClient{_config;baseUrl;_exchangeExpiry=0;_exchangePromise=null;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins/*/connect","/api/origins","/api/api-keys/exchange"];constructor(e){const r={...e};this.validateConfig(r),this._config=r,this.baseUrl=r.baseUrl??API_BASE_URLS[r.mode??"production"]}get origin(){return this._config.origin}get auth(){return this._config.auth}get urn(){return this._config.urn}get accessToken(){return this._config.accessToken}setAccessToken(e){this._config.accessToken=e}setJwtToken(e){if("jwt"!==this._config.auth.type)throw new BloqueConfigError("JWT token can only be set for JWT auth");this._config.tokenStorage?.set(e),this._config.accessToken=e}getJwtToken(){if("jwt"!==this._config.auth.type)throw new BloqueConfigError("JWT token is only available for JWT auth");return this._config.tokenStorage?.get()??null}setUrn(e){this._config.urn=e}setOrigin(e){this._config.origin=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 (sk_ secret key) is required for apiKey authentication");if(isFrontendPlatform(e.platform))throw new BloqueConfigError("API key authentication is not allowed in frontend platforms")}if("originKey"===e.auth.type){if(!e.auth.originKey?.trim())throw new BloqueConfigError("Origin key is required for originKey authentication");if(!e.origin?.trim())throw new BloqueConfigError("Origin is required for originKey authentication");if(isFrontendPlatform(e.platform))throw new BloqueConfigError("Origin key authentication is not allowed in frontend platforms")}if("jwt"===e.auth.type&&"browser"!==e.platform&&!e.tokenStorage)throw new BloqueConfigError("tokenStorage must be provided when using JWT authentication outside browser platform")}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}`}:{};if("originKey"===this._config.auth.type)return this._config.accessToken?{Authorization:`Bearer ${this._config.accessToken}`}:{Authorization:this._config.auth.originKey};if("jwt"===this._config.auth.type){if("browser"===this._config.platform)return{};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 i=t*2**e,s=.25*i*(2*Math.random()-1);return Math.min(i+s,o)}sleep(e){return new Promise(r=>setTimeout(r,e))}async ensureExchanged(){"apiKey"!==this._config.auth.type||(this._exchangePromise?await this._exchangePromise:this._config.accessToken&&Date.now()<this._exchangeExpiry-6e4||(this._exchangePromise=(async()=>{try{let e=this._config.auth,r=await this.request({method:"POST",path:"/api/api-keys/exchange",body:{key:e.apiKey,scopes:e.scopes},_skipExchange:!0});if(!r.access_token)throw new BloqueAuthenticationError("API key exchange returned an invalid response (missing access_token)",{status:401});this._config.accessToken=r.access_token,this._exchangeExpiry=Date.now()+1e3*r.expires_in}finally{this._exchangePromise=null}})(),await this._exchangePromise))}async request(e){let r;"apiKey"!==this._config.auth.type||e._skipExchange||await this.ensureExchanged();let{method:t,path:o,body:i,headers:s={},timeout:n}=e,a=`${this.baseUrl}${o}`,u={...DEFAULT_HEADERS,...this.buildAuthHeaders(o),...s},c=void 0!==n?n:this._config.timeout??3e4,{enabled:l=!0,maxRetries:_=3}=this._config.retry??{},p=0;for(;p<=(l?_:0);){let e,o=new AbortController;c>0&&(e=setTimeout(()=>{o.abort()},c));try{let s=await fetch(a,{method:t,headers:u,body:i?JSON.stringify(i):void 0,credentials:"jwt"===this._config.auth.type&&"browser"===this._config.platform?"include":void 0,signal:o.signal});void 0!==e&&clearTimeout(e);let n=await s.json().catch(()=>({}));if(!s.ok){let e=s.headers.get("X-Request-ID")??s.headers.get("Request-ID")??void 0,t=s.headers.get("Retry-After"),o=429===s.status?new BloqueRateLimitError(n.message||"Rate limit exceeded",{status:s.status,code:n.code,requestId:e,response:n,retryAfter:t?Number.parseInt(t,10):void 0}):createBloqueError(n.message||`HTTP ${s.status}: ${s.statusText}`,{status:s.status,code:n.code,requestId:e,response:n});if(l&&p<_&&this.isRetryableError(o)){r=o;let e=this.calculateRetryDelay(p,t??void 0);await this.sleep(e),p++;continue}throw o}return n}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 ${c}ms`,{timeoutMs:c,cause:o}):o instanceof Error?new BloqueNetworkError(`Request failed: ${o.message}`,{cause:o}):createBloqueError("Unknown error occurred",{code:"UNKNOWN_ERROR"}),l&&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.SUPPORTED_ASSETS=__webpack_exports__.SUPPORTED_ASSETS,exports.createBloqueError=__webpack_exports__.createBloqueError,exports.isSupportedAsset=__webpack_exports__.isSupportedAsset,__webpack_exports__)-1===["API_BASE_URLS","BaseClient","BloqueAPIError","BloqueAuthenticationError","BloqueConfigError","BloqueInsufficientFundsError","BloqueNetworkError","BloqueNotFoundError","BloqueRateLimitError","BloqueTimeoutError","BloqueValidationError","DEFAULT_HEADERS","HttpClient","SUPPORTED_ASSETS","createBloqueError","isSupportedAsset"].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 e{httpClient;constructor(e){this.httpClient=e}}let t={sandbox:"https://dev.bloque.app",production:"https://api.bloque.app"},r={"Content-Type":"application/json"},
|
|
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"},i=["DUSD/6","COPB/6","COPM/2","KSM/12"];function s(e){return i.includes(e)}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 n extends o{retryAfter;constructor(e,t){super(e,{...t,status:429}),this.name="BloqueRateLimitError",this.retryAfter=t?.retryAfter,Object.setPrototypeOf(this,n.prototype)}toJSON(){return{...super.toJSON(),retryAfter:this.retryAfter}}}class a extends o{constructor(e,t){super(e,t),this.name="BloqueAuthenticationError",Object.setPrototypeOf(this,a.prototype)}}class u extends o{validationErrors;constructor(e,t){super(e,{...t,status:400}),this.name="BloqueValidationError",this.validationErrors=t?.validationErrors,Object.setPrototypeOf(this,u.prototype)}toJSON(){return{...super.toJSON(),validationErrors:this.validationErrors}}}class c 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,c.prototype)}toJSON(){return{...super.toJSON(),resourceType:this.resourceType,resourceId:this.resourceId}}}class h 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,h.prototype)}toJSON(){return{...super.toJSON(),requestedAmount:this.requestedAmount,availableBalance:this.availableBalance,currency:this.currency}}}class l extends o{constructor(e,t){super(e,{...t,code:t?.code??"NETWORK_ERROR"}),this.name="BloqueNetworkError",Object.setPrototypeOf(this,l.prototype)}}class p extends o{timeoutMs;constructor(e,t){super(e,{...t,code:"TIMEOUT_ERROR"}),this.name="BloqueTimeoutError",this.timeoutMs=t?.timeoutMs??0,Object.setPrototypeOf(this,p.prototype)}toJSON(){return{...super.toJSON(),timeoutMs:this.timeoutMs}}}class f extends Error{constructor(e){super(e),this.name="BloqueConfigError",Object.setPrototypeOf(this,f.prototype)}}let d={INSUFFICIENT_FUNDS:h,INSUFFICIENT_BALANCE:h};function y(e,t){let{status:r,code:i}=t??{};if(i&&d[i])return new d[i](e,t);switch(r){case 400:return new u(e,t);case 401:case 403:return new a(e,t);case 404:return new c(e,t);case 429:return new n(e,t);default:return new o(e,t)}}let m=e=>"browser"===e||"react-native"===e;class g{_config;baseUrl;_exchangeExpiry=0;_exchangePromise=null;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins/*/connect","/api/origins","/api/api-keys/exchange"];constructor(e){let r={...e};this.validateConfig(r),this._config=r,this.baseUrl=r.baseUrl??t[r.mode??"production"]}get origin(){return this._config.origin}get auth(){return this._config.auth}get urn(){return this._config.urn}get accessToken(){return this._config.accessToken}setAccessToken(e){this._config.accessToken=e}setJwtToken(e){if("jwt"!==this._config.auth.type)throw new f("JWT token can only be set for JWT auth");this._config.tokenStorage?.set(e),this._config.accessToken=e}getJwtToken(){if("jwt"!==this._config.auth.type)throw new f("JWT token is only available for JWT auth");return this._config.tokenStorage?.get()??null}setUrn(e){this._config.urn=e}setOrigin(e){this._config.origin=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 f('Mode must be either "sandbox" or "production"');if(void 0!==e.timeout&&e.timeout<0)throw new f("Timeout must be a non-negative number");if(void 0!==e.retry.maxRetries&&e.retry.maxRetries<0)throw new f("maxRetries must be a non-negative number");if(void 0!==e.retry.initialDelay&&e.retry.initialDelay<0)throw new f("initialDelay must be a non-negative number");if(void 0!==e.retry.maxDelay&&e.retry.maxDelay<0)throw new f("maxDelay must be a non-negative number");if("apiKey"===e.auth.type){if(!e.auth.apiKey?.trim())throw new f("API key (sk_ secret key) is required for apiKey authentication");if(m(e.platform))throw new f("API key authentication is not allowed in frontend platforms")}if("originKey"===e.auth.type){if(!e.auth.originKey?.trim())throw new f("Origin key is required for originKey authentication");if(!e.origin?.trim())throw new f("Origin is required for originKey authentication");if(m(e.platform))throw new f("Origin key authentication is not allowed in frontend platforms")}if("jwt"===e.auth.type&&"browser"!==e.platform&&!e.tokenStorage)throw new f("tokenStorage must be provided when using JWT authentication outside browser platform")}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}`}:{};if("originKey"===this._config.auth.type)return this._config.accessToken?{Authorization:`Bearer ${this._config.accessToken}`}:{Authorization:this._config.auth.originKey};if("jwt"===this._config.auth.type){if("browser"===this._config.platform)return{};let e=this._config.tokenStorage?.get();if(!e)throw new f("Authentication token is missing");return{Authorization:`Bearer ${e}`}}return{}}isRetryableError(e){return e instanceof n||e instanceof l||e instanceof p||e instanceof Error&&"status"in e&&503===e.status}calculateRetryDelay(e,t){let{initialDelay:r=1e3,maxDelay:i=3e4}=this._config.retry??{};if(t){let e=Number.parseInt(t,10);if(!Number.isNaN(e))return Math.min(1e3*e,i);let r=new Date(t);if(!Number.isNaN(r.getTime()))return Math.min(Math.max(r.getTime()-Date.now(),0),i)}let s=r*2**e,o=.25*s*(2*Math.random()-1);return Math.min(s+o,i)}sleep(e){return new Promise(t=>setTimeout(t,e))}async ensureExchanged(){"apiKey"!==this._config.auth.type||(this._exchangePromise?await this._exchangePromise:this._config.accessToken&&Date.now()<this._exchangeExpiry-6e4||(this._exchangePromise=(async()=>{try{let e=this._config.auth,t=await this.request({method:"POST",path:"/api/api-keys/exchange",body:{key:e.apiKey,scopes:e.scopes},_skipExchange:!0});if(!t.access_token)throw new a("API key exchange returned an invalid response (missing access_token)",{status:401});this._config.accessToken=t.access_token,this._exchangeExpiry=Date.now()+1e3*t.expires_in}finally{this._exchangePromise=null}})(),await this._exchangePromise))}async request(e){let t;"apiKey"!==this._config.auth.type||e._skipExchange||await this.ensureExchanged();let{method:i,path:s,body:o,headers:a={},timeout:u}=e,c=`${this.baseUrl}${s}`,h={...r,...this.buildAuthHeaders(s),...a},f=void 0!==u?u:this._config.timeout??3e4,{enabled:d=!0,maxRetries:m=3}=this._config.retry??{},g=0;for(;g<=(d?m:0);){let e,r=new AbortController;f>0&&(e=setTimeout(()=>{r.abort()},f));try{let s=await fetch(c,{method:i,headers:h,body:o?JSON.stringify(o):void 0,credentials:"jwt"===this._config.auth.type&&"browser"===this._config.platform?"include":void 0,signal:r.signal});void 0!==e&&clearTimeout(e);let a=await s.json().catch(()=>({}));if(!s.ok){let e=s.headers.get("X-Request-ID")??s.headers.get("Request-ID")??void 0,r=s.headers.get("Retry-After"),i=429===s.status?new n(a.message||"Rate limit exceeded",{status:s.status,code:a.code,requestId:e,response:a,retryAfter:r?Number.parseInt(r,10):void 0}):y(a.message||`HTTP ${s.status}: ${s.statusText}`,{status:s.status,code:a.code,requestId:e,response:a});if(d&&g<m&&this.isRetryableError(i)){t=i;let e=this.calculateRetryDelay(g,r??void 0);await this.sleep(e),g++;continue}throw i}return a}catch(i){let r;if(void 0!==e&&clearTimeout(e),i&&"object"==typeof i&&"name"in i&&"string"==typeof i.name&&i.name.startsWith("Bloque")&&!this.isRetryableError(i))throw i;if(r=i instanceof Error&&"AbortError"===i.name?new p(`Request timeout after ${f}ms`,{timeoutMs:f,cause:i}):i instanceof Error?new l(`Request failed: ${i.message}`,{cause:i}):y("Unknown error occurred",{code:"UNKNOWN_ERROR"}),d&&g<m&&this.isRetryableError(r)){t=r;let e=this.calculateRetryDelay(g);await this.sleep(e),g++;continue}throw r}}throw t||y("Request failed after retries",{code:"MAX_RETRIES_EXCEEDED"})}}export{t as API_BASE_URLS,e as BaseClient,o as BloqueAPIError,a as BloqueAuthenticationError,f as BloqueConfigError,h as BloqueInsufficientFundsError,l as BloqueNetworkError,c as BloqueNotFoundError,n as BloqueRateLimitError,p as BloqueTimeoutError,u as BloqueValidationError,r as DEFAULT_HEADERS,g as HttpClient,i as SUPPORTED_ASSETS,y as createBloqueError,s as isSupportedAsset};
|
package/dist/types.d.ts
CHANGED
|
@@ -147,12 +147,53 @@ export interface TokenStorage {
|
|
|
147
147
|
*/
|
|
148
148
|
clear(): void;
|
|
149
149
|
}
|
|
150
|
-
export type AuthStrategy =
|
|
150
|
+
export type AuthStrategy =
|
|
151
|
+
/**
|
|
152
|
+
* Opaque API key authentication (`sk_live_...` / `sk_test_...`).
|
|
153
|
+
*
|
|
154
|
+
* The SDK automatically exchanges the secret key for a short-lived JWT
|
|
155
|
+
* via `POST /api/api-keys/exchange` and refreshes it before expiry.
|
|
156
|
+
*
|
|
157
|
+
* Intended for backend platforms (`node`, `bun`, `deno`).
|
|
158
|
+
*/
|
|
159
|
+
{
|
|
151
160
|
type: 'apiKey';
|
|
152
161
|
apiKey: string;
|
|
153
|
-
|
|
162
|
+
scopes?: string[];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Legacy origins-based key authentication.
|
|
166
|
+
*
|
|
167
|
+
* Sends the raw key as an `Authorization` header and uses the
|
|
168
|
+
* `API_KEY` challenge type in `connect(alias)` / `register()` flows.
|
|
169
|
+
*
|
|
170
|
+
* Intended for backend platforms (`node`, `bun`, `deno`).
|
|
171
|
+
*/
|
|
172
|
+
| {
|
|
173
|
+
type: 'originKey';
|
|
174
|
+
originKey: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* JWT authentication for frontend platforms (`browser`, `react-native`).
|
|
178
|
+
*/
|
|
179
|
+
| {
|
|
154
180
|
type: 'jwt';
|
|
155
181
|
};
|
|
182
|
+
/**
|
|
183
|
+
* Parameters for exchanging an API secret key for a short-lived JWT.
|
|
184
|
+
*/
|
|
185
|
+
export interface ExchangeApiKeyParams {
|
|
186
|
+
key: string;
|
|
187
|
+
scopes?: string[];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Result of an API key exchange.
|
|
191
|
+
*/
|
|
192
|
+
export interface ExchangeApiKeyResult {
|
|
193
|
+
accessToken: string;
|
|
194
|
+
expiresIn: number;
|
|
195
|
+
tokenType: string;
|
|
196
|
+
}
|
|
156
197
|
type BaseSDKConfig = {
|
|
157
198
|
/**
|
|
158
199
|
* Base URL for the SDK.
|
|
@@ -167,8 +208,9 @@ type BaseSDKConfig = {
|
|
|
167
208
|
* Used to scope requests and operations
|
|
168
209
|
* to a specific origin within the Bloque platform.
|
|
169
210
|
*
|
|
170
|
-
* Required for `
|
|
171
|
-
* Optional for `
|
|
211
|
+
* Required for `originKey` authentication.
|
|
212
|
+
* Optional for `apiKey` authentication (resolved via `/me` after exchange).
|
|
213
|
+
* Optional for `jwt` authentication (resolved during `authenticate()`).
|
|
172
214
|
*/
|
|
173
215
|
origin?: string;
|
|
174
216
|
/**
|
|
@@ -193,8 +235,9 @@ type BaseSDKConfig = {
|
|
|
193
235
|
/**
|
|
194
236
|
* Authentication strategy used by the SDK.
|
|
195
237
|
*
|
|
196
|
-
* - `apiKey`:
|
|
197
|
-
* - `
|
|
238
|
+
* - `apiKey`: opaque `sk_` keys exchanged for short-lived JWTs. Backend only.
|
|
239
|
+
* - `originKey`: legacy origins-based key. Backend only.
|
|
240
|
+
* - `jwt`: interactive OTP / browser / React Native.
|
|
198
241
|
*
|
|
199
242
|
* The SDK validates the compatibility between the selected
|
|
200
243
|
* platform and the authentication strategy at runtime.
|
|
@@ -356,6 +399,8 @@ export interface RequestOptions<U = unknown> {
|
|
|
356
399
|
* @default 30000 (30 seconds)
|
|
357
400
|
*/
|
|
358
401
|
timeout?: number;
|
|
402
|
+
/** @internal Bypass the auto-exchange guard to prevent recursion. */
|
|
403
|
+
_skipExchange?: boolean;
|
|
359
404
|
}
|
|
360
405
|
export interface BloqueResponse<T> {
|
|
361
406
|
data?: T;
|