@bloque/sdk-core 0.0.32 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,3 +3,16 @@ export declare const API_BASE_URLS: Record<Mode, string>;
3
3
  export declare const DEFAULT_HEADERS: {
4
4
  'Content-Type': string;
5
5
  };
6
+ /**
7
+ * Supported asset types for transfers and operations.
8
+ * Format: SYMBOL/DECIMALS
9
+ */
10
+ export declare const SUPPORTED_ASSETS: readonly ["DUSD/6", "COPB/6", "COPM/2", "KSM/12"];
11
+ /**
12
+ * Type representing a supported asset
13
+ */
14
+ export type SupportedAsset = (typeof SUPPORTED_ASSETS)[number];
15
+ /**
16
+ * Check if a string is a supported asset
17
+ */
18
+ export declare function isSupportedAsset(asset: string): asset is SupportedAsset;
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,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});
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,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=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}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.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"};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};
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"},o=["DUSD/6","COPB/6","COPM/2","KSM/12"];function s(e){return o.includes(e)}class i 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,i.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 a extends i{retryAfter;constructor(e,t){super(e,{...t,status:429}),this.name="BloqueRateLimitError",this.retryAfter=t?.retryAfter,Object.setPrototypeOf(this,a.prototype)}toJSON(){return{...super.toJSON(),retryAfter:this.retryAfter}}}class n extends i{constructor(e,t){super(e,t),this.name="BloqueAuthenticationError",Object.setPrototypeOf(this,n.prototype)}}class u extends i{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 i{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 l extends i{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,l.prototype)}toJSON(){return{...super.toJSON(),requestedAmount:this.requestedAmount,availableBalance:this.availableBalance,currency:this.currency}}}class h extends i{constructor(e,t){super(e,{...t,code:t?.code??"NETWORK_ERROR"}),this.name="BloqueNetworkError",Object.setPrototypeOf(this,h.prototype)}}class p extends i{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 d extends Error{constructor(e){super(e),this.name="BloqueConfigError",Object.setPrototypeOf(this,d.prototype)}}let m={INSUFFICIENT_FUNDS:l,INSUFFICIENT_BALANCE:l};function f(e,t){let{status:r,code:o}=t??{};if(o&&m[o])return new m[o](e,t);switch(r){case 400:return new u(e,t);case 401:case 403:return new n(e,t);case 404:return new c(e,t);case 429:return new a(e,t);default:return new i(e,t)}}class y{_config;baseUrl;publicRoutes=["/api/aliases","/api/origins/*/assert","/api/origins"];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}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 d('Mode must be either "sandbox" or "production"');if(void 0!==e.timeout&&e.timeout<0)throw new d("Timeout must be a non-negative number");if(void 0!==e.retry.maxRetries&&e.retry.maxRetries<0)throw new d("maxRetries must be a non-negative number");if(void 0!==e.retry.initialDelay&&e.retry.initialDelay<0)throw new d("initialDelay must be a non-negative number");if(void 0!==e.retry.maxDelay&&e.retry.maxDelay<0)throw new d("maxDelay must be a non-negative number");if("apiKey"===e.auth.type){let t;if(!e.auth.apiKey?.trim())throw new d("API key is required for apiKey authentication");if("browser"===(t=e.platform)||"react-native"===t)throw new d("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 d("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 d("Authentication token is missing");return{Authorization:`Bearer ${e}`}}return{}}isRetryableError(e){return e instanceof a||e instanceof h||e instanceof p||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:s,body:i,headers:n={},timeout:u}=e,c=`${this.baseUrl}${s}`,l={...r,...this.buildAuthHeaders(s),...n},d=void 0!==u?u:this._config.timeout??3e4,{enabled:m=!0,maxRetries:y=3}=this._config.retry??{},g=0;for(;g<=(m?y:0);){let e,r=new AbortController;d>0&&(e=setTimeout(()=>{r.abort()},d));try{let s=await fetch(c,{method:o,headers:l,body:i?JSON.stringify(i):void 0,signal:r.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,r=s.headers.get("Retry-After"),o=429===s.status?new a(n.message||"Rate limit exceeded",{status:s.status,code:n.code,requestId:e,response:n,retryAfter:r?Number.parseInt(r,10):void 0}):f(n.message||`HTTP ${s.status}: ${s.statusText}`,{status:s.status,code:n.code,requestId:e,response:n});if(m&&g<y&&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 p(`Request timeout after ${d}ms`,{timeoutMs:d,cause:o}):o instanceof Error?new h(`Request failed: ${o.message}`,{cause:o}):f("Unknown error occurred",{code:"UNKNOWN_ERROR"}),m&&g<y&&this.isRetryableError(r)){t=r;let e=this.calculateRetryDelay(g);await this.sleep(e),g++;continue}throw r}}throw t||f("Request failed after retries",{code:"MAX_RETRIES_EXCEEDED"})}}export{t as API_BASE_URLS,e as BaseClient,i as BloqueAPIError,n as BloqueAuthenticationError,d as BloqueConfigError,l as BloqueInsufficientFundsError,h as BloqueNetworkError,c as BloqueNotFoundError,a as BloqueRateLimitError,p as BloqueTimeoutError,u as BloqueValidationError,r as DEFAULT_HEADERS,y as HttpClient,o as SUPPORTED_ASSETS,f as createBloqueError,s as isSupportedAsset};
package/dist/types.d.ts CHANGED
@@ -154,15 +154,14 @@ export type AuthStrategy = {
154
154
  } | {
155
155
  type: 'jwt';
156
156
  };
157
- /**
158
- * Public configuration object for the Bloque SDK.
159
- *
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
164
- */
165
- export interface BloqueSDKConfig {
157
+ type BaseSDKConfig = {
158
+ /**
159
+ * Base URL for the SDK.
160
+ *
161
+ * If not specified, the SDK defaults to the production environment.
162
+ * If specified, the SDK will use the base URL for all requests.
163
+ */
164
+ baseUrl?: string;
166
165
  /**
167
166
  * Origin identifier for the SDK.
168
167
  *
@@ -277,7 +276,36 @@ export interface BloqueSDKConfig {
277
276
  */
278
277
  maxDelay?: number;
279
278
  };
280
- }
279
+ };
280
+ /**
281
+ * Configuration with custom base URL.
282
+ * When baseUrl is provided, mode must not be specified.
283
+ */
284
+ type ConfigWithBaseUrl = Omit<BaseSDKConfig, 'baseUrl' | 'mode'> & {
285
+ baseUrl: string;
286
+ mode?: never;
287
+ };
288
+ /**
289
+ * Configuration with mode.
290
+ * When mode is provided, baseUrl must not be specified (uses default endpoint).
291
+ */
292
+ type ConfigWithMode = Omit<BaseSDKConfig, 'baseUrl' | 'mode'> & {
293
+ mode: Mode;
294
+ baseUrl?: never;
295
+ };
296
+ /**
297
+ * Public configuration object for the Bloque SDK.
298
+ *
299
+ * This is the configuration interface that users should use when
300
+ * initializing the SDK (via `new SDK()` or `init()` in the modular API).
301
+ *
302
+ * Either provide:
303
+ * - `baseUrl`: custom API endpoint (mode cannot be specified)
304
+ * - `mode`: 'production' or 'sandbox' (uses default endpoints)
305
+ *
306
+ * @public
307
+ */
308
+ export type BloqueSDKConfig = ConfigWithBaseUrl | ConfigWithMode;
281
309
  /**
282
310
  * Internal configuration object with runtime state.
283
311
  *
@@ -287,7 +315,7 @@ export interface BloqueSDKConfig {
287
315
  * @internal - This interface is not part of the public API.
288
316
  * Users should not depend on these fields as they may change without notice.
289
317
  */
290
- export interface BloqueInternalConfig extends BloqueSDKConfig {
318
+ export interface BloqueInternalConfig extends BaseSDKConfig {
291
319
  /**
292
320
  * Access token for authenticated requests.
293
321
  *
@@ -332,3 +360,4 @@ export interface BloqueError {
332
360
  code?: string;
333
361
  status?: number;
334
362
  }
363
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloque/sdk-core",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Core utilities and types for Bloque SDK.",
5
5
  "type": "module",
6
6
  "keywords": [