@kelviq/react-sdk 2.0.0-beta → 2.0.1-beta

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.
@@ -1,5 +1,6 @@
1
1
  import { Entitlement, EntitlementMap, EntitlementsResponse } from '../types/sdk.types';
2
2
  import { AsyncState } from '../types';
3
+ import { RawSubscriptionData, RawCustomerApiResponse } from '../types/api.types';
3
4
  /** Defines the shape of the context value provided by KelviqProvider. */
4
5
  export interface KelviqContextValue {
5
6
  allEntitlements: AsyncState<EntitlementMap | null>;
@@ -14,6 +15,14 @@ export interface KelviqContextValue {
14
15
  getRawEntitlement: (featureId: string) => EntitlementsResponse | null;
15
16
  hasAccess: (featureId: string) => boolean;
16
17
  updateEntitlement: (featureId: string, data: Partial<Omit<Entitlement, 'featureId' | 'featureType' | 'items'>>) => void;
18
+ /** Subscription data for the customer. Only populated when fetchSubscriptionsOnMount is true. */
19
+ subscriptions: AsyncState<RawSubscriptionData[] | null>;
20
+ /** Manually refresh subscriptions data. */
21
+ refreshSubscriptions: () => Promise<void>;
22
+ /** Customer data. Only populated when fetchCustomerOnMount is true. */
23
+ customer: AsyncState<RawCustomerApiResponse | null>;
24
+ /** Manually refresh customer data. */
25
+ refreshCustomer: () => Promise<void>;
17
26
  isLoading: boolean;
18
27
  error: Error | null;
19
28
  customerId: string;
@@ -0,0 +1,9 @@
1
+ import { AsyncState } from '../types';
2
+ import { RawCustomerApiResponse } from '../types/api.types';
3
+ /**
4
+ * Hook to get customer data.
5
+ * Only populated when `fetchCustomerOnMount` is enabled in the provider config,
6
+ * or after calling `refreshCustomer()` from `useKelviq()`.
7
+ * @returns AsyncState containing the customer data or null.
8
+ */
9
+ export declare const useCustomer: () => AsyncState<RawCustomerApiResponse | null>;
@@ -0,0 +1,9 @@
1
+ import { AsyncState } from '../types';
2
+ import { RawSubscriptionData } from '../types/api.types';
3
+ /**
4
+ * Hook to get the customer's subscription data.
5
+ * Only populated when `fetchSubscriptionsOnMount` is enabled in the provider config,
6
+ * or after calling `refreshSubscriptions()` from `useKelviq()`.
7
+ * @returns AsyncState containing the array of subscriptions or null.
8
+ */
9
+ export declare const useSubscriptions: () => AsyncState<RawSubscriptionData[] | null>;
package/dist/index.d.ts CHANGED
@@ -9,3 +9,5 @@ export * from './hooks/useAllEntitlements';
9
9
  export * from './hooks/useBooleanEntitlement';
10
10
  export * from './hooks/useCustomizableEntitlement';
11
11
  export * from './hooks/useMeteredEntitlement';
12
+ export * from './hooks/useSubscriptions';
13
+ export * from './hooks/useCustomer';
@@ -1,164 +1,164 @@
1
- import { jsx as v, Fragment as $ } from "react/jsx-runtime";
2
- import { createContext as N, useState as P, useCallback as T, useEffect as H, useMemo as G, useContext as W } from "react";
3
- const X = "GET", z = 5e3, J = 3, V = 1e3;
4
- function Y(t) {
1
+ import { jsx as S, Fragment as M } from "react/jsx-runtime";
2
+ import { createContext as Z, useState as T, useCallback as y, useEffect as Q, useMemo as ee, useContext as te } from "react";
3
+ const re = "GET", ne = 5e3, se = 3, oe = 1e3;
4
+ function O(n) {
5
5
  const {
6
- method: r = X,
7
- headers: s = {},
6
+ method: o = re,
7
+ headers: i = {},
8
8
  // Renamed to avoid conflict with internal 'headers' variable
9
- body: e,
10
- timeout: E = z,
11
- maxRetries: A = J,
12
- backoffBaseDelay: h = V,
13
- queryParams: L,
14
- accessToken: a
9
+ body: t,
10
+ timeout: g = ne,
11
+ maxRetries: m = se,
12
+ backoffBaseDelay: L = oe,
13
+ queryParams: v,
14
+ accessToken: l
15
15
  // Use the direct accessToken
16
- } = t;
17
- let { url: f } = t, y = 0;
18
- return new Promise((K, p) => {
19
- if (L) {
20
- const o = new URLSearchParams();
21
- for (const u in L)
22
- L[u] !== void 0 && o.append(u, String(L[u]));
23
- o.toString() && (f += (f.includes("?") ? "&" : "?") + o.toString());
16
+ } = n;
17
+ let { url: f } = n, p = 0;
18
+ return new Promise((U, b) => {
19
+ if (v) {
20
+ const s = new URLSearchParams();
21
+ for (const u in v)
22
+ v[u] !== void 0 && s.append(u, String(v[u]));
23
+ s.toString() && (f += (f.includes("?") ? "&" : "?") + s.toString());
24
24
  }
25
- function g() {
26
- const o = new XMLHttpRequest();
27
- o.open(r, f, !0), o.timeout = E;
25
+ function C() {
26
+ const s = new XMLHttpRequest();
27
+ s.open(o, f, !0), s.timeout = g;
28
28
  const u = {
29
29
  Accept: "application/json",
30
30
  "X-Requested-With": "XMLHttpRequest",
31
- ...s
31
+ ...i
32
32
  // Apply custom headers, allowing them to override defaults
33
33
  };
34
- r !== "GET" && e && typeof e == "object" && !(e instanceof FormData) && (u["Content-Type"] || (u["Content-Type"] = "application/json")), a && (u.Authorization = `Bearer ${a}`);
35
- for (const [i, c] of Object.entries(u))
36
- o.setRequestHeader(i, c);
37
- o.onload = function() {
38
- const { status: i, statusText: c, responseText: U } = o;
39
- if (i >= 200 && i < 300)
34
+ o !== "GET" && t && typeof t == "object" && !(t instanceof FormData) && (u["Content-Type"] || (u["Content-Type"] = "application/json")), l && (u.Authorization = `Bearer ${l}`);
35
+ for (const [a, w] of Object.entries(u))
36
+ s.setRequestHeader(a, w);
37
+ s.onload = function() {
38
+ const { status: a, statusText: w, responseText: P } = s;
39
+ if (a >= 200 && a < 300)
40
40
  try {
41
- const w = U ? JSON.parse(U) : {};
42
- K({ status: i, statusText: c, data: w });
43
- } catch (w) {
44
- const R = w instanceof Error ? w : new Error(String(w));
41
+ const A = P ? JSON.parse(P) : {};
42
+ U({ status: a, statusText: w, data: A });
43
+ } catch (A) {
44
+ const I = A instanceof Error ? A : new Error(String(A));
45
45
  console.error(
46
- `Kelviq SDK (apiRequest): Invalid JSON response for URL ${f}. Error: ${R.message}. Response: ${U}`
47
- ), p(
46
+ `Kelviq SDK (apiRequest): Invalid JSON response for URL ${f}. Error: ${I.message}. Response: ${P}`
47
+ ), b(
48
48
  new Error(
49
- `Invalid JSON response: ${R.message}`
49
+ `Invalid JSON response: ${I.message}`
50
50
  )
51
51
  );
52
52
  }
53
53
  else
54
- S(
55
- `Request to ${f} failed with status ${i} ${c}. Response: ${U}`
54
+ h(
55
+ `Request to ${f} failed with status ${a} ${w}. Response: ${P}`
56
56
  );
57
- }, o.onerror = () => S(
57
+ }, s.onerror = () => h(
58
58
  `Network error for URL ${f}. The request could not be completed.`
59
- ), o.ontimeout = () => S(`Request to ${f} timed out.`);
60
- function S(i) {
61
- if (y < A) {
62
- y++;
63
- const c = Math.min(
64
- h * Math.pow(2, y - 1),
59
+ ), s.ontimeout = () => h(`Request to ${f} timed out.`);
60
+ function h(a) {
61
+ if (p < m) {
62
+ p++;
63
+ const w = Math.min(
64
+ L * Math.pow(2, p - 1),
65
65
  3e4
66
66
  // Cap retry delay at 30 seconds
67
67
  );
68
68
  console.warn(
69
- `Kelviq SDK (apiRequest): Retrying request to ${f}. Attempt ${y}/${A}. Error: ${i}. Retrying in ${c}ms.`
70
- ), setTimeout(g, c);
69
+ `Kelviq SDK (apiRequest): Retrying request to ${f}. Attempt ${p}/${m}. Error: ${a}. Retrying in ${w}ms.`
70
+ ), setTimeout(C, w);
71
71
  } else
72
- p(
72
+ b(
73
73
  new Error(
74
- `${i} (Max retries ${A} reached)`
74
+ `${a} (Max retries ${m} reached)`
75
75
  )
76
76
  );
77
77
  }
78
78
  try {
79
- let i = e;
80
- e && typeof e == "object" && !(e instanceof FormData) && u["Content-Type"] === "application/json" && (i = JSON.stringify(e)), o.send(i);
81
- } catch (i) {
82
- const c = i instanceof Error ? i : new Error(String(i));
79
+ let a = t;
80
+ t && typeof t == "object" && !(t instanceof FormData) && u["Content-Type"] === "application/json" && (a = JSON.stringify(t)), s.send(a);
81
+ } catch (a) {
82
+ const w = a instanceof Error ? a : new Error(String(a));
83
83
  console.error(
84
84
  `Kelviq SDK (apiRequest): Error sending request to ${f}.`,
85
- c
86
- ), p(new Error(`Failed to send request: ${c.message}`));
85
+ w
86
+ ), b(new Error(`Failed to send request: ${w.message}`));
87
87
  }
88
88
  }
89
- g();
89
+ C();
90
90
  });
91
91
  }
92
- function Z(t, r) {
93
- const s = r[0].featureType;
94
- if (s === "METER") {
95
- let e = 0, E = 0, A = !1;
96
- for (const L of r) {
97
- const a = L, f = typeof a.usageLimit == "number" ? a.usageLimit : null, y = typeof a.currentUsage == "number" ? a.currentUsage : 0;
98
- f === null && (e = null), e !== null && (e += f), E += y, a.hardLimit && (A = !0);
92
+ function ie(n, o) {
93
+ const i = o[0].featureType;
94
+ if (i === "METER") {
95
+ let t = 0, g = 0, m = !1;
96
+ for (const v of o) {
97
+ const l = v, f = typeof l.usageLimit == "number" ? l.usageLimit : null, p = typeof l.currentUsage == "number" ? l.currentUsage : 0;
98
+ f === null && (t = null), t !== null && (t += f), g += p, l.hardLimit && (m = !0);
99
99
  }
100
- const h = e !== null ? e - E : null;
100
+ const L = t !== null ? t - g : null;
101
101
  return {
102
- featureId: t,
103
- featureType: s,
104
- hasAccess: h === null || h > 0,
105
- currentUsage: E,
106
- usageLimit: e,
107
- remaining: h,
108
- hardLimit: A,
109
- items: r
102
+ featureId: n,
103
+ featureType: i,
104
+ hasAccess: L === null || L > 0,
105
+ currentUsage: g,
106
+ usageLimit: t,
107
+ remaining: L,
108
+ hardLimit: m,
109
+ items: o
110
110
  };
111
111
  }
112
- if (s === "CUSTOMIZABLE") {
113
- const e = r[0];
112
+ if (i === "CUSTOMIZABLE") {
113
+ const t = o[0];
114
114
  return {
115
- featureId: t,
116
- featureType: s,
117
- hasAccess: r.some((E) => E.hasAccess),
118
- currentUsage: typeof e.currentUsage == "number" ? e.currentUsage : 0,
119
- usageLimit: typeof e.usageLimit == "number" ? e.usageLimit : null,
120
- remaining: typeof e.remaining == "number" ? e.remaining : null,
121
- hardLimit: e.hardLimit === !0,
122
- items: r
115
+ featureId: n,
116
+ featureType: i,
117
+ hasAccess: o.some((g) => g.hasAccess),
118
+ currentUsage: typeof t.currentUsage == "number" ? t.currentUsage : 0,
119
+ usageLimit: typeof t.usageLimit == "number" ? t.usageLimit : null,
120
+ remaining: typeof t.remaining == "number" ? t.remaining : null,
121
+ hardLimit: t.hardLimit === !0,
122
+ items: o
123
123
  };
124
124
  }
125
125
  return {
126
- featureId: t,
127
- featureType: s,
128
- hasAccess: r.some((e) => e.hasAccess),
126
+ featureId: n,
127
+ featureType: i,
128
+ hasAccess: o.some((t) => t.hasAccess),
129
129
  currentUsage: 0,
130
130
  usageLimit: null,
131
131
  remaining: null,
132
132
  hardLimit: !1,
133
- items: r
133
+ items: o
134
134
  };
135
135
  }
136
- function Q(t) {
137
- const r = {};
138
- if (!t || !Array.isArray(t.entitlements))
136
+ function ae(n) {
137
+ const o = {};
138
+ if (!n || !Array.isArray(n.entitlements))
139
139
  return console.warn(
140
140
  "Kelviq SDK: Invalid or empty entitlements array in API response.",
141
- t
142
- ), r;
143
- const s = {};
144
- for (const e of t.entitlements) {
145
- if (!e || typeof e.featureId != "string" || typeof e.featureType != "string") {
141
+ n
142
+ ), o;
143
+ const i = {};
144
+ for (const t of n.entitlements) {
145
+ if (!t || typeof t.featureId != "string" || typeof t.featureType != "string") {
146
146
  console.warn(
147
147
  "Kelviq SDK: Skipping invalid raw entitlement object:",
148
- e
148
+ t
149
149
  );
150
150
  continue;
151
151
  }
152
- s[e.featureId] || (s[e.featureId] = []), s[e.featureId].push(e);
152
+ i[t.featureId] || (i[t.featureId] = []), i[t.featureId].push(t);
153
153
  }
154
- for (const e in s)
155
- r[e] = Z(
156
- e,
157
- s[e]
154
+ for (const t in i)
155
+ o[t] = ie(
156
+ t,
157
+ i[t]
158
158
  );
159
- return r;
159
+ return o;
160
160
  }
161
- const B = {
161
+ const J = {
162
162
  allEntitlements: {
163
163
  data: null,
164
164
  isLoading: !0,
@@ -177,281 +177,472 @@ const B = {
177
177
  "updateEntitlement called on default KelviqContext. Ensure KelviqProvider is properly set up."
178
178
  );
179
179
  },
180
+ subscriptions: {
181
+ data: null,
182
+ isLoading: !1,
183
+ error: null
184
+ },
185
+ refreshSubscriptions: () => (console.warn(
186
+ "refreshSubscriptions called on default KelviqContext. Ensure KelviqProvider is properly set up."
187
+ ), Promise.resolve()),
188
+ customer: {
189
+ data: null,
190
+ isLoading: !1,
191
+ error: null
192
+ },
193
+ refreshCustomer: () => (console.warn(
194
+ "refreshCustomer called on default KelviqContext. Ensure KelviqProvider is properly set up."
195
+ ), Promise.resolve()),
180
196
  isLoading: !0,
181
197
  error: null,
182
198
  apiUrl: "",
183
199
  environment: "production",
184
200
  entitlementsPath: "",
185
201
  customerId: ""
186
- }, O = N(
187
- B
188
- ), ee = "https://edge.api.kelviq.com/api/v1/", te = "https://edge.sandboxapi.kelviq.com/api/v1/", ne = "entitlements/", oe = ({
189
- children: t,
190
- apiUrl: r,
191
- entitlementsPath: s = ne,
192
- customerId: e,
193
- environment: E = "production",
194
- accessToken: A,
195
- config: h = {}
202
+ }, V = Z(
203
+ J
204
+ ), le = "https://edge.api.kelviq.com/api/v1/", ue = "https://edge.sandboxapi.kelviq.com/api/v1/", ce = "entitlements/", me = ({
205
+ children: n,
206
+ apiUrl: o,
207
+ entitlementsPath: i = ce,
208
+ customerId: t,
209
+ environment: g = "production",
210
+ accessToken: m,
211
+ config: L = {}
196
212
  }) => {
197
213
  const {
198
- onError: L,
199
- maxRetries: a,
214
+ onError: v,
215
+ maxRetries: l,
200
216
  timeout: f,
201
- backoffBaseDelay: y,
202
- fetchEntitlementsOnMount: K = !0
203
- } = h, p = r || (E === "sandbox" ? te : ee), [g, o] = P({
217
+ backoffBaseDelay: p,
218
+ fetchEntitlementsOnMount: U = !0,
219
+ fetchSubscriptionsOnMount: b = !1,
220
+ fetchCustomerOnMount: C = !1
221
+ } = L, s = o || (g === "sandbox" ? ue : le), [u, h] = T({
204
222
  data: null,
205
- isLoading: !!p && K,
223
+ isLoading: !!s && U,
206
224
  error: null
207
- }), [u, S] = P(null), [i, c] = P(
208
- !!p && K
209
- ), [U, w] = P(null), R = T(
210
- (d, l, n) => {
211
- const m = new Error(
212
- `Kelviq SDK (${l}): ${d}`
225
+ }), [a, w] = T(null), [P, A] = T({
226
+ data: null,
227
+ isLoading: !!s && b,
228
+ error: null
229
+ }), [I, $] = T({
230
+ data: null,
231
+ isLoading: !!s && C,
232
+ error: null
233
+ }), [D, K] = T(
234
+ !!s && U
235
+ ), [k, x] = T(null), E = y(
236
+ (c, e, r) => {
237
+ const d = new Error(
238
+ `Kelviq SDK (${e}): ${c}`
213
239
  );
214
- console.error(m.message, n || ""), w((q) => q || m), L && L(m);
240
+ console.error(d.message, r || ""), x((q) => q || d), v && v(d);
215
241
  },
216
- [L]
217
- ), b = T(() => {
218
- if (!p) {
219
- const n = "API URL not configured. Cannot fetch entitlements.";
220
- return o({
242
+ [v]
243
+ ), _ = y(() => {
244
+ if (!s) {
245
+ const r = "API URL not configured. Cannot fetch entitlements.";
246
+ return h({
221
247
  data: null,
222
248
  isLoading: !1,
223
- error: new Error(n)
224
- }), R(n, "fetchAllEntitlements"), c(!1), Promise.reject(new Error(n));
249
+ error: new Error(r)
250
+ }), E(r, "fetchAllEntitlements"), K(!1), Promise.reject(new Error(r));
225
251
  }
226
- if (!e) {
227
- const n = "CustomerId must be provided as props to KelviqProvider.";
228
- return o(() => ({
252
+ if (!t) {
253
+ const r = "CustomerId must be provided as props to KelviqProvider.";
254
+ return h(() => ({
229
255
  data: null,
230
256
  isLoading: !1,
231
- error: new Error(n)
232
- })), R(n, "fetchAllEntitlements"), c(!1), Promise.reject(new Error(n));
257
+ error: new Error(r)
258
+ })), E(r, "fetchAllEntitlements"), K(!1), Promise.reject(new Error(r));
233
259
  }
234
- o((n) => ({
235
- ...n,
260
+ h((r) => ({
261
+ ...r,
236
262
  isLoading: !0,
237
263
  error: null
238
- })), c(!0), w(null);
239
- const d = `${p.replace(/\/$/, "")}/${s.replace(/^\//, "")}`;
240
- return Y({
241
- url: d,
264
+ })), K(!0), x(null);
265
+ const c = `${s.replace(/\/$/, "")}/${i.replace(/^\//, "")}`;
266
+ return O({
267
+ url: c,
242
268
  method: "GET",
243
269
  timeout: f,
244
- maxRetries: a,
245
- backoffBaseDelay: y,
246
- accessToken: A,
270
+ maxRetries: l,
271
+ backoffBaseDelay: p,
272
+ accessToken: m,
247
273
  queryParams: {
248
- customer_id: e
274
+ customer_id: t
249
275
  }
250
- }).then((n) => {
251
- if (n && n.data && Array.isArray(n.data.entitlements)) {
252
- S(n.data);
253
- const m = Q(
254
- n.data
276
+ }).then((r) => {
277
+ if (r && r.data && Array.isArray(r.data.entitlements)) {
278
+ w(r.data);
279
+ const d = ae(
280
+ r.data
255
281
  );
256
- o({
257
- data: m,
282
+ h({
283
+ data: d,
258
284
  isLoading: !1,
259
285
  error: null
260
286
  });
261
287
  } else {
262
- const m = new Error(
288
+ const d = new Error(
263
289
  "Received empty, malformed, or invalid data structure from entitlements API."
264
290
  );
265
- throw o({
291
+ throw h({
266
292
  data: null,
267
293
  isLoading: !1,
268
- error: m
269
- }), R(
270
- m.message,
294
+ error: d
295
+ }), E(
296
+ d.message,
271
297
  "fetchAllEntitlements",
272
- n
273
- ), m;
298
+ r
299
+ ), d;
274
300
  }
275
- }).catch((n) => {
276
- const m = n instanceof Error ? n : new Error(String(n));
277
- return o((q) => ({
301
+ }).catch((r) => {
302
+ const d = r instanceof Error ? r : new Error(String(r));
303
+ return h((q) => ({
278
304
  ...q,
279
305
  isLoading: !1,
280
- error: m
281
- })), R(m.message, "fetchAllEntitlements", n), Promise.reject(m);
306
+ error: d
307
+ })), E(d.message, "fetchAllEntitlements", r), Promise.reject(d);
282
308
  }).finally(() => {
283
- c(!1);
309
+ K(!1);
284
310
  });
285
311
  }, [
312
+ s,
313
+ t,
314
+ i,
315
+ f,
316
+ l,
286
317
  p,
287
- e,
318
+ m,
319
+ E
320
+ ]), j = y(() => {
321
+ if (!s) {
322
+ const e = "API URL not configured. Cannot fetch subscriptions.";
323
+ return A({
324
+ data: null,
325
+ isLoading: !1,
326
+ error: new Error(e)
327
+ }), E(e, "fetchSubscriptions"), Promise.reject(new Error(e));
328
+ }
329
+ if (!t) {
330
+ const e = "CustomerId must be provided as props to KelviqProvider.";
331
+ return A({
332
+ data: null,
333
+ isLoading: !1,
334
+ error: new Error(e)
335
+ }), E(e, "fetchSubscriptions"), Promise.reject(new Error(e));
336
+ }
337
+ A((e) => ({
338
+ ...e,
339
+ isLoading: !0,
340
+ error: null
341
+ }));
342
+ const c = `${s.replace(/\/$/, "")}/subscriptions/`;
343
+ return O({
344
+ url: c,
345
+ method: "GET",
346
+ timeout: f,
347
+ maxRetries: l,
348
+ backoffBaseDelay: p,
349
+ accessToken: m,
350
+ queryParams: { customer_id: t }
351
+ }).then((e) => {
352
+ if (e && e.data && Array.isArray(e.data.results))
353
+ A({
354
+ data: e.data.results,
355
+ isLoading: !1,
356
+ error: null
357
+ });
358
+ else {
359
+ const r = new Error(
360
+ "Received empty, malformed, or invalid data structure from subscriptions API."
361
+ );
362
+ throw A({
363
+ data: null,
364
+ isLoading: !1,
365
+ error: r
366
+ }), E(
367
+ r.message,
368
+ "fetchSubscriptions",
369
+ e
370
+ ), r;
371
+ }
372
+ }).catch((e) => {
373
+ const r = e instanceof Error ? e : new Error(String(e));
374
+ return A((d) => ({
375
+ ...d,
376
+ isLoading: !1,
377
+ error: r
378
+ })), E(
379
+ r.message,
380
+ "fetchSubscriptions",
381
+ e
382
+ ), Promise.reject(r);
383
+ });
384
+ }, [
288
385
  s,
386
+ t,
289
387
  f,
290
- a,
291
- y,
292
- A,
293
- R
388
+ l,
389
+ p,
390
+ m,
391
+ E
392
+ ]), F = y(() => {
393
+ if (!s) {
394
+ const e = "API URL not configured. Cannot fetch customer.";
395
+ return $({
396
+ data: null,
397
+ isLoading: !1,
398
+ error: new Error(e)
399
+ }), E(e, "fetchCustomer"), Promise.reject(new Error(e));
400
+ }
401
+ if (!t) {
402
+ const e = "CustomerId must be provided as props to KelviqProvider.";
403
+ return $({
404
+ data: null,
405
+ isLoading: !1,
406
+ error: new Error(e)
407
+ }), E(e, "fetchCustomer"), Promise.reject(new Error(e));
408
+ }
409
+ $((e) => ({
410
+ ...e,
411
+ isLoading: !0,
412
+ error: null
413
+ }));
414
+ const c = `${s.replace(/\/$/, "")}/customers/${t}/`;
415
+ return O({
416
+ url: c,
417
+ method: "GET",
418
+ timeout: f,
419
+ maxRetries: l,
420
+ backoffBaseDelay: p,
421
+ accessToken: m
422
+ }).then((e) => {
423
+ if (e && e.data)
424
+ $({
425
+ data: e.data,
426
+ isLoading: !1,
427
+ error: null
428
+ });
429
+ else {
430
+ const r = new Error(
431
+ "Received empty or invalid data from customer API."
432
+ );
433
+ throw $({
434
+ data: null,
435
+ isLoading: !1,
436
+ error: r
437
+ }), E(
438
+ r.message,
439
+ "fetchCustomer",
440
+ e
441
+ ), r;
442
+ }
443
+ }).catch((e) => {
444
+ const r = e instanceof Error ? e : new Error(String(e));
445
+ return $((d) => ({
446
+ ...d,
447
+ isLoading: !1,
448
+ error: r
449
+ })), E(r.message, "fetchCustomer", e), Promise.reject(r);
450
+ });
451
+ }, [
452
+ s,
453
+ t,
454
+ f,
455
+ l,
456
+ p,
457
+ m,
458
+ E
294
459
  ]);
295
- H(() => {
296
- let d = !0;
297
- if (!p) {
298
- if (d) {
299
- const l = new Error(
460
+ Q(() => {
461
+ let c = !0;
462
+ if (!s) {
463
+ if (c) {
464
+ const e = new Error(
300
465
  "KelviqProvider: `apiBaseUrl` must be provided in config."
301
466
  );
302
- w(l), c(!1), o((n) => ({
303
- ...n,
467
+ x(e), K(!1), h((r) => ({
468
+ ...r,
304
469
  isLoading: !1,
305
- error: l
306
- })), h.onError && h.onError(l);
470
+ error: e
471
+ })), L.onError && L.onError(e);
307
472
  }
308
473
  return;
309
474
  }
310
- return K ? b().catch((l) => {
311
- d && i && c(!1), console.error(
475
+ return U ? _().catch((e) => {
476
+ c && D && K(!1), console.error(
312
477
  "Kelviq SDK: Initial entitlement fetch failed.",
313
- l
478
+ e
314
479
  );
315
- }) : d && (o((l) => ({
316
- ...l,
480
+ }) : c && (h((e) => ({
481
+ ...e,
317
482
  isLoading: !1,
318
483
  data: null,
319
484
  error: null
320
- })), c(!1)), () => {
321
- d = !1;
485
+ })), K(!1)), b && j().catch((e) => {
486
+ console.error(
487
+ "Kelviq SDK: Initial subscriptions fetch failed.",
488
+ e
489
+ );
490
+ }), C && F().catch((e) => {
491
+ console.error(
492
+ "Kelviq SDK: Initial customer fetch failed.",
493
+ e
494
+ );
495
+ }), () => {
496
+ c = !1;
322
497
  };
323
- }, [p, K, h.onError]);
324
- const x = T(
325
- () => g.data ? g.data : {},
326
- [g.data]
327
- ), M = T(
328
- () => u ? {
329
- customerId: u.customer_id,
330
- entitlements: u.entitlements
498
+ }, [s, U, b, C, L.onError]);
499
+ const N = y(
500
+ () => u.data ? u.data : {},
501
+ [u.data]
502
+ ), G = y(
503
+ () => a ? {
504
+ customerId: a.customerId,
505
+ entitlements: a.entitlements
331
506
  } : null,
332
- [u]
333
- ), C = T(
334
- (d) => u ? {
335
- customerId: u.customer_id,
336
- entitlements: u.entitlements.filter(
337
- (l) => l.featureId === d
507
+ [a]
508
+ ), H = y(
509
+ (c) => a ? {
510
+ customerId: a.customerId,
511
+ entitlements: a.entitlements.filter(
512
+ (e) => e.featureId === c
338
513
  )
339
514
  } : null,
340
- [u]
341
- ), _ = T(
342
- (d) => g.data ? g.data[d] ?? null : null,
343
- [g.data]
344
- ), F = T(
345
- (d, l) => {
346
- o((n) => {
347
- if (!n.data) return n;
348
- const m = n.data[d];
349
- if (!m) return n;
350
- const q = { ...m, ...l };
351
- return ("usageLimit" in l || "currentUsage" in l) && (q.remaining = q.usageLimit !== null ? q.usageLimit - q.currentUsage : null), {
352
- ...n,
515
+ [a]
516
+ ), W = y(
517
+ (c) => u.data ? u.data[c] ?? null : null,
518
+ [u.data]
519
+ ), X = y(
520
+ (c, e) => {
521
+ h((r) => {
522
+ if (!r.data) return r;
523
+ const d = r.data[c];
524
+ if (!d) return r;
525
+ const q = { ...d, ...e };
526
+ return ("usageLimit" in e || "currentUsage" in e) && (q.remaining = q.usageLimit !== null ? q.usageLimit - q.currentUsage : null), {
527
+ ...r,
353
528
  data: {
354
- ...n.data,
355
- [d]: q
529
+ ...r.data,
530
+ [c]: q
356
531
  }
357
532
  };
358
533
  });
359
534
  },
360
535
  []
361
- ), k = T(
362
- (d) => {
363
- if (!g.data) return !1;
364
- const l = g.data[d];
365
- return l ? l.hasAccess : !1;
536
+ ), z = y(
537
+ (c) => {
538
+ if (!u.data) return !1;
539
+ const e = u.data[c];
540
+ return e ? e.hasAccess : !1;
366
541
  },
367
- [g.data]
368
- ), j = G(
542
+ [u.data]
543
+ ), Y = ee(
369
544
  () => ({
370
- allEntitlements: g,
371
- refreshAllEntitlements: b,
372
- getEntitlements: x,
373
- getEntitlement: _,
374
- getRawEntitlements: M,
375
- getRawEntitlement: C,
376
- hasAccess: k,
377
- updateEntitlement: F,
378
- isLoading: i,
379
- error: U,
380
- environment: E,
381
- apiUrl: p,
382
- entitlementsPath: s,
383
- customerId: e
545
+ allEntitlements: u,
546
+ refreshAllEntitlements: _,
547
+ getEntitlements: N,
548
+ getEntitlement: W,
549
+ getRawEntitlements: G,
550
+ getRawEntitlement: H,
551
+ hasAccess: z,
552
+ updateEntitlement: X,
553
+ subscriptions: P,
554
+ refreshSubscriptions: j,
555
+ customer: I,
556
+ refreshCustomer: F,
557
+ isLoading: D,
558
+ error: k,
559
+ environment: g,
560
+ apiUrl: s,
561
+ entitlementsPath: i,
562
+ customerId: t
384
563
  }),
385
564
  [
386
- g,
387
- b,
388
- x,
565
+ u,
389
566
  _,
390
- M,
391
- C,
392
- k,
567
+ N,
568
+ W,
569
+ G,
570
+ H,
571
+ z,
572
+ X,
573
+ P,
574
+ j,
575
+ I,
393
576
  F,
394
- i,
395
- U,
396
- E,
397
- p,
577
+ D,
578
+ k,
579
+ g,
398
580
  s,
399
- e
581
+ i,
582
+ t
400
583
  ]
401
584
  );
402
- return /* @__PURE__ */ v(O.Provider, { value: j, children: t });
403
- }, D = () => {
404
- const t = W(O);
405
- if (t === void 0 || t === B)
585
+ return /* @__PURE__ */ S(V.Provider, { value: Y, children: n });
586
+ }, R = () => {
587
+ const n = te(V);
588
+ if (n === void 0 || n === J)
406
589
  throw new Error(
407
590
  "useKelviq must be used within an initialized KelviqProvider."
408
591
  );
409
- return t;
410
- }, I = ({
411
- featureId: t,
412
- children: r,
413
- fallback: s = null,
414
- loadingComponent: e,
415
- condition: E
592
+ return n;
593
+ }, B = ({
594
+ featureId: n,
595
+ children: o,
596
+ fallback: i = null,
597
+ loadingComponent: t,
598
+ condition: g
416
599
  }) => {
417
600
  const {
418
- getEntitlement: A,
419
- isLoading: h,
420
- error: L
421
- } = D(), a = A(t);
422
- return h && !a ? /* @__PURE__ */ v($, { children: e !== void 0 ? e : s }) : L && !a ? /* @__PURE__ */ v($, { children: s }) : !a || !a.hasAccess || E && !E(a) ? /* @__PURE__ */ v($, { children: s }) : typeof r == "function" ? /* @__PURE__ */ v($, { children: r(a) }) : /* @__PURE__ */ v($, { children: r });
423
- }, ie = (t) => /* @__PURE__ */ v(I, { ...t }), le = (t) => {
424
- const r = (s) => s.hasAccess && (s.remaining === null || s.remaining > 0);
425
- return /* @__PURE__ */ v(
426
- I,
601
+ getEntitlement: m,
602
+ isLoading: L,
603
+ error: v
604
+ } = R(), l = m(n);
605
+ return L && !l ? /* @__PURE__ */ S(M, { children: t !== void 0 ? t : i }) : v && !l ? /* @__PURE__ */ S(M, { children: i }) : !l || !l.hasAccess || g && !g(l) ? /* @__PURE__ */ S(M, { children: i }) : typeof o == "function" ? /* @__PURE__ */ S(M, { children: o(l) }) : /* @__PURE__ */ S(M, { children: o });
606
+ }, Ee = (n) => /* @__PURE__ */ S(B, { ...n }), ge = (n) => {
607
+ const o = (i) => i.hasAccess && (i.remaining === null || i.remaining > 0);
608
+ return /* @__PURE__ */ S(
609
+ B,
427
610
  {
428
- ...t,
429
- condition: t.condition || r
611
+ ...n,
612
+ condition: n.condition || o
430
613
  }
431
614
  );
432
- }, ae = (t) => /* @__PURE__ */ v(I, { ...t }), ue = () => {
433
- const { allEntitlements: t } = D();
434
- return t;
435
- }, ce = (t) => {
436
- const { getEntitlement: r } = D();
437
- return r(t);
438
- }, de = (t) => {
439
- const { getEntitlement: r } = D();
440
- return r(t);
441
- }, me = (t) => {
442
- const { getEntitlement: r } = D();
443
- return r(t);
615
+ }, pe = (n) => /* @__PURE__ */ S(B, { ...n }), he = () => {
616
+ const { allEntitlements: n } = R();
617
+ return n;
618
+ }, Le = (n) => {
619
+ const { getEntitlement: o } = R();
620
+ return o(n);
621
+ }, ve = (n) => {
622
+ const { getEntitlement: o } = R();
623
+ return o(n);
624
+ }, we = (n) => {
625
+ const { getEntitlement: o } = R();
626
+ return o(n);
627
+ }, Ae = () => {
628
+ const { subscriptions: n } = R();
629
+ return n;
630
+ }, ye = () => {
631
+ const { customer: n } = R();
632
+ return n;
444
633
  };
445
634
  export {
446
- O as KelviqContext,
447
- oe as KelviqProvider,
448
- ae as ShowWhenBooleanEntitled,
449
- ie as ShowWhenCustomizableEntitled,
450
- le as ShowWhenMeteredEntitled,
451
- B as defaultKelviqContextValue,
452
- ue as useAllEntitlements,
453
- ce as useBooleanEntitlement,
454
- de as useCustomizableEntitlement,
455
- D as useKelviq,
456
- me as useMeteredEntitlement
635
+ V as KelviqContext,
636
+ me as KelviqProvider,
637
+ pe as ShowWhenBooleanEntitled,
638
+ Ee as ShowWhenCustomizableEntitled,
639
+ ge as ShowWhenMeteredEntitled,
640
+ J as defaultKelviqContextValue,
641
+ he as useAllEntitlements,
642
+ Le as useBooleanEntitlement,
643
+ ye as useCustomer,
644
+ ve as useCustomizableEntitlement,
645
+ R as useKelviq,
646
+ we as useMeteredEntitlement,
647
+ Ae as useSubscriptions
457
648
  };
@@ -1 +1 @@
1
- (function(a,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],u):(a=typeof globalThis<"u"?globalThis:a||self,u(a.KelviqReactSDK={},a.jsxRuntime,a.React))})(this,function(a,u,E){"use strict";const x="GET";function j(t){const{method:r=x,headers:s={},body:e,timeout:p=5e3,maxRetries:v=3,backoffBaseDelay:y=1e3,queryParams:q,accessToken:c}=t;let{url:h}=t,w=0;return new Promise((C,L)=>{if(q){const i=new URLSearchParams;for(const d in q)q[d]!==void 0&&i.append(d,String(q[d]));i.toString()&&(h+=(h.includes("?")?"&":"?")+i.toString())}function A(){const i=new XMLHttpRequest;i.open(r,h,!0),i.timeout=p;const d={Accept:"application/json","X-Requested-With":"XMLHttpRequest",...s};r!=="GET"&&e&&typeof e=="object"&&!(e instanceof FormData)&&(d["Content-Type"]||(d["Content-Type"]="application/json")),c&&(d.Authorization=`Bearer ${c}`);for(const[o,f]of Object.entries(d))i.setRequestHeader(o,f);i.onload=function(){const{status:o,statusText:f,responseText:K}=i;if(o>=200&&o<300)try{const T=K?JSON.parse(K):{};C({status:o,statusText:f,data:T})}catch(T){const U=T instanceof Error?T:new Error(String(T));console.error(`Kelviq SDK (apiRequest): Invalid JSON response for URL ${h}. Error: ${U.message}. Response: ${K}`),L(new Error(`Invalid JSON response: ${U.message}`))}else D(`Request to ${h} failed with status ${o} ${f}. Response: ${K}`)},i.onerror=()=>D(`Network error for URL ${h}. The request could not be completed.`),i.ontimeout=()=>D(`Request to ${h} timed out.`);function D(o){if(w<v){w++;const f=Math.min(y*Math.pow(2,w-1),3e4);console.warn(`Kelviq SDK (apiRequest): Retrying request to ${h}. Attempt ${w}/${v}. Error: ${o}. Retrying in ${f}ms.`),setTimeout(A,f)}else L(new Error(`${o} (Max retries ${v} reached)`))}try{let o=e;e&&typeof e=="object"&&!(e instanceof FormData)&&d["Content-Type"]==="application/json"&&(o=JSON.stringify(e)),i.send(o)}catch(o){const f=o instanceof Error?o:new Error(String(o));console.error(`Kelviq SDK (apiRequest): Error sending request to ${h}.`,f),L(new Error(`Failed to send request: ${f.message}`))}}A()})}function N(t,r){const s=r[0].featureType;if(s==="METER"){let e=0,p=0,v=!1;for(const q of r){const c=q,h=typeof c.usageLimit=="number"?c.usageLimit:null,w=typeof c.currentUsage=="number"?c.currentUsage:0;h===null&&(e=null),e!==null&&(e+=h),p+=w,c.hardLimit&&(v=!0)}const y=e!==null?e-p:null;return{featureId:t,featureType:s,hasAccess:y===null||y>0,currentUsage:p,usageLimit:e,remaining:y,hardLimit:v,items:r}}if(s==="CUSTOMIZABLE"){const e=r[0];return{featureId:t,featureType:s,hasAccess:r.some(p=>p.hasAccess),currentUsage:typeof e.currentUsage=="number"?e.currentUsage:0,usageLimit:typeof e.usageLimit=="number"?e.usageLimit:null,remaining:typeof e.remaining=="number"?e.remaining:null,hardLimit:e.hardLimit===!0,items:r}}return{featureId:t,featureType:s,hasAccess:r.some(e=>e.hasAccess),currentUsage:0,usageLimit:null,remaining:null,hardLimit:!1,items:r}}function W(t){const r={};if(!t||!Array.isArray(t.entitlements))return console.warn("Kelviq SDK: Invalid or empty entitlements array in API response.",t),r;const s={};for(const e of t.entitlements){if(!e||typeof e.featureId!="string"||typeof e.featureType!="string"){console.warn("Kelviq SDK: Skipping invalid raw entitlement object:",e);continue}s[e.featureId]||(s[e.featureId]=[]),s[e.featureId].push(e)}for(const e in s)r[e]=N(e,s[e]);return r}const $={allEntitlements:{data:null,isLoading:!0,error:null},refreshAllEntitlements:()=>(console.warn("refreshAllEntitlements called on default KelviqContext. Ensure KelviqProvider is properly set up."),Promise.resolve()),getEntitlements:()=>({}),getEntitlement:()=>null,getRawEntitlements:()=>null,getRawEntitlement:()=>null,hasAccess:()=>!1,updateEntitlement:()=>{console.warn("updateEntitlement called on default KelviqContext. Ensure KelviqProvider is properly set up.")},isLoading:!0,error:null,apiUrl:"",environment:"production",entitlementsPath:"",customerId:""},M=E.createContext($),H="https://edge.api.kelviq.com/api/v1/",z="https://edge.sandboxapi.kelviq.com/api/v1/",X="entitlements/",G=({children:t,apiUrl:r,entitlementsPath:s=X,customerId:e,environment:p="production",accessToken:v,config:y={}})=>{const{onError:q,maxRetries:c,timeout:h,backoffBaseDelay:w,fetchEntitlementsOnMount:C=!0}=y,L=r||(p==="sandbox"?z:H),[A,i]=E.useState({data:null,isLoading:!!L&&C,error:null}),[d,D]=E.useState(null),[o,f]=E.useState(!!L&&C),[K,T]=E.useState(null),U=E.useCallback((m,l,n)=>{const g=new Error(`Kelviq SDK (${l}): ${m}`);console.error(g.message,n||""),T(S=>S||g),q&&q(g)},[q]),R=E.useCallback(()=>{if(!L){const n="API URL not configured. Cannot fetch entitlements.";return i({data:null,isLoading:!1,error:new Error(n)}),U(n,"fetchAllEntitlements"),f(!1),Promise.reject(new Error(n))}if(!e){const n="CustomerId must be provided as props to KelviqProvider.";return i(()=>({data:null,isLoading:!1,error:new Error(n)})),U(n,"fetchAllEntitlements"),f(!1),Promise.reject(new Error(n))}i(n=>({...n,isLoading:!0,error:null})),f(!0),T(null);const m=`${L.replace(/\/$/,"")}/${s.replace(/^\//,"")}`;return j({url:m,method:"GET",timeout:h,maxRetries:c,backoffBaseDelay:w,accessToken:v,queryParams:{customer_id:e}}).then(n=>{if(n&&n.data&&Array.isArray(n.data.entitlements)){D(n.data);const g=W(n.data);i({data:g,isLoading:!1,error:null})}else{const g=new Error("Received empty, malformed, or invalid data structure from entitlements API.");throw i({data:null,isLoading:!1,error:g}),U(g.message,"fetchAllEntitlements",n),g}}).catch(n=>{const g=n instanceof Error?n:new Error(String(n));return i(S=>({...S,isLoading:!1,error:g})),U(g.message,"fetchAllEntitlements",n),Promise.reject(g)}).finally(()=>{f(!1)})},[L,e,s,h,c,w,v,U]);E.useEffect(()=>{let m=!0;if(!L){if(m){const l=new Error("KelviqProvider: `apiBaseUrl` must be provided in config.");T(l),f(!1),i(n=>({...n,isLoading:!1,error:l})),y.onError&&y.onError(l)}return}return C?R().catch(l=>{m&&o&&f(!1),console.error("Kelviq SDK: Initial entitlement fetch failed.",l)}):m&&(i(l=>({...l,isLoading:!1,data:null,error:null})),f(!1)),()=>{m=!1}},[L,C,y.onError]);const _=E.useCallback(()=>A.data?A.data:{},[A.data]),F=E.useCallback(()=>d?{customerId:d.customer_id,entitlements:d.entitlements}:null,[d]),I=E.useCallback(m=>d?{customerId:d.customer_id,entitlements:d.entitlements.filter(l=>l.featureId===m)}:null,[d]),k=E.useCallback(m=>A.data?A.data[m]??null:null,[A.data]),B=E.useCallback((m,l)=>{i(n=>{if(!n.data)return n;const g=n.data[m];if(!g)return n;const S={...g,...l};return("usageLimit"in l||"currentUsage"in l)&&(S.remaining=S.usageLimit!==null?S.usageLimit-S.currentUsage:null),{...n,data:{...n.data,[m]:S}}})},[]),O=E.useCallback(m=>{if(!A.data)return!1;const l=A.data[m];return l?l.hasAccess:!1},[A.data]),ne=E.useMemo(()=>({allEntitlements:A,refreshAllEntitlements:R,getEntitlements:_,getEntitlement:k,getRawEntitlements:F,getRawEntitlement:I,hasAccess:O,updateEntitlement:B,isLoading:o,error:K,environment:p,apiUrl:L,entitlementsPath:s,customerId:e}),[A,R,_,k,F,I,O,B,o,K,p,L,s,e]);return u.jsx(M.Provider,{value:ne,children:t})},b=()=>{const t=E.useContext(M);if(t===void 0||t===$)throw new Error("useKelviq must be used within an initialized KelviqProvider.");return t},P=({featureId:t,children:r,fallback:s=null,loadingComponent:e,condition:p})=>{const{getEntitlement:v,isLoading:y,error:q}=b(),c=v(t);return y&&!c?u.jsx(u.Fragment,{children:e!==void 0?e:s}):q&&!c?u.jsx(u.Fragment,{children:s}):!c||!c.hasAccess||p&&!p(c)?u.jsx(u.Fragment,{children:s}):typeof r=="function"?u.jsx(u.Fragment,{children:r(c)}):u.jsx(u.Fragment,{children:r})},J=t=>u.jsx(P,{...t}),V=t=>{const r=s=>s.hasAccess&&(s.remaining===null||s.remaining>0);return u.jsx(P,{...t,condition:t.condition||r})},Y=t=>u.jsx(P,{...t}),Z=()=>{const{allEntitlements:t}=b();return t},Q=t=>{const{getEntitlement:r}=b();return r(t)},ee=t=>{const{getEntitlement:r}=b();return r(t)},te=t=>{const{getEntitlement:r}=b();return r(t)};a.KelviqContext=M,a.KelviqProvider=G,a.ShowWhenBooleanEntitled=Y,a.ShowWhenCustomizableEntitled=J,a.ShowWhenMeteredEntitled=V,a.defaultKelviqContextValue=$,a.useAllEntitlements=Z,a.useBooleanEntitlement=Q,a.useCustomizableEntitlement=ee,a.useKelviq=b,a.useMeteredEntitlement=te,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
1
+ (function(d,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],g):(d=typeof globalThis<"u"?globalThis:d||self,g(d.KelviqReactSDK={},d.jsxRuntime,d.React))})(this,function(d,g,f){"use strict";const V="GET";function I(n){const{method:o=V,headers:i={},body:t,timeout:L=5e3,maxRetries:h=3,backoffBaseDelay:w=1e3,queryParams:S,accessToken:a}=n;let{url:E}=n,v=0;return new Promise((T,K)=>{if(S){const s=new URLSearchParams;for(const u in S)S[u]!==void 0&&s.append(u,String(S[u]));s.toString()&&(E+=(E.includes("?")?"&":"?")+s.toString())}function M(){const s=new XMLHttpRequest;s.open(o,E,!0),s.timeout=L;const u={Accept:"application/json","X-Requested-With":"XMLHttpRequest",...i};o!=="GET"&&t&&typeof t=="object"&&!(t instanceof FormData)&&(u["Content-Type"]||(u["Content-Type"]="application/json")),a&&(u.Authorization=`Bearer ${a}`);for(const[l,q]of Object.entries(u))s.setRequestHeader(l,q);s.onload=function(){const{status:l,statusText:q,responseText:P}=s;if(l>=200&&l<300)try{const y=P?JSON.parse(P):{};T({status:l,statusText:q,data:y})}catch(y){const D=y instanceof Error?y:new Error(String(y));console.error(`Kelviq SDK (apiRequest): Invalid JSON response for URL ${E}. Error: ${D.message}. Response: ${P}`),K(new Error(`Invalid JSON response: ${D.message}`))}else A(`Request to ${E} failed with status ${l} ${q}. Response: ${P}`)},s.onerror=()=>A(`Network error for URL ${E}. The request could not be completed.`),s.ontimeout=()=>A(`Request to ${E} timed out.`);function A(l){if(v<h){v++;const q=Math.min(w*Math.pow(2,v-1),3e4);console.warn(`Kelviq SDK (apiRequest): Retrying request to ${E}. Attempt ${v}/${h}. Error: ${l}. Retrying in ${q}ms.`),setTimeout(M,q)}else K(new Error(`${l} (Max retries ${h} reached)`))}try{let l=t;t&&typeof t=="object"&&!(t instanceof FormData)&&u["Content-Type"]==="application/json"&&(l=JSON.stringify(t)),s.send(l)}catch(l){const q=l instanceof Error?l:new Error(String(l));console.error(`Kelviq SDK (apiRequest): Error sending request to ${E}.`,q),K(new Error(`Failed to send request: ${q.message}`))}}M()})}function Y(n,o){const i=o[0].featureType;if(i==="METER"){let t=0,L=0,h=!1;for(const S of o){const a=S,E=typeof a.usageLimit=="number"?a.usageLimit:null,v=typeof a.currentUsage=="number"?a.currentUsage:0;E===null&&(t=null),t!==null&&(t+=E),L+=v,a.hardLimit&&(h=!0)}const w=t!==null?t-L:null;return{featureId:n,featureType:i,hasAccess:w===null||w>0,currentUsage:L,usageLimit:t,remaining:w,hardLimit:h,items:o}}if(i==="CUSTOMIZABLE"){const t=o[0];return{featureId:n,featureType:i,hasAccess:o.some(L=>L.hasAccess),currentUsage:typeof t.currentUsage=="number"?t.currentUsage:0,usageLimit:typeof t.usageLimit=="number"?t.usageLimit:null,remaining:typeof t.remaining=="number"?t.remaining:null,hardLimit:t.hardLimit===!0,items:o}}return{featureId:n,featureType:i,hasAccess:o.some(t=>t.hasAccess),currentUsage:0,usageLimit:null,remaining:null,hardLimit:!1,items:o}}function Z(n){const o={};if(!n||!Array.isArray(n.entitlements))return console.warn("Kelviq SDK: Invalid or empty entitlements array in API response.",n),o;const i={};for(const t of n.entitlements){if(!t||typeof t.featureId!="string"||typeof t.featureType!="string"){console.warn("Kelviq SDK: Skipping invalid raw entitlement object:",t);continue}i[t.featureId]||(i[t.featureId]=[]),i[t.featureId].push(t)}for(const t in i)o[t]=Y(t,i[t]);return o}const R={allEntitlements:{data:null,isLoading:!0,error:null},refreshAllEntitlements:()=>(console.warn("refreshAllEntitlements called on default KelviqContext. Ensure KelviqProvider is properly set up."),Promise.resolve()),getEntitlements:()=>({}),getEntitlement:()=>null,getRawEntitlements:()=>null,getRawEntitlement:()=>null,hasAccess:()=>!1,updateEntitlement:()=>{console.warn("updateEntitlement called on default KelviqContext. Ensure KelviqProvider is properly set up.")},subscriptions:{data:null,isLoading:!1,error:null},refreshSubscriptions:()=>(console.warn("refreshSubscriptions called on default KelviqContext. Ensure KelviqProvider is properly set up."),Promise.resolve()),customer:{data:null,isLoading:!1,error:null},refreshCustomer:()=>(console.warn("refreshCustomer called on default KelviqContext. Ensure KelviqProvider is properly set up."),Promise.resolve()),isLoading:!0,error:null,apiUrl:"",environment:"production",entitlementsPath:"",customerId:""},F=f.createContext(R),Q="https://edge.api.kelviq.com/api/v1/",ee="https://edge.sandboxapi.kelviq.com/api/v1/",te="entitlements/",re=({children:n,apiUrl:o,entitlementsPath:i=te,customerId:t,environment:L="production",accessToken:h,config:w={}})=>{const{onError:S,maxRetries:a,timeout:E,backoffBaseDelay:v,fetchEntitlementsOnMount:T=!0,fetchSubscriptionsOnMount:K=!1,fetchCustomerOnMount:M=!1}=w,s=o||(L==="sandbox"?ee:Q),[u,A]=f.useState({data:null,isLoading:!!s&&T,error:null}),[l,q]=f.useState(null),[P,y]=f.useState({data:null,isLoading:!!s&&K,error:null}),[D,$]=f.useState({data:null,isLoading:!!s&&M,error:null}),[k,U]=f.useState(!!s&&T),[N,O]=f.useState(null),p=f.useCallback((c,e,r)=>{const m=new Error(`Kelviq SDK (${e}): ${c}`);console.error(m.message,r||""),O(b=>b||m),S&&S(m)},[S]),j=f.useCallback(()=>{if(!s){const r="API URL not configured. Cannot fetch entitlements.";return A({data:null,isLoading:!1,error:new Error(r)}),p(r,"fetchAllEntitlements"),U(!1),Promise.reject(new Error(r))}if(!t){const r="CustomerId must be provided as props to KelviqProvider.";return A(()=>({data:null,isLoading:!1,error:new Error(r)})),p(r,"fetchAllEntitlements"),U(!1),Promise.reject(new Error(r))}A(r=>({...r,isLoading:!0,error:null})),U(!0),O(null);const c=`${s.replace(/\/$/,"")}/${i.replace(/^\//,"")}`;return I({url:c,method:"GET",timeout:E,maxRetries:a,backoffBaseDelay:v,accessToken:h,queryParams:{customer_id:t}}).then(r=>{if(r&&r.data&&Array.isArray(r.data.entitlements)){q(r.data);const m=Z(r.data);A({data:m,isLoading:!1,error:null})}else{const m=new Error("Received empty, malformed, or invalid data structure from entitlements API.");throw A({data:null,isLoading:!1,error:m}),p(m.message,"fetchAllEntitlements",r),m}}).catch(r=>{const m=r instanceof Error?r:new Error(String(r));return A(b=>({...b,isLoading:!1,error:m})),p(m.message,"fetchAllEntitlements",r),Promise.reject(m)}).finally(()=>{U(!1)})},[s,t,i,E,a,v,h,p]),B=f.useCallback(()=>{if(!s){const e="API URL not configured. Cannot fetch subscriptions.";return y({data:null,isLoading:!1,error:new Error(e)}),p(e,"fetchSubscriptions"),Promise.reject(new Error(e))}if(!t){const e="CustomerId must be provided as props to KelviqProvider.";return y({data:null,isLoading:!1,error:new Error(e)}),p(e,"fetchSubscriptions"),Promise.reject(new Error(e))}y(e=>({...e,isLoading:!0,error:null}));const c=`${s.replace(/\/$/,"")}/subscriptions/`;return I({url:c,method:"GET",timeout:E,maxRetries:a,backoffBaseDelay:v,accessToken:h,queryParams:{customer_id:t}}).then(e=>{if(e&&e.data&&Array.isArray(e.data.results))y({data:e.data.results,isLoading:!1,error:null});else{const r=new Error("Received empty, malformed, or invalid data structure from subscriptions API.");throw y({data:null,isLoading:!1,error:r}),p(r.message,"fetchSubscriptions",e),r}}).catch(e=>{const r=e instanceof Error?e:new Error(String(e));return y(m=>({...m,isLoading:!1,error:r})),p(r.message,"fetchSubscriptions",e),Promise.reject(r)})},[s,t,E,a,v,h,p]),x=f.useCallback(()=>{if(!s){const e="API URL not configured. Cannot fetch customer.";return $({data:null,isLoading:!1,error:new Error(e)}),p(e,"fetchCustomer"),Promise.reject(new Error(e))}if(!t){const e="CustomerId must be provided as props to KelviqProvider.";return $({data:null,isLoading:!1,error:new Error(e)}),p(e,"fetchCustomer"),Promise.reject(new Error(e))}$(e=>({...e,isLoading:!0,error:null}));const c=`${s.replace(/\/$/,"")}/customers/${t}/`;return I({url:c,method:"GET",timeout:E,maxRetries:a,backoffBaseDelay:v,accessToken:h}).then(e=>{if(e&&e.data)$({data:e.data,isLoading:!1,error:null});else{const r=new Error("Received empty or invalid data from customer API.");throw $({data:null,isLoading:!1,error:r}),p(r.message,"fetchCustomer",e),r}}).catch(e=>{const r=e instanceof Error?e:new Error(String(e));return $(m=>({...m,isLoading:!1,error:r})),p(r.message,"fetchCustomer",e),Promise.reject(r)})},[s,t,E,a,v,h,p]);f.useEffect(()=>{let c=!0;if(!s){if(c){const e=new Error("KelviqProvider: `apiBaseUrl` must be provided in config.");O(e),U(!1),A(r=>({...r,isLoading:!1,error:e})),w.onError&&w.onError(e)}return}return T?j().catch(e=>{c&&k&&U(!1),console.error("Kelviq SDK: Initial entitlement fetch failed.",e)}):c&&(A(e=>({...e,isLoading:!1,data:null,error:null})),U(!1)),K&&B().catch(e=>{console.error("Kelviq SDK: Initial subscriptions fetch failed.",e)}),M&&x().catch(e=>{console.error("Kelviq SDK: Initial customer fetch failed.",e)}),()=>{c=!1}},[s,T,K,M,w.onError]);const W=f.useCallback(()=>u.data?u.data:{},[u.data]),G=f.useCallback(()=>l?{customerId:l.customerId,entitlements:l.entitlements}:null,[l]),H=f.useCallback(c=>l?{customerId:l.customerId,entitlements:l.entitlements.filter(e=>e.featureId===c)}:null,[l]),z=f.useCallback(c=>u.data?u.data[c]??null:null,[u.data]),X=f.useCallback((c,e)=>{A(r=>{if(!r.data)return r;const m=r.data[c];if(!m)return r;const b={...m,...e};return("usageLimit"in e||"currentUsage"in e)&&(b.remaining=b.usageLimit!==null?b.usageLimit-b.currentUsage:null),{...r,data:{...r.data,[c]:b}}})},[]),J=f.useCallback(c=>{if(!u.data)return!1;const e=u.data[c];return e?e.hasAccess:!1},[u.data]),fe=f.useMemo(()=>({allEntitlements:u,refreshAllEntitlements:j,getEntitlements:W,getEntitlement:z,getRawEntitlements:G,getRawEntitlement:H,hasAccess:J,updateEntitlement:X,subscriptions:P,refreshSubscriptions:B,customer:D,refreshCustomer:x,isLoading:k,error:N,environment:L,apiUrl:s,entitlementsPath:i,customerId:t}),[u,j,W,z,G,H,J,X,P,B,D,x,k,N,L,s,i,t]);return g.jsx(F.Provider,{value:fe,children:n})},C=()=>{const n=f.useContext(F);if(n===void 0||n===R)throw new Error("useKelviq must be used within an initialized KelviqProvider.");return n},_=({featureId:n,children:o,fallback:i=null,loadingComponent:t,condition:L})=>{const{getEntitlement:h,isLoading:w,error:S}=C(),a=h(n);return w&&!a?g.jsx(g.Fragment,{children:t!==void 0?t:i}):S&&!a?g.jsx(g.Fragment,{children:i}):!a||!a.hasAccess||L&&!L(a)?g.jsx(g.Fragment,{children:i}):typeof o=="function"?g.jsx(g.Fragment,{children:o(a)}):g.jsx(g.Fragment,{children:o})},ne=n=>g.jsx(_,{...n}),se=n=>{const o=i=>i.hasAccess&&(i.remaining===null||i.remaining>0);return g.jsx(_,{...n,condition:n.condition||o})},oe=n=>g.jsx(_,{...n}),ie=()=>{const{allEntitlements:n}=C();return n},le=n=>{const{getEntitlement:o}=C();return o(n)},ae=n=>{const{getEntitlement:o}=C();return o(n)},ue=n=>{const{getEntitlement:o}=C();return o(n)},ce=()=>{const{subscriptions:n}=C();return n},de=()=>{const{customer:n}=C();return n};d.KelviqContext=F,d.KelviqProvider=re,d.ShowWhenBooleanEntitled=oe,d.ShowWhenCustomizableEntitled=ne,d.ShowWhenMeteredEntitled=se,d.defaultKelviqContextValue=R,d.useAllEntitlements=ie,d.useBooleanEntitlement=le,d.useCustomer=de,d.useCustomizableEntitlement=ae,d.useKelviq=C,d.useMeteredEntitlement=ue,d.useSubscriptions=ce,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})});
@@ -40,10 +40,71 @@ export interface RawMeteredEntitlementData extends RawEntitlement {
40
40
  export type AnyRawEntitlementData = RawBooleanEntitlementData | RawCustomizableEntitlementData | RawMeteredEntitlementData;
41
41
  /**
42
42
  * Represents the expected raw API response structure when fetching all entitlements.
43
- * Example: { "customer_id": "128", "entitlements": [...] }
43
+ * Example: { "customerId": "128", "entitlements": [...] }
44
44
  */
45
45
  export interface RawEntitlementsApiResponse {
46
- customer_id: string;
46
+ customerId: string;
47
47
  entitlements: AnyRawEntitlementData[];
48
48
  }
49
+ export interface RawSubscriptionFeature {
50
+ featureId: string;
51
+ featureType: string;
52
+ hasAccess: boolean;
53
+ usageLimit: number | null;
54
+ currentUsage: number;
55
+ remaining: number | null;
56
+ }
57
+ export interface RawSubscriptionPlan {
58
+ name: string;
59
+ identifier: string;
60
+ }
61
+ export interface RawSubscriptionProduct {
62
+ name: string;
63
+ identifier: string;
64
+ }
65
+ /** A single subscription object as returned by the subscriptions API. */
66
+ export interface RawSubscriptionData {
67
+ id: string;
68
+ externalSubscriptionId: string;
69
+ startDate: string;
70
+ endDate: string | null;
71
+ billingPeriodStartTime: string;
72
+ billingPeriodEndTime: string;
73
+ amount: string;
74
+ recurrence: string;
75
+ currency: string;
76
+ status: string;
77
+ product: RawSubscriptionProduct;
78
+ plan: RawSubscriptionPlan;
79
+ features: RawSubscriptionFeature[];
80
+ trialDaysRemaining: number | null;
81
+ customerId: string;
82
+ }
83
+ /** Paginated response from the subscriptions list endpoint. */
84
+ export interface RawSubscriptionsApiResponse {
85
+ count: number;
86
+ next: string | null;
87
+ previous: string | null;
88
+ results: RawSubscriptionData[];
89
+ }
90
+ export interface RawBillingAddress {
91
+ country: string;
92
+ line1: string;
93
+ line2: string;
94
+ postalCode: string;
95
+ city: string;
96
+ state: string;
97
+ }
98
+ /** Raw customer data as returned by the customer API. */
99
+ export interface RawCustomerApiResponse {
100
+ id: string;
101
+ customerId: string;
102
+ name: string;
103
+ email: string;
104
+ details: Record<string, unknown>;
105
+ metadata: Record<string, unknown>;
106
+ billingAddress: RawBillingAddress | null;
107
+ createdOn: string;
108
+ modifiedOn: string;
109
+ }
49
110
  export {};
@@ -63,4 +63,14 @@ export interface KelviqApiBehaviorOptions {
63
63
  * If not provided, the `apiRequest` service's default will be used.
64
64
  */
65
65
  backoffBaseDelay?: number;
66
+ /**
67
+ * If true, fetches the customer's subscriptions when the provider mounts.
68
+ * Defaults to false. When enabled, data is available via `useSubscriptions()`.
69
+ */
70
+ fetchSubscriptionsOnMount?: boolean;
71
+ /**
72
+ * If true, fetches customer data when the provider mounts.
73
+ * Defaults to false. When enabled, data is available via `useCustomer()`.
74
+ */
75
+ fetchCustomerOnMount?: boolean;
66
76
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kelviq/react-sdk",
3
3
  "private": false,
4
- "version": "2.0.0-beta",
4
+ "version": "2.0.1-beta",
5
5
  "type": "module",
6
6
  "main": "dist/kelviq-react-sdk.js",
7
7
  "types": "dist/index.d.ts",