@flexbe/sdk 0.2.29 → 0.2.31

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.
@@ -7,12 +7,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { NotFoundException, ForbiddenException, BadRequestException, UnauthorizedException, ServerException, TimeoutException } from '../types';
11
- import { FlexbeAuth } from './auth';
10
+ import { NotFoundException, ForbiddenException, BadRequestException, UnauthorizedException, ServerException, TimeoutException, FlexbeAuthType } from '../types';
11
+ import { TokenManager } from './token-manager';
12
12
  export class ApiClient {
13
13
  constructor(config) {
14
14
  this.config = config;
15
- this.auth = new FlexbeAuth(config);
15
+ this.tokenManager = TokenManager.getInstance();
16
+ if (this.config.authType === FlexbeAuthType.BEARER) {
17
+ // Start initialization but don't wait for it
18
+ void this.tokenManager.getToken(); // just warm up the token manager before any request
19
+ }
20
+ }
21
+ getAuthHeaders() {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const headers = {};
24
+ if (this.config.authType === FlexbeAuthType.API_KEY) {
25
+ headers['x-api-key'] = this.config.apiKey;
26
+ }
27
+ else if (this.config.authType === FlexbeAuthType.BEARER) {
28
+ const token = yield this.tokenManager.getToken();
29
+ if (!token) {
30
+ throw new Error('No valid bearer token available');
31
+ }
32
+ headers['Authorization'] = `Bearer ${token}`;
33
+ }
34
+ return headers;
35
+ });
16
36
  }
17
37
  buildUrl(path, params) {
18
38
  const searchParams = new URLSearchParams();
@@ -27,12 +47,12 @@ export class ApiClient {
27
47
  }
28
48
  request(config) {
29
49
  return __awaiter(this, void 0, void 0, function* () {
50
+ var _a, _b;
30
51
  try {
31
- yield this.auth.ensureInitialized();
32
52
  const controller = new AbortController();
33
53
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
34
54
  const url = this.buildUrl(config.url, config.params);
35
- const headers = Object.assign(Object.assign({}, (yield this.auth.getAuthHeaders())), config.headers);
55
+ const headers = Object.assign(Object.assign({ 'Content-Type': 'application/json' }, (yield this.getAuthHeaders())), config.headers);
36
56
  const response = yield fetch(this.config.baseUrl + url, Object.assign(Object.assign({}, config), { headers, signal: controller.signal }));
37
57
  clearTimeout(timeoutId);
38
58
  if (!response.ok) {
@@ -81,6 +101,9 @@ export class ApiClient {
81
101
  };
82
102
  }
83
103
  catch (error) {
104
+ if (error instanceof UnauthorizedException) {
105
+ (_b = (_a = this.config.hooks) === null || _a === void 0 ? void 0 : _a.onUnauthorized) === null || _b === void 0 ? void 0 : _b.call(_a);
106
+ }
84
107
  if (error instanceof Error && error.name === 'AbortError') {
85
108
  throw new TimeoutException('Request timeout');
86
109
  }
@@ -21,12 +21,13 @@ export class FlexbeClient {
21
21
  }
22
22
  return undefined;
23
23
  };
24
- this.config = {
25
- baseUrl: (config === null || config === void 0 ? void 0 : config.baseUrl) || getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
26
- timeout: (config === null || config === void 0 ? void 0 : config.timeout) || 30000,
27
- apiKey: (config === null || config === void 0 ? void 0 : config.apiKey) || getEnvVar('FLEXBE_API_KEY') || '',
28
- authType: (config === null || config === void 0 ? void 0 : config.authType) || FlexbeAuthType.API_KEY,
24
+ const defaultConfig = {
25
+ baseUrl: getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
26
+ timeout: 30000,
27
+ apiKey: getEnvVar('FLEXBE_API_KEY') || '',
28
+ authType: FlexbeAuthType.API_KEY,
29
29
  };
30
+ this.config = Object.assign(Object.assign({}, defaultConfig), config);
30
31
  if (this.config.authType === 'apiKey' && !this.config.apiKey) {
31
32
  throw new Error('API key is required when using apiKey authentication. Please provide it either through config or FLEXBE_API_KEY environment variable.');
32
33
  }
@@ -7,105 +7,84 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- const TOKEN_STORAGE_KEY = 'flexbe_jwt_token';
11
- const REFRESH_THRESHOLD = 0.8; // Refresh when 80% of token lifetime has passed
12
- const REFRESH_CHECK_INTERVAL = 30 * 1000; // Check every 30 seconds
13
- const MAX_REFRESH_DELAY = 10000; // Maximum random delay of 10 seconds
10
+ import { UnauthorizedException } from '../types';
11
+ const TOKEN_STORAGE_KEY = 'flexbe_jwt';
12
+ const TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; // update token 5 minutes before expiration
14
13
  export class TokenManager {
15
14
  constructor() {
16
- this.token = null;
17
- this.refreshInterval = null;
18
- this.refreshTimeout = null;
19
15
  this.tokenPromise = null;
20
- this.debug = false;
21
- this.initializeFromStorage();
22
- this.setupStorageListener();
23
16
  }
24
17
  static getInstance() {
25
18
  if (!TokenManager.instance) {
26
19
  TokenManager.instance = new TokenManager();
27
20
  }
21
+ if (typeof window !== 'undefined') {
22
+ // Clean up old token storage place
23
+ // TODO remove this after June 1, 2025
24
+ localStorage.removeItem('flexbe_jwt_token');
25
+ ;
26
+ }
28
27
  return TokenManager.instance;
29
28
  }
30
- initializeFromStorage() {
31
- if (typeof window === 'undefined')
32
- return;
33
- const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
34
- if (!storedToken)
35
- return;
36
- try {
37
- this.token = JSON.parse(storedToken);
38
- if (this.token.expiresAt > Date.now()) {
39
- this.logTokenStatus('Token loaded from storage');
40
- this.startRefreshInterval();
41
- }
42
- else {
43
- this.clearToken();
29
+ getToken() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ var _a;
32
+ const token = this.getStoredToken();
33
+ if (token && token.expiresAt > Date.now()) {
34
+ // TODO check if token expire less that 1 minute
35
+ // if so, retrieve a new token
36
+ if (token.expiresAt - Date.now() < TOKEN_REFRESH_THRESHOLD) {
37
+ void this.retrieveToken();
38
+ }
39
+ return token.accessToken;
44
40
  }
45
- }
46
- catch (error) {
47
- console.error('Failed to parse stored token:', error);
48
- this.clearToken();
49
- }
41
+ yield this.retrieveToken();
42
+ const retrievedToken = this.getStoredToken();
43
+ return (_a = retrievedToken === null || retrievedToken === void 0 ? void 0 : retrievedToken.accessToken) !== null && _a !== void 0 ? _a : null;
44
+ });
50
45
  }
51
- setupStorageListener() {
52
- if (typeof window === 'undefined')
53
- return;
54
- window.addEventListener('storage', (event) => {
55
- if (event.key !== TOKEN_STORAGE_KEY)
56
- return;
57
- if (!event.newValue) {
58
- this.clearToken();
59
- // void this.retrieveToken();
46
+ revokeToken() {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ const token = this.getStoredToken();
49
+ this.clearToken();
50
+ if (!token)
60
51
  return;
61
- }
62
52
  try {
63
- const newToken = JSON.parse(event.newValue);
64
- // Skip if the new token is exactly the same as current token
65
- if (this.token &&
66
- this.token.accessToken === newToken.accessToken &&
67
- this.token.expiresAt === newToken.expiresAt) {
68
- return;
69
- }
70
- if (newToken.expiresAt > Date.now()) {
71
- this.token = newToken;
72
- this.logTokenStatus('Token updated from storage');
73
- this.startRefreshInterval();
74
- }
75
- else {
76
- this.clearToken();
77
- void this.retrieveToken();
78
- }
53
+ const controller = new AbortController();
54
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
55
+ yield fetch('/oauth/revoke', {
56
+ method: 'POST',
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ 'Authorization': `Bearer ${token.accessToken}`
60
+ },
61
+ body: JSON.stringify({ token: token.accessToken }),
62
+ credentials: 'include',
63
+ signal: controller.signal,
64
+ });
65
+ clearTimeout(timeoutId);
79
66
  }
80
67
  catch (error) {
81
- console.error('Failed to parse token from storage event:', error);
82
- this.clearToken();
83
- void this.retrieveToken();
68
+ console.error('Failed to revoke token:', error);
84
69
  }
85
70
  });
86
71
  }
87
- getExpirationFromToken(token) {
72
+ getStoredToken() {
73
+ if (typeof window === 'undefined')
74
+ return null;
75
+ const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
76
+ if (!storedToken) {
77
+ return null;
78
+ }
88
79
  try {
89
- const [, payload] = token.split('.');
90
- const decodedPayload = JSON.parse(atob(payload));
91
- return decodedPayload.exp * 1000; // Convert to milliseconds
80
+ const token = JSON.parse(storedToken);
81
+ return token;
92
82
  }
93
83
  catch (error) {
94
- console.error('Failed to parse token expiration:', error);
95
- return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
84
+ console.error('getStoredToken: Failed to parse stored token:', error);
85
+ return null;
96
86
  }
97
87
  }
98
- getToken() {
99
- return __awaiter(this, void 0, void 0, function* () {
100
- var _a, _b;
101
- const token = this.token;
102
- if (token && token.expiresAt && token.expiresAt > Date.now()) {
103
- return token.accessToken;
104
- }
105
- yield this.retrieveToken();
106
- return (_b = (_a = this.token) === null || _a === void 0 ? void 0 : _a.accessToken) !== null && _b !== void 0 ? _b : null;
107
- });
108
- }
109
88
  retrieveToken() {
110
89
  return __awaiter(this, void 0, void 0, function* () {
111
90
  if (this.tokenPromise) {
@@ -136,130 +115,44 @@ export class TokenManager {
136
115
  clearTimeout(timeoutId);
137
116
  if (!response.ok) {
138
117
  const errorData = yield response.json().catch(() => ({ message: response.statusText }));
118
+ if (response.status === 401) {
119
+ throw new UnauthorizedException(errorData.message || response.statusText);
120
+ }
139
121
  throw new Error(errorData.message || response.statusText);
140
122
  }
141
123
  const data = yield response.json();
142
124
  this.setToken(data);
143
125
  }
144
126
  catch (error) {
145
- console.error('Failed to retrieve token:', error);
146
- this.clearToken();
147
- // Schedule a retry after REFRESH_CHECK_INTERVAL
148
- setTimeout(() => {
149
- void this.retrieveToken();
150
- }, REFRESH_CHECK_INTERVAL);
151
127
  throw error;
152
128
  }
153
129
  });
154
130
  }
155
- startRefreshInterval() {
156
- this.clearRefreshTimers();
157
- if (!this.token)
158
- return;
159
- const tokenLifetime = this.token.expiresAt - Date.now();
160
- const refreshThreshold = Math.round(tokenLifetime * REFRESH_THRESHOLD);
161
- const timeUntilRefresh = refreshThreshold - (Math.random() * MAX_REFRESH_DELAY);
162
- this.logTokenStatus('Starting refresh interval', {
163
- tokenLifetime: `${Math.round(tokenLifetime / 1000)} seconds`,
164
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
165
- timeUntilRefresh: `${Math.round(timeUntilRefresh / 1000)} seconds`,
166
- });
167
- this.scheduleRefresh(timeUntilRefresh);
168
- this.refreshInterval = window.setInterval(() => {
169
- if (!this.token)
170
- return;
171
- const timeUntilExpiry = this.token.expiresAt - Date.now();
172
- if (timeUntilExpiry <= 0) {
173
- this.logTokenStatus('Token expired');
174
- this.clearToken();
175
- void this.retrieveToken();
176
- return;
177
- }
178
- const refreshThreshold = Math.round(timeUntilExpiry * REFRESH_THRESHOLD);
179
- if (timeUntilExpiry <= refreshThreshold) {
180
- this.logTokenStatus('Refreshing token', {
181
- timeUntilExpiry: `${Math.round(timeUntilExpiry / 1000)} seconds`,
182
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
183
- });
184
- this.scheduleRefresh(refreshThreshold - (Math.random() * MAX_REFRESH_DELAY));
185
- }
186
- }, REFRESH_CHECK_INTERVAL);
187
- }
188
- scheduleRefresh(delay) {
189
- if (this.refreshTimeout) {
190
- window.clearTimeout(this.refreshTimeout);
191
- }
192
- this.refreshTimeout = window.setTimeout(() => {
193
- const token = this.token;
194
- if (token && token.expiresAt - Date.now() <= token.expiresAt * REFRESH_THRESHOLD) {
195
- void this.retrieveToken();
196
- }
197
- }, delay);
198
- }
199
- clearRefreshTimers() {
200
- if (this.refreshInterval) {
201
- clearInterval(this.refreshInterval);
202
- this.refreshInterval = null;
203
- }
204
- if (this.refreshTimeout) {
205
- window.clearTimeout(this.refreshTimeout);
206
- this.refreshTimeout = null;
207
- }
208
- }
209
- logTokenStatus(message, additionalInfo = {}) {
210
- if (!this.debug)
211
- return;
212
- const token = this.token;
213
- if (!token)
214
- return;
215
- console.log(message, Object.assign({ expiresIn: `${Math.round((token.expiresAt - Date.now()) / 1000)} seconds`, expiresAt: new Date(token.expiresAt).toISOString() }, additionalInfo));
216
- }
217
131
  setToken(tokenResponse) {
218
132
  const expiresAt = this.getExpirationFromToken(tokenResponse.accessToken);
219
- this.token = {
133
+ const token = {
220
134
  accessToken: tokenResponse.accessToken,
221
135
  expiresAt,
222
136
  };
223
- this.logTokenStatus('Token set', {
224
- expiresAt: new Date(expiresAt).toISOString(),
225
- });
226
137
  if (typeof window !== 'undefined') {
227
- localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(this.token));
138
+ localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(token));
228
139
  }
229
- this.startRefreshInterval();
230
140
  }
231
- clearToken() {
232
- this.token = null;
233
- this.clearRefreshTimers();
234
- if (typeof window !== 'undefined') {
235
- localStorage.removeItem(TOKEN_STORAGE_KEY);
141
+ getExpirationFromToken(token) {
142
+ try {
143
+ const [, payload] = token.split('.');
144
+ const decodedPayload = JSON.parse(atob(payload));
145
+ return decodedPayload.exp * 1000; // Convert to milliseconds
146
+ }
147
+ catch (error) {
148
+ console.error('getExpirationFromToken: Failed to parse token expiration:', error);
149
+ return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
236
150
  }
237
151
  }
238
- revokeToken() {
239
- return __awaiter(this, void 0, void 0, function* () {
240
- const token = this.token;
241
- this.clearToken();
242
- if (!token)
243
- return;
244
- try {
245
- const controller = new AbortController();
246
- const timeoutId = setTimeout(() => controller.abort(), 30000);
247
- yield fetch('/oauth/revoke', {
248
- method: 'POST',
249
- headers: {
250
- 'Content-Type': 'application/json',
251
- 'Authorization': `Bearer ${token.accessToken}`
252
- },
253
- body: JSON.stringify({ token: token.accessToken }),
254
- credentials: 'include',
255
- signal: controller.signal,
256
- });
257
- clearTimeout(timeoutId);
258
- }
259
- catch (error) {
260
- console.error('Failed to revoke token:', error);
261
- // Even if revocation fails, we still want to clear the local token
262
- }
263
- });
152
+ clearToken() {
153
+ if (typeof window === 'undefined') {
154
+ return;
155
+ }
156
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
264
157
  }
265
158
  }
@@ -2,11 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiClient = void 0;
4
4
  const types_1 = require("../types");
5
- const auth_1 = require("./auth");
5
+ const token_manager_1 = require("./token-manager");
6
6
  class ApiClient {
7
7
  constructor(config) {
8
8
  this.config = config;
9
- this.auth = new auth_1.FlexbeAuth(config);
9
+ this.tokenManager = token_manager_1.TokenManager.getInstance();
10
+ if (this.config.authType === types_1.FlexbeAuthType.BEARER) {
11
+ // Start initialization but don't wait for it
12
+ void this.tokenManager.getToken(); // just warm up the token manager before any request
13
+ }
14
+ }
15
+ async getAuthHeaders() {
16
+ const headers = {};
17
+ if (this.config.authType === types_1.FlexbeAuthType.API_KEY) {
18
+ headers['x-api-key'] = this.config.apiKey;
19
+ }
20
+ else if (this.config.authType === types_1.FlexbeAuthType.BEARER) {
21
+ const token = await this.tokenManager.getToken();
22
+ if (!token) {
23
+ throw new Error('No valid bearer token available');
24
+ }
25
+ headers['Authorization'] = `Bearer ${token}`;
26
+ }
27
+ return headers;
10
28
  }
11
29
  buildUrl(path, params) {
12
30
  const searchParams = new URLSearchParams();
@@ -21,12 +39,12 @@ class ApiClient {
21
39
  }
22
40
  async request(config) {
23
41
  try {
24
- await this.auth.ensureInitialized();
25
42
  const controller = new AbortController();
26
43
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
27
44
  const url = this.buildUrl(config.url, config.params);
28
45
  const headers = {
29
- ...(await this.auth.getAuthHeaders()),
46
+ 'Content-Type': 'application/json',
47
+ ...(await this.getAuthHeaders()),
30
48
  ...config.headers,
31
49
  };
32
50
  const response = await fetch(this.config.baseUrl + url, {
@@ -81,6 +99,9 @@ class ApiClient {
81
99
  };
82
100
  }
83
101
  catch (error) {
102
+ if (error instanceof types_1.UnauthorizedException) {
103
+ this.config.hooks?.onUnauthorized?.();
104
+ }
84
105
  if (error instanceof Error && error.name === 'AbortError') {
85
106
  throw new types_1.TimeoutException('Request timeout');
86
107
  }
@@ -15,11 +15,15 @@ class FlexbeClient {
15
15
  }
16
16
  return undefined;
17
17
  };
18
+ const defaultConfig = {
19
+ baseUrl: getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
20
+ timeout: 30000,
21
+ apiKey: getEnvVar('FLEXBE_API_KEY') || '',
22
+ authType: types_1.FlexbeAuthType.API_KEY,
23
+ };
18
24
  this.config = {
19
- baseUrl: config?.baseUrl || getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
20
- timeout: config?.timeout || 30000,
21
- apiKey: config?.apiKey || getEnvVar('FLEXBE_API_KEY') || '',
22
- authType: config?.authType || types_1.FlexbeAuthType.API_KEY,
25
+ ...defaultConfig,
26
+ ...config,
23
27
  };
24
28
  if (this.config.authType === 'apiKey' && !this.config.apiKey) {
25
29
  throw new Error('API key is required when using apiKey authentication. Please provide it either through config or FLEXBE_API_KEY environment variable.');
@@ -1,102 +1,79 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TokenManager = void 0;
4
- const TOKEN_STORAGE_KEY = 'flexbe_jwt_token';
5
- const REFRESH_THRESHOLD = 0.8; // Refresh when 80% of token lifetime has passed
6
- const REFRESH_CHECK_INTERVAL = 30 * 1000; // Check every 30 seconds
7
- const MAX_REFRESH_DELAY = 10000; // Maximum random delay of 10 seconds
4
+ const types_1 = require("../types");
5
+ const TOKEN_STORAGE_KEY = 'flexbe_jwt';
6
+ const TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; // update token 5 minutes before expiration
8
7
  class TokenManager {
9
8
  constructor() {
10
- this.token = null;
11
- this.refreshInterval = null;
12
- this.refreshTimeout = null;
13
9
  this.tokenPromise = null;
14
- this.debug = false;
15
- this.initializeFromStorage();
16
- this.setupStorageListener();
17
10
  }
18
11
  static getInstance() {
19
12
  if (!TokenManager.instance) {
20
13
  TokenManager.instance = new TokenManager();
21
14
  }
15
+ if (typeof window !== 'undefined') {
16
+ // Clean up old token storage place
17
+ // TODO remove this after June 1, 2025
18
+ localStorage.removeItem('flexbe_jwt_token');
19
+ ;
20
+ }
22
21
  return TokenManager.instance;
23
22
  }
24
- initializeFromStorage() {
25
- if (typeof window === 'undefined')
26
- return;
27
- const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
28
- if (!storedToken)
23
+ async getToken() {
24
+ const token = this.getStoredToken();
25
+ if (token && token.expiresAt > Date.now()) {
26
+ // TODO check if token expire less that 1 minute
27
+ // if so, retrieve a new token
28
+ if (token.expiresAt - Date.now() < TOKEN_REFRESH_THRESHOLD) {
29
+ void this.retrieveToken();
30
+ }
31
+ return token.accessToken;
32
+ }
33
+ await this.retrieveToken();
34
+ const retrievedToken = this.getStoredToken();
35
+ return retrievedToken?.accessToken ?? null;
36
+ }
37
+ async revokeToken() {
38
+ const token = this.getStoredToken();
39
+ this.clearToken();
40
+ if (!token)
29
41
  return;
30
42
  try {
31
- this.token = JSON.parse(storedToken);
32
- if (this.token.expiresAt > Date.now()) {
33
- this.logTokenStatus('Token loaded from storage');
34
- this.startRefreshInterval();
35
- }
36
- else {
37
- this.clearToken();
38
- }
43
+ const controller = new AbortController();
44
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
45
+ await fetch('/oauth/revoke', {
46
+ method: 'POST',
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ 'Authorization': `Bearer ${token.accessToken}`
50
+ },
51
+ body: JSON.stringify({ token: token.accessToken }),
52
+ credentials: 'include',
53
+ signal: controller.signal,
54
+ });
55
+ clearTimeout(timeoutId);
39
56
  }
40
57
  catch (error) {
41
- console.error('Failed to parse stored token:', error);
42
- this.clearToken();
58
+ console.error('Failed to revoke token:', error);
43
59
  }
44
60
  }
45
- setupStorageListener() {
61
+ getStoredToken() {
46
62
  if (typeof window === 'undefined')
47
- return;
48
- window.addEventListener('storage', (event) => {
49
- if (event.key !== TOKEN_STORAGE_KEY)
50
- return;
51
- if (!event.newValue) {
52
- this.clearToken();
53
- // void this.retrieveToken();
54
- return;
55
- }
56
- try {
57
- const newToken = JSON.parse(event.newValue);
58
- // Skip if the new token is exactly the same as current token
59
- if (this.token &&
60
- this.token.accessToken === newToken.accessToken &&
61
- this.token.expiresAt === newToken.expiresAt) {
62
- return;
63
- }
64
- if (newToken.expiresAt > Date.now()) {
65
- this.token = newToken;
66
- this.logTokenStatus('Token updated from storage');
67
- this.startRefreshInterval();
68
- }
69
- else {
70
- this.clearToken();
71
- void this.retrieveToken();
72
- }
73
- }
74
- catch (error) {
75
- console.error('Failed to parse token from storage event:', error);
76
- this.clearToken();
77
- void this.retrieveToken();
78
- }
79
- });
80
- }
81
- getExpirationFromToken(token) {
63
+ return null;
64
+ const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
65
+ if (!storedToken) {
66
+ return null;
67
+ }
82
68
  try {
83
- const [, payload] = token.split('.');
84
- const decodedPayload = JSON.parse(atob(payload));
85
- return decodedPayload.exp * 1000; // Convert to milliseconds
69
+ const token = JSON.parse(storedToken);
70
+ return token;
86
71
  }
87
72
  catch (error) {
88
- console.error('Failed to parse token expiration:', error);
89
- return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
73
+ console.error('getStoredToken: Failed to parse stored token:', error);
74
+ return null;
90
75
  }
91
76
  }
92
- async getToken() {
93
- const token = this.token;
94
- if (token && token.expiresAt && token.expiresAt > Date.now()) {
95
- return token.accessToken;
96
- }
97
- await this.retrieveToken();
98
- return this.token?.accessToken ?? null;
99
- }
100
77
  async retrieveToken() {
101
78
  if (this.tokenPromise) {
102
79
  await this.tokenPromise;
@@ -124,132 +101,44 @@ class TokenManager {
124
101
  clearTimeout(timeoutId);
125
102
  if (!response.ok) {
126
103
  const errorData = await response.json().catch(() => ({ message: response.statusText }));
104
+ if (response.status === 401) {
105
+ throw new types_1.UnauthorizedException(errorData.message || response.statusText);
106
+ }
127
107
  throw new Error(errorData.message || response.statusText);
128
108
  }
129
109
  const data = await response.json();
130
110
  this.setToken(data);
131
111
  }
132
112
  catch (error) {
133
- console.error('Failed to retrieve token:', error);
134
- this.clearToken();
135
- // Schedule a retry after REFRESH_CHECK_INTERVAL
136
- setTimeout(() => {
137
- void this.retrieveToken();
138
- }, REFRESH_CHECK_INTERVAL);
139
113
  throw error;
140
114
  }
141
115
  }
142
- startRefreshInterval() {
143
- this.clearRefreshTimers();
144
- if (!this.token)
145
- return;
146
- const tokenLifetime = this.token.expiresAt - Date.now();
147
- const refreshThreshold = Math.round(tokenLifetime * REFRESH_THRESHOLD);
148
- const timeUntilRefresh = refreshThreshold - (Math.random() * MAX_REFRESH_DELAY);
149
- this.logTokenStatus('Starting refresh interval', {
150
- tokenLifetime: `${Math.round(tokenLifetime / 1000)} seconds`,
151
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
152
- timeUntilRefresh: `${Math.round(timeUntilRefresh / 1000)} seconds`,
153
- });
154
- this.scheduleRefresh(timeUntilRefresh);
155
- this.refreshInterval = window.setInterval(() => {
156
- if (!this.token)
157
- return;
158
- const timeUntilExpiry = this.token.expiresAt - Date.now();
159
- if (timeUntilExpiry <= 0) {
160
- this.logTokenStatus('Token expired');
161
- this.clearToken();
162
- void this.retrieveToken();
163
- return;
164
- }
165
- const refreshThreshold = Math.round(timeUntilExpiry * REFRESH_THRESHOLD);
166
- if (timeUntilExpiry <= refreshThreshold) {
167
- this.logTokenStatus('Refreshing token', {
168
- timeUntilExpiry: `${Math.round(timeUntilExpiry / 1000)} seconds`,
169
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
170
- });
171
- this.scheduleRefresh(refreshThreshold - (Math.random() * MAX_REFRESH_DELAY));
172
- }
173
- }, REFRESH_CHECK_INTERVAL);
174
- }
175
- scheduleRefresh(delay) {
176
- if (this.refreshTimeout) {
177
- window.clearTimeout(this.refreshTimeout);
178
- }
179
- this.refreshTimeout = window.setTimeout(() => {
180
- const token = this.token;
181
- if (token && token.expiresAt - Date.now() <= token.expiresAt * REFRESH_THRESHOLD) {
182
- void this.retrieveToken();
183
- }
184
- }, delay);
185
- }
186
- clearRefreshTimers() {
187
- if (this.refreshInterval) {
188
- clearInterval(this.refreshInterval);
189
- this.refreshInterval = null;
190
- }
191
- if (this.refreshTimeout) {
192
- window.clearTimeout(this.refreshTimeout);
193
- this.refreshTimeout = null;
194
- }
195
- }
196
- logTokenStatus(message, additionalInfo = {}) {
197
- if (!this.debug)
198
- return;
199
- const token = this.token;
200
- if (!token)
201
- return;
202
- console.log(message, {
203
- expiresIn: `${Math.round((token.expiresAt - Date.now()) / 1000)} seconds`,
204
- expiresAt: new Date(token.expiresAt).toISOString(),
205
- ...additionalInfo,
206
- });
207
- }
208
116
  setToken(tokenResponse) {
209
117
  const expiresAt = this.getExpirationFromToken(tokenResponse.accessToken);
210
- this.token = {
118
+ const token = {
211
119
  accessToken: tokenResponse.accessToken,
212
120
  expiresAt,
213
121
  };
214
- this.logTokenStatus('Token set', {
215
- expiresAt: new Date(expiresAt).toISOString(),
216
- });
217
122
  if (typeof window !== 'undefined') {
218
- localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(this.token));
123
+ localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(token));
219
124
  }
220
- this.startRefreshInterval();
221
125
  }
222
- clearToken() {
223
- this.token = null;
224
- this.clearRefreshTimers();
225
- if (typeof window !== 'undefined') {
226
- localStorage.removeItem(TOKEN_STORAGE_KEY);
227
- }
228
- }
229
- async revokeToken() {
230
- const token = this.token;
231
- this.clearToken();
232
- if (!token)
233
- return;
126
+ getExpirationFromToken(token) {
234
127
  try {
235
- const controller = new AbortController();
236
- const timeoutId = setTimeout(() => controller.abort(), 30000);
237
- await fetch('/oauth/revoke', {
238
- method: 'POST',
239
- headers: {
240
- 'Content-Type': 'application/json',
241
- 'Authorization': `Bearer ${token.accessToken}`
242
- },
243
- body: JSON.stringify({ token: token.accessToken }),
244
- credentials: 'include',
245
- signal: controller.signal,
246
- });
247
- clearTimeout(timeoutId);
128
+ const [, payload] = token.split('.');
129
+ const decodedPayload = JSON.parse(atob(payload));
130
+ return decodedPayload.exp * 1000; // Convert to milliseconds
248
131
  }
249
132
  catch (error) {
250
- console.error('Failed to revoke token:', error);
251
- // Even if revocation fails, we still want to clear the local token
133
+ console.error('getExpirationFromToken: Failed to parse token expiration:', error);
134
+ return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
135
+ }
136
+ }
137
+ clearToken() {
138
+ if (typeof window === 'undefined') {
139
+ return;
252
140
  }
141
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
253
142
  }
254
143
  }
255
144
  exports.TokenManager = TokenManager;
@@ -1,9 +1,27 @@
1
- import { NotFoundException, ForbiddenException, BadRequestException, UnauthorizedException, ServerException, TimeoutException } from '../types';
2
- import { FlexbeAuth } from './auth';
1
+ import { NotFoundException, ForbiddenException, BadRequestException, UnauthorizedException, ServerException, TimeoutException, FlexbeAuthType } from '../types';
2
+ import { TokenManager } from './token-manager';
3
3
  export class ApiClient {
4
4
  constructor(config) {
5
5
  this.config = config;
6
- this.auth = new FlexbeAuth(config);
6
+ this.tokenManager = TokenManager.getInstance();
7
+ if (this.config.authType === FlexbeAuthType.BEARER) {
8
+ // Start initialization but don't wait for it
9
+ void this.tokenManager.getToken(); // just warm up the token manager before any request
10
+ }
11
+ }
12
+ async getAuthHeaders() {
13
+ const headers = {};
14
+ if (this.config.authType === FlexbeAuthType.API_KEY) {
15
+ headers['x-api-key'] = this.config.apiKey;
16
+ }
17
+ else if (this.config.authType === FlexbeAuthType.BEARER) {
18
+ const token = await this.tokenManager.getToken();
19
+ if (!token) {
20
+ throw new Error('No valid bearer token available');
21
+ }
22
+ headers['Authorization'] = `Bearer ${token}`;
23
+ }
24
+ return headers;
7
25
  }
8
26
  buildUrl(path, params) {
9
27
  const searchParams = new URLSearchParams();
@@ -18,12 +36,12 @@ export class ApiClient {
18
36
  }
19
37
  async request(config) {
20
38
  try {
21
- await this.auth.ensureInitialized();
22
39
  const controller = new AbortController();
23
40
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
24
41
  const url = this.buildUrl(config.url, config.params);
25
42
  const headers = {
26
- ...(await this.auth.getAuthHeaders()),
43
+ 'Content-Type': 'application/json',
44
+ ...(await this.getAuthHeaders()),
27
45
  ...config.headers,
28
46
  };
29
47
  const response = await fetch(this.config.baseUrl + url, {
@@ -78,6 +96,9 @@ export class ApiClient {
78
96
  };
79
97
  }
80
98
  catch (error) {
99
+ if (error instanceof UnauthorizedException) {
100
+ this.config.hooks?.onUnauthorized?.();
101
+ }
81
102
  if (error instanceof Error && error.name === 'AbortError') {
82
103
  throw new TimeoutException('Request timeout');
83
104
  }
@@ -12,11 +12,15 @@ export class FlexbeClient {
12
12
  }
13
13
  return undefined;
14
14
  };
15
+ const defaultConfig = {
16
+ baseUrl: getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
17
+ timeout: 30000,
18
+ apiKey: getEnvVar('FLEXBE_API_KEY') || '',
19
+ authType: FlexbeAuthType.API_KEY,
20
+ };
15
21
  this.config = {
16
- baseUrl: config?.baseUrl || getEnvVar('FLEXBE_API_URL') || 'https://api.flexbe.com',
17
- timeout: config?.timeout || 30000,
18
- apiKey: config?.apiKey || getEnvVar('FLEXBE_API_KEY') || '',
19
- authType: config?.authType || FlexbeAuthType.API_KEY,
22
+ ...defaultConfig,
23
+ ...config,
20
24
  };
21
25
  if (this.config.authType === 'apiKey' && !this.config.apiKey) {
22
26
  throw new Error('API key is required when using apiKey authentication. Please provide it either through config or FLEXBE_API_KEY environment variable.');
@@ -1,99 +1,76 @@
1
- const TOKEN_STORAGE_KEY = 'flexbe_jwt_token';
2
- const REFRESH_THRESHOLD = 0.8; // Refresh when 80% of token lifetime has passed
3
- const REFRESH_CHECK_INTERVAL = 30 * 1000; // Check every 30 seconds
4
- const MAX_REFRESH_DELAY = 10000; // Maximum random delay of 10 seconds
1
+ import { UnauthorizedException } from '../types';
2
+ const TOKEN_STORAGE_KEY = 'flexbe_jwt';
3
+ const TOKEN_REFRESH_THRESHOLD = 5 * 60 * 1000; // update token 5 minutes before expiration
5
4
  export class TokenManager {
6
5
  constructor() {
7
- this.token = null;
8
- this.refreshInterval = null;
9
- this.refreshTimeout = null;
10
6
  this.tokenPromise = null;
11
- this.debug = false;
12
- this.initializeFromStorage();
13
- this.setupStorageListener();
14
7
  }
15
8
  static getInstance() {
16
9
  if (!TokenManager.instance) {
17
10
  TokenManager.instance = new TokenManager();
18
11
  }
12
+ if (typeof window !== 'undefined') {
13
+ // Clean up old token storage place
14
+ // TODO remove this after June 1, 2025
15
+ localStorage.removeItem('flexbe_jwt_token');
16
+ ;
17
+ }
19
18
  return TokenManager.instance;
20
19
  }
21
- initializeFromStorage() {
22
- if (typeof window === 'undefined')
23
- return;
24
- const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
25
- if (!storedToken)
20
+ async getToken() {
21
+ const token = this.getStoredToken();
22
+ if (token && token.expiresAt > Date.now()) {
23
+ // TODO check if token expire less that 1 minute
24
+ // if so, retrieve a new token
25
+ if (token.expiresAt - Date.now() < TOKEN_REFRESH_THRESHOLD) {
26
+ void this.retrieveToken();
27
+ }
28
+ return token.accessToken;
29
+ }
30
+ await this.retrieveToken();
31
+ const retrievedToken = this.getStoredToken();
32
+ return retrievedToken?.accessToken ?? null;
33
+ }
34
+ async revokeToken() {
35
+ const token = this.getStoredToken();
36
+ this.clearToken();
37
+ if (!token)
26
38
  return;
27
39
  try {
28
- this.token = JSON.parse(storedToken);
29
- if (this.token.expiresAt > Date.now()) {
30
- this.logTokenStatus('Token loaded from storage');
31
- this.startRefreshInterval();
32
- }
33
- else {
34
- this.clearToken();
35
- }
40
+ const controller = new AbortController();
41
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
42
+ await fetch('/oauth/revoke', {
43
+ method: 'POST',
44
+ headers: {
45
+ 'Content-Type': 'application/json',
46
+ 'Authorization': `Bearer ${token.accessToken}`
47
+ },
48
+ body: JSON.stringify({ token: token.accessToken }),
49
+ credentials: 'include',
50
+ signal: controller.signal,
51
+ });
52
+ clearTimeout(timeoutId);
36
53
  }
37
54
  catch (error) {
38
- console.error('Failed to parse stored token:', error);
39
- this.clearToken();
55
+ console.error('Failed to revoke token:', error);
40
56
  }
41
57
  }
42
- setupStorageListener() {
58
+ getStoredToken() {
43
59
  if (typeof window === 'undefined')
44
- return;
45
- window.addEventListener('storage', (event) => {
46
- if (event.key !== TOKEN_STORAGE_KEY)
47
- return;
48
- if (!event.newValue) {
49
- this.clearToken();
50
- // void this.retrieveToken();
51
- return;
52
- }
53
- try {
54
- const newToken = JSON.parse(event.newValue);
55
- // Skip if the new token is exactly the same as current token
56
- if (this.token &&
57
- this.token.accessToken === newToken.accessToken &&
58
- this.token.expiresAt === newToken.expiresAt) {
59
- return;
60
- }
61
- if (newToken.expiresAt > Date.now()) {
62
- this.token = newToken;
63
- this.logTokenStatus('Token updated from storage');
64
- this.startRefreshInterval();
65
- }
66
- else {
67
- this.clearToken();
68
- void this.retrieveToken();
69
- }
70
- }
71
- catch (error) {
72
- console.error('Failed to parse token from storage event:', error);
73
- this.clearToken();
74
- void this.retrieveToken();
75
- }
76
- });
77
- }
78
- getExpirationFromToken(token) {
60
+ return null;
61
+ const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY);
62
+ if (!storedToken) {
63
+ return null;
64
+ }
79
65
  try {
80
- const [, payload] = token.split('.');
81
- const decodedPayload = JSON.parse(atob(payload));
82
- return decodedPayload.exp * 1000; // Convert to milliseconds
66
+ const token = JSON.parse(storedToken);
67
+ return token;
83
68
  }
84
69
  catch (error) {
85
- console.error('Failed to parse token expiration:', error);
86
- return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
70
+ console.error('getStoredToken: Failed to parse stored token:', error);
71
+ return null;
87
72
  }
88
73
  }
89
- async getToken() {
90
- const token = this.token;
91
- if (token && token.expiresAt && token.expiresAt > Date.now()) {
92
- return token.accessToken;
93
- }
94
- await this.retrieveToken();
95
- return this.token?.accessToken ?? null;
96
- }
97
74
  async retrieveToken() {
98
75
  if (this.tokenPromise) {
99
76
  await this.tokenPromise;
@@ -121,131 +98,43 @@ export class TokenManager {
121
98
  clearTimeout(timeoutId);
122
99
  if (!response.ok) {
123
100
  const errorData = await response.json().catch(() => ({ message: response.statusText }));
101
+ if (response.status === 401) {
102
+ throw new UnauthorizedException(errorData.message || response.statusText);
103
+ }
124
104
  throw new Error(errorData.message || response.statusText);
125
105
  }
126
106
  const data = await response.json();
127
107
  this.setToken(data);
128
108
  }
129
109
  catch (error) {
130
- console.error('Failed to retrieve token:', error);
131
- this.clearToken();
132
- // Schedule a retry after REFRESH_CHECK_INTERVAL
133
- setTimeout(() => {
134
- void this.retrieveToken();
135
- }, REFRESH_CHECK_INTERVAL);
136
110
  throw error;
137
111
  }
138
112
  }
139
- startRefreshInterval() {
140
- this.clearRefreshTimers();
141
- if (!this.token)
142
- return;
143
- const tokenLifetime = this.token.expiresAt - Date.now();
144
- const refreshThreshold = Math.round(tokenLifetime * REFRESH_THRESHOLD);
145
- const timeUntilRefresh = refreshThreshold - (Math.random() * MAX_REFRESH_DELAY);
146
- this.logTokenStatus('Starting refresh interval', {
147
- tokenLifetime: `${Math.round(tokenLifetime / 1000)} seconds`,
148
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
149
- timeUntilRefresh: `${Math.round(timeUntilRefresh / 1000)} seconds`,
150
- });
151
- this.scheduleRefresh(timeUntilRefresh);
152
- this.refreshInterval = window.setInterval(() => {
153
- if (!this.token)
154
- return;
155
- const timeUntilExpiry = this.token.expiresAt - Date.now();
156
- if (timeUntilExpiry <= 0) {
157
- this.logTokenStatus('Token expired');
158
- this.clearToken();
159
- void this.retrieveToken();
160
- return;
161
- }
162
- const refreshThreshold = Math.round(timeUntilExpiry * REFRESH_THRESHOLD);
163
- if (timeUntilExpiry <= refreshThreshold) {
164
- this.logTokenStatus('Refreshing token', {
165
- timeUntilExpiry: `${Math.round(timeUntilExpiry / 1000)} seconds`,
166
- refreshThreshold: `${Math.round(refreshThreshold / 1000)} seconds`,
167
- });
168
- this.scheduleRefresh(refreshThreshold - (Math.random() * MAX_REFRESH_DELAY));
169
- }
170
- }, REFRESH_CHECK_INTERVAL);
171
- }
172
- scheduleRefresh(delay) {
173
- if (this.refreshTimeout) {
174
- window.clearTimeout(this.refreshTimeout);
175
- }
176
- this.refreshTimeout = window.setTimeout(() => {
177
- const token = this.token;
178
- if (token && token.expiresAt - Date.now() <= token.expiresAt * REFRESH_THRESHOLD) {
179
- void this.retrieveToken();
180
- }
181
- }, delay);
182
- }
183
- clearRefreshTimers() {
184
- if (this.refreshInterval) {
185
- clearInterval(this.refreshInterval);
186
- this.refreshInterval = null;
187
- }
188
- if (this.refreshTimeout) {
189
- window.clearTimeout(this.refreshTimeout);
190
- this.refreshTimeout = null;
191
- }
192
- }
193
- logTokenStatus(message, additionalInfo = {}) {
194
- if (!this.debug)
195
- return;
196
- const token = this.token;
197
- if (!token)
198
- return;
199
- console.log(message, {
200
- expiresIn: `${Math.round((token.expiresAt - Date.now()) / 1000)} seconds`,
201
- expiresAt: new Date(token.expiresAt).toISOString(),
202
- ...additionalInfo,
203
- });
204
- }
205
113
  setToken(tokenResponse) {
206
114
  const expiresAt = this.getExpirationFromToken(tokenResponse.accessToken);
207
- this.token = {
115
+ const token = {
208
116
  accessToken: tokenResponse.accessToken,
209
117
  expiresAt,
210
118
  };
211
- this.logTokenStatus('Token set', {
212
- expiresAt: new Date(expiresAt).toISOString(),
213
- });
214
119
  if (typeof window !== 'undefined') {
215
- localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(this.token));
120
+ localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(token));
216
121
  }
217
- this.startRefreshInterval();
218
122
  }
219
- clearToken() {
220
- this.token = null;
221
- this.clearRefreshTimers();
222
- if (typeof window !== 'undefined') {
223
- localStorage.removeItem(TOKEN_STORAGE_KEY);
224
- }
225
- }
226
- async revokeToken() {
227
- const token = this.token;
228
- this.clearToken();
229
- if (!token)
230
- return;
123
+ getExpirationFromToken(token) {
231
124
  try {
232
- const controller = new AbortController();
233
- const timeoutId = setTimeout(() => controller.abort(), 30000);
234
- await fetch('/oauth/revoke', {
235
- method: 'POST',
236
- headers: {
237
- 'Content-Type': 'application/json',
238
- 'Authorization': `Bearer ${token.accessToken}`
239
- },
240
- body: JSON.stringify({ token: token.accessToken }),
241
- credentials: 'include',
242
- signal: controller.signal,
243
- });
244
- clearTimeout(timeoutId);
125
+ const [, payload] = token.split('.');
126
+ const decodedPayload = JSON.parse(atob(payload));
127
+ return decodedPayload.exp * 1000; // Convert to milliseconds
245
128
  }
246
129
  catch (error) {
247
- console.error('Failed to revoke token:', error);
248
- // Even if revocation fails, we still want to clear the local token
130
+ console.error('getExpirationFromToken: Failed to parse token expiration:', error);
131
+ return Date.now() + (4 * 60 * 1000); // Default to 4 minutes if parsing fails
132
+ }
133
+ }
134
+ clearToken() {
135
+ if (typeof window === 'undefined') {
136
+ return;
249
137
  }
138
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
250
139
  }
251
140
  }
@@ -1,8 +1,9 @@
1
1
  import { FlexbeConfig, FlexbeResponse } from '../types';
2
2
  export declare class ApiClient {
3
3
  private readonly config;
4
- private readonly auth;
4
+ private readonly tokenManager;
5
5
  constructor(config: FlexbeConfig);
6
+ private getAuthHeaders;
6
7
  private buildUrl;
7
8
  private request;
8
9
  get<T>(url: string, config?: RequestInit & {
@@ -1,24 +1,13 @@
1
- import { TokenResponse } from '../types';
2
1
  export declare class TokenManager {
3
2
  private static instance;
4
- private token;
5
- private refreshInterval;
6
- private refreshTimeout;
7
3
  private tokenPromise;
8
- private debug;
9
- private constructor();
10
4
  static getInstance(): TokenManager;
11
- private initializeFromStorage;
12
- private setupStorageListener;
13
- private getExpirationFromToken;
14
5
  getToken(): Promise<string | null>;
6
+ revokeToken(): Promise<void>;
7
+ private getStoredToken;
15
8
  private retrieveToken;
16
9
  private doRetrieveToken;
17
- private startRefreshInterval;
18
- private scheduleRefresh;
19
- private clearRefreshTimers;
20
- private logTokenStatus;
21
- setToken(tokenResponse: TokenResponse): void;
22
- clearToken(): void;
23
- revokeToken(): Promise<void>;
10
+ private setToken;
11
+ private getExpirationFromToken;
12
+ private clearToken;
24
13
  }
@@ -8,6 +8,9 @@ export interface FlexbeConfig {
8
8
  timeout?: number;
9
9
  siteId?: string;
10
10
  authType?: FlexbeAuthType;
11
+ hooks?: {
12
+ onUnauthorized?: () => void;
13
+ };
11
14
  }
12
15
  export interface FlexbeResponse<T> {
13
16
  data: T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexbe/sdk",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "TypeScript SDK for Flexbe API",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -23,9 +23,7 @@
23
23
  "lint": "eslint src --ext .ts",
24
24
  "format": "prettier --write \"src/**/*.ts\"",
25
25
  "prepare": "npm run build",
26
- "prepublishOnly": "npm test && npm run lint",
27
- "version": "npm version",
28
- "publish": "npm publish --access public"
26
+ "prepublishOnly": "npm test && npm run lint"
29
27
  },
30
28
  "keywords": [
31
29
  "flexbe",