@kelviq/js-sdk 1.0.0 → 2.0.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/README.md +12 -12
- package/dist/index.d.ts +60 -40
- package/dist/kelviq-js-sdk.iife.js +1 -1
- package/dist/kelviq-js-sdk.js +224 -194
- package/dist/kelviq-js-sdk.umd.js +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
title: "JavaScript SDK"
|
|
3
|
-
description: "Documentation for the Kelviq JavaScript SDK"
|
|
4
|
-
icon: "js"
|
|
5
|
-
---
|
|
1
|
+
# @kelviq/js-sdk
|
|
6
2
|
|
|
7
|
-
|
|
3
|
+
A lightweight TypeScript SDK for integrating with the Kelviq entitlement service. Easily check feature access, retrieve entitlement details, and manage client-side caching for your application.
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
- Cache these entitlements client-side for efficient and repeated access.
|
|
11
|
-
- Check if a user has access to a particular feature.
|
|
12
|
-
- Retrieve specific entitlement details, including boolean flags, numeric configurations, and metered usage data.
|
|
13
|
-
- Manually refresh entitlement data from the server.
|
|
5
|
+
## Installation
|
|
14
6
|
|
|
15
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kelviq/js-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Documentation
|
|
12
|
+
|
|
13
|
+
For full usage instructions, API reference, and examples, visit the official docs:
|
|
14
|
+
|
|
15
|
+
**[https://docs.kelviq.com/frontend-integration/js-sdk](https://docs.kelviq.com/frontend-integration/js-sdk)**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface RawEntitlement {
|
|
4
4
|
featureId: string;
|
|
5
|
+
featureType: "BOOLEAN" | "CUSTOMIZABLE" | "METER";
|
|
5
6
|
hasAccess: boolean;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
resetAt?: string | null;
|
|
8
|
+
hardLimit?: boolean;
|
|
9
|
+
usageLimit?: number | null;
|
|
10
|
+
currentUsage?: number;
|
|
11
|
+
remaining?: number | null;
|
|
10
12
|
}
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
export interface RawEntitlementsApiResponse {
|
|
14
|
+
customerId: string;
|
|
15
|
+
entitlements: RawEntitlement[];
|
|
14
16
|
}
|
|
15
|
-
export interface
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
export interface Entitlement {
|
|
18
|
+
featureId: string;
|
|
19
|
+
featureType: "METER" | "BOOLEAN" | "CUSTOMIZABLE";
|
|
20
|
+
hasAccess: boolean;
|
|
21
|
+
hardLimit: boolean;
|
|
22
|
+
currentUsage: number;
|
|
23
|
+
usageLimit: number | null;
|
|
19
24
|
remaining: number | null;
|
|
20
|
-
|
|
21
|
-
hardLimit?: boolean;
|
|
25
|
+
items: RawEntitlement[];
|
|
22
26
|
}
|
|
23
|
-
export type
|
|
24
|
-
export type EntitlementMap = Record<string, AnyEntitlement>;
|
|
27
|
+
export type EntitlementMap = Record<string, Entitlement>;
|
|
25
28
|
/**
|
|
26
29
|
* Configuration for an API request.
|
|
27
30
|
* @template ReqBody The type of the request body.
|
|
@@ -95,53 +98,63 @@ declare function apiRequest<ResData = any, ReqBody = any>(config: ApiRequestConf
|
|
|
95
98
|
declare class KelviqClient {
|
|
96
99
|
private readonly options;
|
|
97
100
|
private entitlementsCache;
|
|
101
|
+
private rawApiResponse;
|
|
98
102
|
private isFetching;
|
|
103
|
+
private inFlightPromise;
|
|
104
|
+
private readyPromise;
|
|
99
105
|
private lastFetchError;
|
|
100
106
|
private readonly apiRequestService;
|
|
101
107
|
/**
|
|
102
108
|
* Creates an instance of KelviqClient.
|
|
103
|
-
* Prefer using the `
|
|
109
|
+
* Prefer using the `kelviqSDK` factory function for instantiation.
|
|
104
110
|
* @internal
|
|
105
111
|
* @param options Configuration options for the client.
|
|
106
112
|
* @param customApiRequest Optional: A custom function to handle API requests, conforming to ApiRequestConfig and ApiResponse.
|
|
107
113
|
*/
|
|
108
114
|
constructor(options: KelviqClientOptions, customApiRequest?: typeof apiRequest);
|
|
109
115
|
/**
|
|
110
|
-
* Fetches all entitlements from the API for the configured
|
|
116
|
+
* Fetches all entitlements from the API for the configured customerId,
|
|
111
117
|
* processes them, and caches the result.
|
|
112
118
|
* Subsequent calls will return cached data unless `forceRefresh` is true.
|
|
113
119
|
*
|
|
114
120
|
* @param forceRefresh - If true, fetches from the API even if data is already cached. Defaults to false.
|
|
115
|
-
* @returns A promise that resolves to the map of entitlements
|
|
121
|
+
* @returns A promise that resolves to the map of aggregated entitlements.
|
|
116
122
|
* @throws Will throw an error if the API request fails after all retries.
|
|
117
123
|
*/
|
|
118
124
|
fetchAllEntitlements(forceRefresh?: boolean): Promise<EntitlementMap>;
|
|
119
125
|
/**
|
|
120
|
-
* Retrieves a specific entitlement from the local cache.
|
|
121
|
-
* Note: `fetchAllEntitlements()` must be called successfully at least once before using this method,
|
|
122
|
-
* or the cache will be empty.
|
|
126
|
+
* Retrieves a specific aggregated entitlement from the local cache.
|
|
123
127
|
*
|
|
124
|
-
* @param featureId The unique
|
|
125
|
-
* @
|
|
126
|
-
* @returns The processed entitlement object if found in the cache and the type matches, otherwise `null`.
|
|
127
|
-
* Logs a warning if entitlements haven't been fetched or if a type mismatch occurs.
|
|
128
|
+
* @param featureId The unique identifier of the feature.
|
|
129
|
+
* @returns The aggregated Entitlement object if found, otherwise `null`.
|
|
128
130
|
*/
|
|
129
|
-
getEntitlement
|
|
131
|
+
getEntitlement(featureId: string): Entitlement | null;
|
|
130
132
|
/**
|
|
131
|
-
*
|
|
132
|
-
* Note: `fetchAllEntitlements()` should be called successfully at least once before using this method.
|
|
133
|
+
* Returns all aggregated entitlements as a map keyed by featureId.
|
|
133
134
|
*
|
|
134
|
-
* @
|
|
135
|
-
* @returns `true` if access is granted, `false` if denied or the feature is not found in the cache.
|
|
136
|
-
* Logs a warning and returns `false` if entitlements haven't been fetched.
|
|
135
|
+
* @returns Record of featureId to aggregated Entitlement, or `null` if not fetched.
|
|
137
136
|
*/
|
|
138
|
-
|
|
137
|
+
getEntitlements(): EntitlementMap | null;
|
|
138
|
+
/**
|
|
139
|
+
* Returns the raw API items for a specific featureId (un-aggregated).
|
|
140
|
+
*
|
|
141
|
+
* @param featureId The unique identifier of the feature.
|
|
142
|
+
* @returns Array of raw entitlement items for the given featureId, or `null` if not fetched.
|
|
143
|
+
*/
|
|
144
|
+
getRawEntitlement(featureId: string): RawEntitlement[] | null;
|
|
145
|
+
/**
|
|
146
|
+
* Returns the full raw API response including the customer_id wrapper.
|
|
147
|
+
*
|
|
148
|
+
* @returns The raw API response `{ customer_id, entitlements }`, or `null` if not fetched.
|
|
149
|
+
*/
|
|
150
|
+
getRawEntitlements(): RawEntitlementsApiResponse | null;
|
|
139
151
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* @
|
|
152
|
+
* Checks if the user has access to a specific feature based on cached entitlements.
|
|
153
|
+
*
|
|
154
|
+
* @param featureId The identifier of the feature.
|
|
155
|
+
* @returns `true` if access is granted, `false` otherwise.
|
|
143
156
|
*/
|
|
144
|
-
|
|
157
|
+
hasAccess(featureId: string): boolean;
|
|
145
158
|
/**
|
|
146
159
|
* Indicates if an entitlement fetch operation is currently in progress.
|
|
147
160
|
* @returns `true` if a fetch is in progress, `false` otherwise.
|
|
@@ -152,9 +165,17 @@ declare class KelviqClient {
|
|
|
152
165
|
* @returns An `Error` object if the last fetch failed, otherwise `null`.
|
|
153
166
|
*/
|
|
154
167
|
getLastError(): Error | null;
|
|
168
|
+
/**
|
|
169
|
+
* Returns a promise that resolves when the initial fetch (triggered by `initializeAndFetch: true`) completes.
|
|
170
|
+
* If no initial fetch was triggered, resolves immediately.
|
|
171
|
+
*/
|
|
172
|
+
ready(): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* @internal Sets the ready promise. Called by the factory when initializeAndFetch is true.
|
|
175
|
+
*/
|
|
176
|
+
_setReadyPromise(promise: Promise<void>): void;
|
|
155
177
|
/**
|
|
156
178
|
* Clears the local entitlement cache and resets loading and error states.
|
|
157
|
-
* After calling this, `fetchAllEntitlements()` will need to be called again to populate the cache.
|
|
158
179
|
*/
|
|
159
180
|
clearCache(): void;
|
|
160
181
|
}
|
|
@@ -165,8 +186,7 @@ declare class KelviqClient {
|
|
|
165
186
|
* @param customApiRequest Optional: A custom function to handle API requests, useful for testing or custom HTTP clients.
|
|
166
187
|
* @returns An instance of KelviqClient.
|
|
167
188
|
*/
|
|
168
|
-
declare function kelviqSDK(
|
|
169
|
-
options: KelviqClientOptions, customApiRequest?: typeof apiRequest): KelviqClient;
|
|
189
|
+
declare function kelviqSDK(options: KelviqClientOptions, customApiRequest?: typeof apiRequest): KelviqClient;
|
|
170
190
|
|
|
171
191
|
export {
|
|
172
192
|
kelviqSDK as default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var kelviqSDK=function(){"use strict";var
|
|
1
|
+
var kelviqSDK=function(){"use strict";var U=Object.defineProperty;var _=(y,p,q)=>p in y?U(y,p,{enumerable:!0,configurable:!0,writable:!0,value:q}):y[p]=q;var E=(y,p,q)=>_(y,typeof p!="symbol"?p+"":p,q);function y(a){const e={};if(!a||!Array.isArray(a.entitlements))return console.warn("[Kelviq SDK] transformApiEntitlements: Invalid or empty entitlements array in API response.",a),e;const s={};a.entitlements.forEach(t=>{if(!t||typeof t.featureId!="string"||typeof t.featureType!="string"){console.warn("[Kelviq SDK] transformApiEntitlements: Skipping invalid raw entitlement object:",t);return}s[t.featureId]||(s[t.featureId]=[]),s[t.featureId].push(t)});for(const t of Object.keys(s)){const c=s[t],u=c[0].featureType;let A=0,r=null,f=null,l=!1,h=!1;if(u==="METER"){let n=!0;for(const i of c)A+=typeof i.currentUsage=="number"?i.currentUsage:0,typeof i.usageLimit=="number"&&(r=(r??0)+i.usageLimit,n=!1);n&&(r=null),f=r!==null?r-A:null,l=c.some(i=>i.hardLimit===!0),h=f===null||f>0}else if(u==="CUSTOMIZABLE"){const n=c[0];r=typeof n.usageLimit=="number"?n.usageLimit:null,A=typeof n.currentUsage=="number"?n.currentUsage:0,f=typeof n.remaining=="number"?n.remaining:null,l=n.hardLimit===!0,h=c.some(i=>i.hasAccess)}else if(u==="BOOLEAN")h=c.some(n=>n.hasAccess);else{console.warn(`[Kelviq SDK] transformApiEntitlements: Encountered unknown featureType: '${u}' for feature '${t}'. This entitlement will be ignored.`);continue}const R={featureId:t,featureType:u,hasAccess:h,hardLimit:l,currentUsage:A,usageLimit:r,remaining:f,items:c};e[t]=R}return e}const p="GET",q=5e3,K=3,P=1e3;function C(a){const{method:e=p,headers:s={},body:t,timeout:c=q,maxRetries:u=K,backoffBaseDelay:A=P,queryParams:r,accessToken:f}=a;let l=a.url,h=0;return new Promise((R,n)=>{if(r){const m=new URLSearchParams;for(const g in r)r[g]!==void 0&&m.append(g,String(r[g]));m.toString()&&(l=l+(l.includes("?")?"&":"?")+m.toString())}function i(){const m=new XMLHttpRequest;m.open(e,l,!0),m.timeout=c;const g={Accept:"application/json","X-Requested-With":"XMLHttpRequest",...s};e!=="GET"&&t&&typeof t=="object"&&!(t instanceof FormData)&&(g["Content-Type"]||(g["Content-Type"]="application/json")),f&&(g.Authorization=`Bearer ${f}`);for(const[o,d]of Object.entries(g))m.setRequestHeader(o,d);m.onload=function(){const{status:o,statusText:d,responseText:F}=m;if(o>=200&&o<300)try{const v=F?JSON.parse(F):{};R({status:o,statusText:d,data:v})}catch(v){const w=v instanceof Error?v:new Error(String(v));console.error(`Kelviq SDK (apiRequest): Invalid JSON response for URL ${l}. Error: ${w.message}. Response: ${F}`),n(new Error(`Invalid JSON response: ${w.message}`))}else S(`Request to ${l} failed with status ${o} ${d}. Response: ${F}`)},m.onerror=()=>S(`Network error for URL ${l}. The request could not be completed.`),m.ontimeout=()=>S(`Request to ${l} timed out.`);function S(o){if(h<u){h++;const d=Math.min(A*Math.pow(2,h-1),3e4);console.warn(`Kelviq SDK (apiRequest): Retrying request to ${l}. Attempt ${h}/${u}. Error: ${o}. Retrying in ${d}ms.`),setTimeout(i,d)}else n(new Error(`${o} (Max retries ${u} reached)`))}try{let o=t;t&&typeof t=="object"&&!(t instanceof FormData)&&g["Content-Type"]==="application/json"&&(o=JSON.stringify(t)),m.send(o)}catch(o){const d=o instanceof Error?o:new Error(String(o));console.error(`Kelviq SDK (apiRequest): Error sending request to ${l}.`,d),n(new Error(`Failed to send request: ${d.message}`))}}i()})}const L="https://edge.api.kelviq.com/api/v1/",D="https://edge.sandboxapi.kelviq.com/api/v1/",T="entitlements/";class ${constructor(e,s){E(this,"options");E(this,"entitlementsCache",null);E(this,"rawApiResponse",null);E(this,"isFetching",!1);E(this,"inFlightPromise",null);E(this,"readyPromise",null);E(this,"lastFetchError",null);E(this,"apiRequestService");if(!e||!e.customerId)throw new Error("[Kelviq SDK] KelviqClient: CustomerId is required in options.");this.options={...e,apiConfig:e.apiConfig||{},entitlementsPath:e.entitlementsPath||T},this.apiRequestService=s||C}async fetchAllEntitlements(e=!1){if(this.inFlightPromise&&!e)return this.inFlightPromise;if(this.entitlementsCache&&!e)return Promise.resolve(this.entitlementsCache);this.isFetching=!0,this.lastFetchError=null;const{apiUrl:s=this.options.environment==="sandbox"?D:L,entitlementsPath:t,customerId:c,accessToken:u,apiConfig:A,onError:r}=this.options,f=s.replace(/\/$/,""),l=t.replace(/^\//,""),h=`${f}/${l}`,R={customer_id:c};return this.inFlightPromise=this.apiRequestService({url:h,method:"GET",queryParams:R,accessToken:u,...A||{}}).then(n=>{if(n&&n.data&&Array.isArray(n.data.entitlements)){const i=n.data;return this.rawApiResponse={customerId:i.customer_id??i.customerId,entitlements:i.entitlements},this.entitlementsCache=y(n.data),this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache}else{const i=new Error("[Kelviq SDK] Received empty, malformed, or invalid data structure from entitlements API.");throw this.lastFetchError=i,this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache=null,this.rawApiResponse=null,r&&r(i),i}}).catch(n=>{const i=n instanceof Error?n:new Error(String(n));throw this.lastFetchError=i,this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache=null,this.rawApiResponse=null,r&&r(i),i}),this.inFlightPromise}getEntitlement(e){return this.entitlementsCache?this.entitlementsCache[e]??null:(console.warn(`[Kelviq SDK] getEntitlement: Entitlements not fetched or cache is empty for featureId '${e}'. Call fetchAllEntitlements() first.`),null)}getEntitlements(){return this.entitlementsCache}getRawEntitlement(e){return this.rawApiResponse?this.rawApiResponse.entitlements.filter(s=>s.featureId===e):(console.warn(`[Kelviq SDK] getRawEntitlement: Entitlements not fetched for featureId '${e}'. Call fetchAllEntitlements() first.`),null)}getRawEntitlements(){return this.rawApiResponse}hasAccess(e){if(!this.entitlementsCache)return console.warn(`[Kelviq SDK] hasAccess: Entitlements not fetched yet for featureId '${e}'. Call fetchAllEntitlements() first. Returning false.`),!1;const s=this.entitlementsCache[e];return s?s.hasAccess:!1}isLoading(){return this.isFetching}getLastError(){return this.lastFetchError}ready(){return this.readyPromise??Promise.resolve()}_setReadyPromise(e){this.readyPromise=e}clearCache(){this.entitlementsCache=null,this.rawApiResponse=null,this.isFetching=!1,this.lastFetchError=null,console.log("[Kelviq SDK] Entitlement cache cleared.")}}function I(a,e){const s={...a,initializeAndFetch:a.initializeAndFetch??!0,apiConfig:a.apiConfig||{},environment:a.environment||"production",entitlementsPath:a.entitlementsPath||T},t=new $(s,e);if(s.initializeAndFetch===!0){const c=t.fetchAllEntitlements().catch(u=>{console.error("[Kelviq SDK] Initial fetch on client creation failed:",u.message)});t._setReadyPromise(c.then(()=>{}))}return t}return I}();
|
package/dist/kelviq-js-sdk.js
CHANGED
|
@@ -1,257 +1,270 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var T = (r, e, i) => e in r ? S(r, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[e] = i;
|
|
3
|
+
var g = (r, e, i) => T(r, typeof e != "symbol" ? e + "" : e, i);
|
|
4
|
+
function w(r) {
|
|
5
|
+
const e = {};
|
|
6
|
+
if (!r || !Array.isArray(r.entitlements))
|
|
7
|
+
return console.warn(
|
|
8
|
+
"[Kelviq SDK] transformApiEntitlements: Invalid or empty entitlements array in API response.",
|
|
9
|
+
r
|
|
10
|
+
), e;
|
|
11
|
+
const i = {};
|
|
12
|
+
r.entitlements.forEach((t) => {
|
|
13
|
+
if (!t || typeof t.featureId != "string" || typeof t.featureType != "string") {
|
|
11
14
|
console.warn(
|
|
12
15
|
"[Kelviq SDK] transformApiEntitlements: Skipping invalid raw entitlement object:",
|
|
13
|
-
|
|
16
|
+
t
|
|
14
17
|
);
|
|
15
18
|
return;
|
|
16
19
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
n =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
else if (
|
|
28
|
-
const
|
|
29
|
-
n =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
} else if (e.featureType === "METER") {
|
|
35
|
-
const i = e, p = typeof i.usageLimit == "number" ? i.usageLimit : null, o = typeof i.currentUsage == "number" ? i.currentUsage : 0;
|
|
36
|
-
n = {
|
|
37
|
-
...m,
|
|
38
|
-
type: "metered",
|
|
39
|
-
limit: p,
|
|
40
|
-
used: o,
|
|
41
|
-
remaining: p !== null ? p - o : null,
|
|
42
|
-
resetAt: i.resetAt,
|
|
43
|
-
hardLimit: i.hardLimit
|
|
44
|
-
};
|
|
45
|
-
} else {
|
|
46
|
-
const i = e;
|
|
20
|
+
i[t.featureId] || (i[t.featureId] = []), i[t.featureId].push(t);
|
|
21
|
+
});
|
|
22
|
+
for (const t of Object.keys(i)) {
|
|
23
|
+
const c = i[t], u = c[0].featureType;
|
|
24
|
+
let E = 0, l = null, f = null, o = !1, h = !1;
|
|
25
|
+
if (u === "METER") {
|
|
26
|
+
let n = !0;
|
|
27
|
+
for (const s of c)
|
|
28
|
+
E += typeof s.currentUsage == "number" ? s.currentUsage : 0, typeof s.usageLimit == "number" && (l = (l ?? 0) + s.usageLimit, n = !1);
|
|
29
|
+
n && (l = null), f = l !== null ? l - E : null, o = c.some((s) => s.hardLimit === !0), h = f === null || f > 0;
|
|
30
|
+
} else if (u === "CUSTOMIZABLE") {
|
|
31
|
+
const n = c[0];
|
|
32
|
+
l = typeof n.usageLimit == "number" ? n.usageLimit : null, E = typeof n.currentUsage == "number" ? n.currentUsage : 0, f = typeof n.remaining == "number" ? n.remaining : null, o = n.hardLimit === !0, h = c.some((s) => s.hasAccess);
|
|
33
|
+
} else if (u === "BOOLEAN")
|
|
34
|
+
h = c.some((n) => n.hasAccess);
|
|
35
|
+
else {
|
|
47
36
|
console.warn(
|
|
48
|
-
`[Kelviq SDK] transformApiEntitlements: Encountered unknown
|
|
37
|
+
`[Kelviq SDK] transformApiEntitlements: Encountered unknown featureType: '${u}' for feature '${t}'. This entitlement will be ignored.`
|
|
49
38
|
);
|
|
39
|
+
continue;
|
|
50
40
|
}
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
const A = {
|
|
42
|
+
featureId: t,
|
|
43
|
+
featureType: u,
|
|
44
|
+
hasAccess: h,
|
|
45
|
+
hardLimit: o,
|
|
46
|
+
currentUsage: E,
|
|
47
|
+
usageLimit: l,
|
|
48
|
+
remaining: f,
|
|
49
|
+
items: c
|
|
50
|
+
};
|
|
51
|
+
e[t] = A;
|
|
52
|
+
}
|
|
53
|
+
return e;
|
|
53
54
|
}
|
|
54
|
-
const
|
|
55
|
-
function
|
|
55
|
+
const P = "GET", C = 5e3, K = 3, L = 1e3;
|
|
56
|
+
function $(r) {
|
|
56
57
|
const {
|
|
57
|
-
method:
|
|
58
|
-
headers:
|
|
58
|
+
method: e = P,
|
|
59
|
+
headers: i = {},
|
|
59
60
|
// Renamed to avoid conflict with internal 'headers' variable
|
|
60
|
-
body:
|
|
61
|
-
timeout:
|
|
62
|
-
maxRetries:
|
|
63
|
-
backoffBaseDelay:
|
|
64
|
-
queryParams:
|
|
65
|
-
accessToken:
|
|
61
|
+
body: t,
|
|
62
|
+
timeout: c = C,
|
|
63
|
+
maxRetries: u = K,
|
|
64
|
+
backoffBaseDelay: E = L,
|
|
65
|
+
queryParams: l,
|
|
66
|
+
accessToken: f
|
|
66
67
|
// Use the direct accessToken
|
|
67
68
|
} = r;
|
|
68
|
-
let
|
|
69
|
-
return new Promise((
|
|
70
|
-
if (
|
|
71
|
-
const
|
|
72
|
-
for (const
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
let o = r.url, h = 0;
|
|
70
|
+
return new Promise((A, n) => {
|
|
71
|
+
if (l) {
|
|
72
|
+
const m = new URLSearchParams();
|
|
73
|
+
for (const p in l)
|
|
74
|
+
l[p] !== void 0 && m.append(p, String(l[p]));
|
|
75
|
+
m.toString() && (o = o + (o.includes("?") ? "&" : "?") + m.toString());
|
|
75
76
|
}
|
|
76
|
-
function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const
|
|
77
|
+
function s() {
|
|
78
|
+
const m = new XMLHttpRequest();
|
|
79
|
+
m.open(e, o, !0), m.timeout = c;
|
|
80
|
+
const p = {
|
|
80
81
|
Accept: "application/json",
|
|
81
82
|
"X-Requested-With": "XMLHttpRequest",
|
|
82
|
-
...
|
|
83
|
+
...i
|
|
83
84
|
// Apply custom headers, allowing them to override defaults
|
|
84
85
|
};
|
|
85
|
-
|
|
86
|
-
for (const [
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const { status:
|
|
90
|
-
if (
|
|
86
|
+
e !== "GET" && t && typeof t == "object" && !(t instanceof FormData) && (p["Content-Type"] || (p["Content-Type"] = "application/json")), f && (p.Authorization = `Bearer ${f}`);
|
|
87
|
+
for (const [a, d] of Object.entries(p))
|
|
88
|
+
m.setRequestHeader(a, d);
|
|
89
|
+
m.onload = function() {
|
|
90
|
+
const { status: a, statusText: d, responseText: R } = m;
|
|
91
|
+
if (a >= 200 && a < 300)
|
|
91
92
|
try {
|
|
92
|
-
const y =
|
|
93
|
-
|
|
93
|
+
const y = R ? JSON.parse(R) : {};
|
|
94
|
+
A({ status: a, statusText: d, data: y });
|
|
94
95
|
} catch (y) {
|
|
95
|
-
const
|
|
96
|
+
const v = y instanceof Error ? y : new Error(String(y));
|
|
96
97
|
console.error(
|
|
97
|
-
`Kelviq SDK (apiRequest): Invalid JSON response for URL ${
|
|
98
|
-
),
|
|
98
|
+
`Kelviq SDK (apiRequest): Invalid JSON response for URL ${o}. Error: ${v.message}. Response: ${R}`
|
|
99
|
+
), n(new Error(`Invalid JSON response: ${v.message}`));
|
|
99
100
|
}
|
|
100
101
|
else
|
|
101
|
-
|
|
102
|
-
`Request to ${
|
|
102
|
+
q(
|
|
103
|
+
`Request to ${o} failed with status ${a} ${d}. Response: ${R}`
|
|
103
104
|
);
|
|
104
|
-
},
|
|
105
|
-
`Network error for URL ${
|
|
106
|
-
),
|
|
107
|
-
function
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
105
|
+
}, m.onerror = () => q(
|
|
106
|
+
`Network error for URL ${o}. The request could not be completed.`
|
|
107
|
+
), m.ontimeout = () => q(`Request to ${o} timed out.`);
|
|
108
|
+
function q(a) {
|
|
109
|
+
if (h < u) {
|
|
110
|
+
h++;
|
|
111
|
+
const d = Math.min(
|
|
112
|
+
E * Math.pow(2, h - 1),
|
|
112
113
|
3e4
|
|
113
114
|
// Cap retry delay at 30 seconds
|
|
114
115
|
);
|
|
115
116
|
console.warn(
|
|
116
|
-
`Kelviq SDK (apiRequest): Retrying request to ${
|
|
117
|
-
), setTimeout(
|
|
117
|
+
`Kelviq SDK (apiRequest): Retrying request to ${o}. Attempt ${h}/${u}. Error: ${a}. Retrying in ${d}ms.`
|
|
118
|
+
), setTimeout(s, d);
|
|
118
119
|
} else
|
|
119
|
-
|
|
120
|
-
new Error(`${
|
|
120
|
+
n(
|
|
121
|
+
new Error(`${a} (Max retries ${u} reached)`)
|
|
121
122
|
);
|
|
122
123
|
}
|
|
123
124
|
try {
|
|
124
|
-
let
|
|
125
|
-
|
|
126
|
-
} catch (
|
|
127
|
-
const
|
|
125
|
+
let a = t;
|
|
126
|
+
t && typeof t == "object" && !(t instanceof FormData) && p["Content-Type"] === "application/json" && (a = JSON.stringify(t)), m.send(a);
|
|
127
|
+
} catch (a) {
|
|
128
|
+
const d = a instanceof Error ? a : new Error(String(a));
|
|
128
129
|
console.error(
|
|
129
|
-
`Kelviq SDK (apiRequest): Error sending request to ${
|
|
130
|
-
|
|
131
|
-
),
|
|
130
|
+
`Kelviq SDK (apiRequest): Error sending request to ${o}.`,
|
|
131
|
+
d
|
|
132
|
+
), n(new Error(`Failed to send request: ${d.message}`));
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
+
s();
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
|
-
const
|
|
138
|
-
class
|
|
138
|
+
const D = "https://edge.api.kelviq.com/api/v1/", I = "https://edge.sandboxapi.kelviq.com/api/v1/", F = "entitlements/";
|
|
139
|
+
class U {
|
|
139
140
|
/**
|
|
140
141
|
* Creates an instance of KelviqClient.
|
|
141
|
-
* Prefer using the `
|
|
142
|
+
* Prefer using the `kelviqSDK` factory function for instantiation.
|
|
142
143
|
* @internal
|
|
143
144
|
* @param options Configuration options for the client.
|
|
144
145
|
* @param customApiRequest Optional: A custom function to handle API requests, conforming to ApiRequestConfig and ApiResponse.
|
|
145
146
|
*/
|
|
146
|
-
constructor(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
constructor(e, i) {
|
|
148
|
+
g(this, "options");
|
|
149
|
+
g(this, "entitlementsCache", null);
|
|
150
|
+
g(this, "rawApiResponse", null);
|
|
151
|
+
g(this, "isFetching", !1);
|
|
152
|
+
g(this, "inFlightPromise", null);
|
|
153
|
+
g(this, "readyPromise", null);
|
|
154
|
+
g(this, "lastFetchError", null);
|
|
155
|
+
g(this, "apiRequestService");
|
|
156
|
+
if (!e || !e.customerId)
|
|
153
157
|
throw new Error(
|
|
154
158
|
"[Kelviq SDK] KelviqClient: CustomerId is required in options."
|
|
155
159
|
);
|
|
156
160
|
this.options = {
|
|
157
|
-
...
|
|
158
|
-
apiConfig:
|
|
159
|
-
entitlementsPath:
|
|
160
|
-
}, this.apiRequestService =
|
|
161
|
+
...e,
|
|
162
|
+
apiConfig: e.apiConfig || {},
|
|
163
|
+
entitlementsPath: e.entitlementsPath || F
|
|
164
|
+
}, this.apiRequestService = i || $;
|
|
161
165
|
}
|
|
162
166
|
/**
|
|
163
|
-
* Fetches all entitlements from the API for the configured
|
|
167
|
+
* Fetches all entitlements from the API for the configured customerId,
|
|
164
168
|
* processes them, and caches the result.
|
|
165
169
|
* Subsequent calls will return cached data unless `forceRefresh` is true.
|
|
166
170
|
*
|
|
167
171
|
* @param forceRefresh - If true, fetches from the API even if data is already cached. Defaults to false.
|
|
168
|
-
* @returns A promise that resolves to the map of entitlements
|
|
172
|
+
* @returns A promise that resolves to the map of aggregated entitlements.
|
|
169
173
|
* @throws Will throw an error if the API request fails after all retries.
|
|
170
174
|
*/
|
|
171
|
-
async fetchAllEntitlements(
|
|
172
|
-
if (this.
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
);
|
|
176
|
-
if (this.entitlementsCache && !t)
|
|
175
|
+
async fetchAllEntitlements(e = !1) {
|
|
176
|
+
if (this.inFlightPromise && !e)
|
|
177
|
+
return this.inFlightPromise;
|
|
178
|
+
if (this.entitlementsCache && !e)
|
|
177
179
|
return Promise.resolve(this.entitlementsCache);
|
|
178
180
|
this.isFetching = !0, this.lastFetchError = null;
|
|
179
181
|
const {
|
|
180
|
-
apiUrl:
|
|
181
|
-
entitlementsPath:
|
|
182
|
-
customerId:
|
|
183
|
-
accessToken:
|
|
184
|
-
apiConfig:
|
|
185
|
-
onError:
|
|
186
|
-
} = this.options,
|
|
187
|
-
customer_id:
|
|
182
|
+
apiUrl: i = this.options.environment === "sandbox" ? I : D,
|
|
183
|
+
entitlementsPath: t,
|
|
184
|
+
customerId: c,
|
|
185
|
+
accessToken: u,
|
|
186
|
+
apiConfig: E,
|
|
187
|
+
onError: l
|
|
188
|
+
} = this.options, f = i.replace(/\/$/, ""), o = t.replace(/^\//, ""), h = `${f}/${o}`, A = {
|
|
189
|
+
customer_id: c
|
|
188
190
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
191
|
+
return this.inFlightPromise = this.apiRequestService({
|
|
192
|
+
url: h,
|
|
193
|
+
method: "GET",
|
|
194
|
+
queryParams: A,
|
|
195
|
+
accessToken: u,
|
|
196
|
+
...E || {}
|
|
197
|
+
}).then((n) => {
|
|
198
|
+
if (n && n.data && Array.isArray(n.data.entitlements)) {
|
|
199
|
+
const s = n.data;
|
|
200
|
+
return this.rawApiResponse = {
|
|
201
|
+
customerId: s.customer_id ?? s.customerId,
|
|
202
|
+
entitlements: s.entitlements
|
|
203
|
+
}, this.entitlementsCache = w(n.data), this.isFetching = !1, this.inFlightPromise = null, this.entitlementsCache;
|
|
204
|
+
} else {
|
|
205
|
+
const s = new Error(
|
|
203
206
|
"[Kelviq SDK] Received empty, malformed, or invalid data structure from entitlements API."
|
|
204
207
|
);
|
|
205
|
-
throw this.lastFetchError =
|
|
208
|
+
throw this.lastFetchError = s, this.isFetching = !1, this.inFlightPromise = null, this.entitlementsCache = null, this.rawApiResponse = null, l && l(s), s;
|
|
206
209
|
}
|
|
207
|
-
}
|
|
208
|
-
const
|
|
209
|
-
throw this.lastFetchError =
|
|
210
|
-
}
|
|
210
|
+
}).catch((n) => {
|
|
211
|
+
const s = n instanceof Error ? n : new Error(String(n));
|
|
212
|
+
throw this.lastFetchError = s, this.isFetching = !1, this.inFlightPromise = null, this.entitlementsCache = null, this.rawApiResponse = null, l && l(s), s;
|
|
213
|
+
}), this.inFlightPromise;
|
|
211
214
|
}
|
|
212
215
|
/**
|
|
213
|
-
* Retrieves a specific entitlement from the local cache.
|
|
214
|
-
* Note: `fetchAllEntitlements()` must be called successfully at least once before using this method,
|
|
215
|
-
* or the cache will be empty.
|
|
216
|
+
* Retrieves a specific aggregated entitlement from the local cache.
|
|
216
217
|
*
|
|
217
|
-
* @param featureId The unique
|
|
218
|
-
* @
|
|
219
|
-
* @returns The processed entitlement object if found in the cache and the type matches, otherwise `null`.
|
|
220
|
-
* Logs a warning if entitlements haven't been fetched or if a type mismatch occurs.
|
|
218
|
+
* @param featureId The unique identifier of the feature.
|
|
219
|
+
* @returns The aggregated Entitlement object if found, otherwise `null`.
|
|
221
220
|
*/
|
|
222
|
-
getEntitlement(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
221
|
+
getEntitlement(e) {
|
|
222
|
+
return this.entitlementsCache ? this.entitlementsCache[e] ?? null : (console.warn(
|
|
223
|
+
`[Kelviq SDK] getEntitlement: Entitlements not fetched or cache is empty for featureId '${e}'. Call fetchAllEntitlements() first.`
|
|
224
|
+
), null);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Returns all aggregated entitlements as a map keyed by featureId.
|
|
228
|
+
*
|
|
229
|
+
* @returns Record of featureId to aggregated Entitlement, or `null` if not fetched.
|
|
230
|
+
*/
|
|
231
|
+
getEntitlements() {
|
|
232
|
+
return this.entitlementsCache;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Returns the raw API items for a specific featureId (un-aggregated).
|
|
236
|
+
*
|
|
237
|
+
* @param featureId The unique identifier of the feature.
|
|
238
|
+
* @returns Array of raw entitlement items for the given featureId, or `null` if not fetched.
|
|
239
|
+
*/
|
|
240
|
+
getRawEntitlement(e) {
|
|
241
|
+
return this.rawApiResponse ? this.rawApiResponse.entitlements.filter(
|
|
242
|
+
(i) => i.featureId === e
|
|
243
|
+
) : (console.warn(
|
|
244
|
+
`[Kelviq SDK] getRawEntitlement: Entitlements not fetched for featureId '${e}'. Call fetchAllEntitlements() first.`
|
|
245
|
+
), null);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Returns the full raw API response including the customer_id wrapper.
|
|
249
|
+
*
|
|
250
|
+
* @returns The raw API response `{ customer_id, entitlements }`, or `null` if not fetched.
|
|
251
|
+
*/
|
|
252
|
+
getRawEntitlements() {
|
|
253
|
+
return this.rawApiResponse;
|
|
231
254
|
}
|
|
232
255
|
/**
|
|
233
256
|
* Checks if the user has access to a specific feature based on cached entitlements.
|
|
234
|
-
* Note: `fetchAllEntitlements()` should be called successfully at least once before using this method.
|
|
235
257
|
*
|
|
236
|
-
* @param featureId The
|
|
237
|
-
* @returns `true` if access is granted, `false`
|
|
238
|
-
* Logs a warning and returns `false` if entitlements haven't been fetched.
|
|
258
|
+
* @param featureId The identifier of the feature.
|
|
259
|
+
* @returns `true` if access is granted, `false` otherwise.
|
|
239
260
|
*/
|
|
240
|
-
hasAccess(
|
|
261
|
+
hasAccess(e) {
|
|
241
262
|
if (!this.entitlementsCache)
|
|
242
263
|
return console.warn(
|
|
243
|
-
`[Kelviq SDK] hasAccess: Entitlements not fetched yet for featureId '${
|
|
264
|
+
`[Kelviq SDK] hasAccess: Entitlements not fetched yet for featureId '${e}'. Call fetchAllEntitlements() first. Returning false.`
|
|
244
265
|
), !1;
|
|
245
|
-
const
|
|
246
|
-
return
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Returns the currently cached map of all entitlements.
|
|
250
|
-
* `fetchAllEntitlements()` should be called successfully at least once.
|
|
251
|
-
* @returns The EntitlementMap (a record of featureKey to entitlement data) or `null` if not fetched or an error occurred during the last fetch.
|
|
252
|
-
*/
|
|
253
|
-
getAllEntitlements() {
|
|
254
|
-
return this.entitlementsCache;
|
|
266
|
+
const i = this.entitlementsCache[e];
|
|
267
|
+
return i ? i.hasAccess : !1;
|
|
255
268
|
}
|
|
256
269
|
/**
|
|
257
270
|
* Indicates if an entitlement fetch operation is currently in progress.
|
|
@@ -267,29 +280,46 @@ class P {
|
|
|
267
280
|
getLastError() {
|
|
268
281
|
return this.lastFetchError;
|
|
269
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Returns a promise that resolves when the initial fetch (triggered by `initializeAndFetch: true`) completes.
|
|
285
|
+
* If no initial fetch was triggered, resolves immediately.
|
|
286
|
+
*/
|
|
287
|
+
ready() {
|
|
288
|
+
return this.readyPromise ?? Promise.resolve();
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* @internal Sets the ready promise. Called by the factory when initializeAndFetch is true.
|
|
292
|
+
*/
|
|
293
|
+
_setReadyPromise(e) {
|
|
294
|
+
this.readyPromise = e;
|
|
295
|
+
}
|
|
270
296
|
/**
|
|
271
297
|
* Clears the local entitlement cache and resets loading and error states.
|
|
272
|
-
* After calling this, `fetchAllEntitlements()` will need to be called again to populate the cache.
|
|
273
298
|
*/
|
|
274
299
|
clearCache() {
|
|
275
|
-
this.entitlementsCache = null, this.isFetching = !1, this.lastFetchError = null, console.log("[Kelviq SDK] Entitlement cache cleared.");
|
|
300
|
+
this.entitlementsCache = null, this.rawApiResponse = null, this.isFetching = !1, this.lastFetchError = null, console.log("[Kelviq SDK] Entitlement cache cleared.");
|
|
276
301
|
}
|
|
277
302
|
}
|
|
278
|
-
function
|
|
279
|
-
const
|
|
303
|
+
function b(r, e) {
|
|
304
|
+
const i = {
|
|
280
305
|
...r,
|
|
281
|
-
initializeAndFetch: r.initializeAndFetch
|
|
306
|
+
initializeAndFetch: r.initializeAndFetch ?? !0,
|
|
282
307
|
apiConfig: r.apiConfig || {},
|
|
283
308
|
environment: r.environment || "production",
|
|
284
|
-
entitlementsPath: r.entitlementsPath ||
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
entitlementsPath: r.entitlementsPath || F
|
|
310
|
+
}, t = new U(i, e);
|
|
311
|
+
if (i.initializeAndFetch === !0) {
|
|
312
|
+
const c = t.fetchAllEntitlements().catch((u) => {
|
|
313
|
+
console.error(
|
|
314
|
+
"[Kelviq SDK] Initial fetch on client creation failed:",
|
|
315
|
+
u.message
|
|
316
|
+
);
|
|
317
|
+
});
|
|
318
|
+
t._setReadyPromise(c.then(() => {
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
return t;
|
|
292
322
|
}
|
|
293
323
|
export {
|
|
294
|
-
|
|
324
|
+
b as default
|
|
295
325
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(f,
|
|
1
|
+
(function(f,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(f=typeof globalThis<"u"?globalThis:f||self,f.kelviqSDK=c())})(this,function(){"use strict";var U=Object.defineProperty;var _=(f,c,q)=>c in f?U(f,c,{enumerable:!0,configurable:!0,writable:!0,value:q}):f[c]=q;var A=(f,c,q)=>_(f,typeof c!="symbol"?c+"":c,q);function f(a){const e={};if(!a||!Array.isArray(a.entitlements))return console.warn("[Kelviq SDK] transformApiEntitlements: Invalid or empty entitlements array in API response.",a),e;const s={};a.entitlements.forEach(t=>{if(!t||typeof t.featureId!="string"||typeof t.featureType!="string"){console.warn("[Kelviq SDK] transformApiEntitlements: Skipping invalid raw entitlement object:",t);return}s[t.featureId]||(s[t.featureId]=[]),s[t.featureId].push(t)});for(const t of Object.keys(s)){const u=s[t],m=u[0].featureType;let y=0,r=null,p=null,o=!1,d=!1;if(m==="METER"){let n=!0;for(const i of u)y+=typeof i.currentUsage=="number"?i.currentUsage:0,typeof i.usageLimit=="number"&&(r=(r??0)+i.usageLimit,n=!1);n&&(r=null),p=r!==null?r-y:null,o=u.some(i=>i.hardLimit===!0),d=p===null||p>0}else if(m==="CUSTOMIZABLE"){const n=u[0];r=typeof n.usageLimit=="number"?n.usageLimit:null,y=typeof n.currentUsage=="number"?n.currentUsage:0,p=typeof n.remaining=="number"?n.remaining:null,o=n.hardLimit===!0,d=u.some(i=>i.hasAccess)}else if(m==="BOOLEAN")d=u.some(n=>n.hasAccess);else{console.warn(`[Kelviq SDK] transformApiEntitlements: Encountered unknown featureType: '${m}' for feature '${t}'. This entitlement will be ignored.`);continue}const R={featureId:t,featureType:m,hasAccess:d,hardLimit:o,currentUsage:y,usageLimit:r,remaining:p,items:u};e[t]=R}return e}const c="GET",q=5e3,K=3,P=1e3;function C(a){const{method:e=c,headers:s={},body:t,timeout:u=q,maxRetries:m=K,backoffBaseDelay:y=P,queryParams:r,accessToken:p}=a;let o=a.url,d=0;return new Promise((R,n)=>{if(r){const h=new URLSearchParams;for(const E in r)r[E]!==void 0&&h.append(E,String(r[E]));h.toString()&&(o=o+(o.includes("?")?"&":"?")+h.toString())}function i(){const h=new XMLHttpRequest;h.open(e,o,!0),h.timeout=u;const E={Accept:"application/json","X-Requested-With":"XMLHttpRequest",...s};e!=="GET"&&t&&typeof t=="object"&&!(t instanceof FormData)&&(E["Content-Type"]||(E["Content-Type"]="application/json")),p&&(E.Authorization=`Bearer ${p}`);for(const[l,g]of Object.entries(E))h.setRequestHeader(l,g);h.onload=function(){const{status:l,statusText:g,responseText:T}=h;if(l>=200&&l<300)try{const v=T?JSON.parse(T):{};R({status:l,statusText:g,data:v})}catch(v){const w=v instanceof Error?v:new Error(String(v));console.error(`Kelviq SDK (apiRequest): Invalid JSON response for URL ${o}. Error: ${w.message}. Response: ${T}`),n(new Error(`Invalid JSON response: ${w.message}`))}else F(`Request to ${o} failed with status ${l} ${g}. Response: ${T}`)},h.onerror=()=>F(`Network error for URL ${o}. The request could not be completed.`),h.ontimeout=()=>F(`Request to ${o} timed out.`);function F(l){if(d<m){d++;const g=Math.min(y*Math.pow(2,d-1),3e4);console.warn(`Kelviq SDK (apiRequest): Retrying request to ${o}. Attempt ${d}/${m}. Error: ${l}. Retrying in ${g}ms.`),setTimeout(i,g)}else n(new Error(`${l} (Max retries ${m} reached)`))}try{let l=t;t&&typeof t=="object"&&!(t instanceof FormData)&&E["Content-Type"]==="application/json"&&(l=JSON.stringify(t)),h.send(l)}catch(l){const g=l instanceof Error?l:new Error(String(l));console.error(`Kelviq SDK (apiRequest): Error sending request to ${o}.`,g),n(new Error(`Failed to send request: ${g.message}`))}}i()})}const L="https://edge.api.kelviq.com/api/v1/",D="https://edge.sandboxapi.kelviq.com/api/v1/",S="entitlements/";class ${constructor(e,s){A(this,"options");A(this,"entitlementsCache",null);A(this,"rawApiResponse",null);A(this,"isFetching",!1);A(this,"inFlightPromise",null);A(this,"readyPromise",null);A(this,"lastFetchError",null);A(this,"apiRequestService");if(!e||!e.customerId)throw new Error("[Kelviq SDK] KelviqClient: CustomerId is required in options.");this.options={...e,apiConfig:e.apiConfig||{},entitlementsPath:e.entitlementsPath||S},this.apiRequestService=s||C}async fetchAllEntitlements(e=!1){if(this.inFlightPromise&&!e)return this.inFlightPromise;if(this.entitlementsCache&&!e)return Promise.resolve(this.entitlementsCache);this.isFetching=!0,this.lastFetchError=null;const{apiUrl:s=this.options.environment==="sandbox"?D:L,entitlementsPath:t,customerId:u,accessToken:m,apiConfig:y,onError:r}=this.options,p=s.replace(/\/$/,""),o=t.replace(/^\//,""),d=`${p}/${o}`,R={customer_id:u};return this.inFlightPromise=this.apiRequestService({url:d,method:"GET",queryParams:R,accessToken:m,...y||{}}).then(n=>{if(n&&n.data&&Array.isArray(n.data.entitlements)){const i=n.data;return this.rawApiResponse={customerId:i.customer_id??i.customerId,entitlements:i.entitlements},this.entitlementsCache=f(n.data),this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache}else{const i=new Error("[Kelviq SDK] Received empty, malformed, or invalid data structure from entitlements API.");throw this.lastFetchError=i,this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache=null,this.rawApiResponse=null,r&&r(i),i}}).catch(n=>{const i=n instanceof Error?n:new Error(String(n));throw this.lastFetchError=i,this.isFetching=!1,this.inFlightPromise=null,this.entitlementsCache=null,this.rawApiResponse=null,r&&r(i),i}),this.inFlightPromise}getEntitlement(e){return this.entitlementsCache?this.entitlementsCache[e]??null:(console.warn(`[Kelviq SDK] getEntitlement: Entitlements not fetched or cache is empty for featureId '${e}'. Call fetchAllEntitlements() first.`),null)}getEntitlements(){return this.entitlementsCache}getRawEntitlement(e){return this.rawApiResponse?this.rawApiResponse.entitlements.filter(s=>s.featureId===e):(console.warn(`[Kelviq SDK] getRawEntitlement: Entitlements not fetched for featureId '${e}'. Call fetchAllEntitlements() first.`),null)}getRawEntitlements(){return this.rawApiResponse}hasAccess(e){if(!this.entitlementsCache)return console.warn(`[Kelviq SDK] hasAccess: Entitlements not fetched yet for featureId '${e}'. Call fetchAllEntitlements() first. Returning false.`),!1;const s=this.entitlementsCache[e];return s?s.hasAccess:!1}isLoading(){return this.isFetching}getLastError(){return this.lastFetchError}ready(){return this.readyPromise??Promise.resolve()}_setReadyPromise(e){this.readyPromise=e}clearCache(){this.entitlementsCache=null,this.rawApiResponse=null,this.isFetching=!1,this.lastFetchError=null,console.log("[Kelviq SDK] Entitlement cache cleared.")}}function I(a,e){const s={...a,initializeAndFetch:a.initializeAndFetch??!0,apiConfig:a.apiConfig||{},environment:a.environment||"production",entitlementsPath:a.entitlementsPath||S},t=new $(s,e);if(s.initializeAndFetch===!0){const u=t.fetchAllEntitlements().catch(m=>{console.error("[Kelviq SDK] Initial fetch on client creation failed:",m.message)});t._setReadyPromise(u.then(()=>{}))}return t}return I});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kelviq/js-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"module": "./dist/kelviq-js-sdk.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"format:styles": "stylelint ./**/*.{css,scss} --fix",
|
|
31
31
|
"format": "npm run format:scripts && npm run format:styles",
|
|
32
32
|
"prepare": "husky && echo 'npx lint-staged' > .husky/pre-commit && git add .husky/pre-commit",
|
|
33
|
-
"uninstall-husky": "npm uninstall husky --no-save && git config --unset core.hooksPath && npx rimraf .husky"
|
|
33
|
+
"uninstall-husky": "npm uninstall husky --no-save && git config --unset core.hooksPath && npx rimraf .husky",
|
|
34
|
+
"publish:stable": "./publish.sh",
|
|
35
|
+
"publish:beta": "./publish.sh beta"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@types/jsdom": "^21.1.7",
|