space-react-client 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,12 +22,13 @@ function isTokenExpired(tokenPayload) {
22
22
  class TokenService {
23
23
  constructor() {
24
24
  this.tokenPayload = null;
25
+ this.listeners = new Set();
25
26
  }
26
27
  /**
27
28
  * Retrieves the stored pricing token's payload.
28
29
  * @returns The stored pricing token payload.
29
30
  */
30
- getPricingToken() {
31
+ getPayload() {
31
32
  if (!this._validToken()) {
32
33
  return null;
33
34
  }
@@ -38,7 +39,7 @@ class TokenService {
38
39
  * @param key - A key of the stored pricing token whose is going to be retrieved.
39
40
  * @return The value of the key in the stored pricing token payload.
40
41
  */
41
- getFromToken(key) {
42
+ getKey(key) {
42
43
  if (!this._validToken()) {
43
44
  return null;
44
45
  }
@@ -48,9 +49,10 @@ class TokenService {
48
49
  * Updates the stored pricing token with the payload of a new one.
49
50
  * @param token - Pricing token string
50
51
  */
51
- updatePricingToken(token) {
52
+ update(token) {
52
53
  const parsedToken = parseJwt(token);
53
54
  this.tokenPayload = parsedToken;
55
+ this._notify();
54
56
  }
55
57
  evaluateFeature(featureId) {
56
58
  if (!this._validToken()) {
@@ -76,6 +78,26 @@ class TokenService {
76
78
  }
77
79
  return true;
78
80
  }
81
+ /**
82
+ * Subscribe to pricing token updates. Returns an unsubscribe function.
83
+ */
84
+ subscribe(listener) {
85
+ this.listeners.add(listener);
86
+ return () => {
87
+ this.listeners.delete(listener);
88
+ };
89
+ }
90
+ /** Notify all listeners that the token has changed. */
91
+ _notify() {
92
+ this.listeners.forEach((l) => {
93
+ try {
94
+ l();
95
+ }
96
+ catch (e) {
97
+ console.error(e);
98
+ }
99
+ });
100
+ }
79
101
  }
80
102
 
81
103
  /**
@@ -96,7 +118,7 @@ class SpaceClient {
96
118
  this.pricingSocketNamespace = this.socketClient.io.socket('/pricings');
97
119
  this.apiKey = config.apiKey;
98
120
  this.emitter = new TinyEmitter();
99
- this.tokenService = new TokenService();
121
+ this.token = new TokenService();
100
122
  this.axios = axios.create({
101
123
  baseURL: this.httpUrl,
102
124
  headers: {
@@ -189,7 +211,7 @@ class SpaceClient {
189
211
  }
190
212
  this.userId = userId;
191
213
  const userPricingToken = await this.generateUserPricingToken();
192
- this.tokenService.updatePricingToken(userPricingToken);
214
+ this.token.update(userPricingToken);
193
215
  }
194
216
  /**
195
217
  * Performs a request to SPACE to retrieve a new pricing token for the user with the given userId.
@@ -228,13 +250,13 @@ const SpaceProvider = ({ config, children, }) => {
228
250
  tokenService = new TokenService();
229
251
  }
230
252
  else {
231
- tokenService = client.tokenService;
253
+ tokenService = client.token;
232
254
  }
233
255
  return {
234
256
  client: client,
235
257
  tokenService: tokenService,
236
258
  };
237
- }, [config.url, config.apiKey]);
259
+ }, [config.url, config.apiKey, config.allowConnectionWithSpace]);
238
260
  useEffect(() => {
239
261
  return () => {
240
262
  if (context.client && typeof context.client.disconnectWebSocket === 'function') {
@@ -267,14 +289,33 @@ function useSpaceClient() {
267
289
  * Custom hook to access the service that manages the pricing token.
268
290
  * Throws an error if used outside of SpaceProvider.
269
291
  */
270
- function usePricingToken() {
292
+ function useTokenService() {
271
293
  const spaceContext = useContext(SpaceContext);
272
294
  if (!spaceContext) {
273
- throw new Error('usePricingToken must be used within a SpaceProvider');
295
+ throw new Error('useTokenService must be used within a SpaceProvider');
274
296
  }
275
297
  return spaceContext.tokenService;
276
298
  }
277
299
 
300
+ /**
301
+ * React hook that returns the current pricing token payload and
302
+ * re-renders when the token changes.
303
+ */
304
+ function usePricingTokenPayload() {
305
+ const tokenService = useTokenService();
306
+ const [payload, setPayload] = useState(() => tokenService.getPayload());
307
+ useEffect(() => {
308
+ // Ensure latest value on mount
309
+ setPayload(tokenService.getPayload());
310
+ // Subscribe to token updates
311
+ const unsubscribe = tokenService.subscribe(() => {
312
+ setPayload(tokenService.getPayload());
313
+ });
314
+ return () => unsubscribe();
315
+ }, [tokenService]);
316
+ return payload;
317
+ }
318
+
278
319
  // Generic wrapper for feature children
279
320
  function On({ children }) {
280
321
  return jsx(Fragment, { children: children });
@@ -294,31 +335,43 @@ function getChildrenOfType(children, type) {
294
335
  return match ? match.props.children : null;
295
336
  }
296
337
  const Feature = ({ id, children }) => {
297
- const tokenService = usePricingToken();
338
+ const tokenService = useTokenService();
339
+ const tokenPayload = usePricingTokenPayload();
298
340
  const [status, setStatus] = useState('loading');
299
341
  const [result, setResult] = useState(null);
300
342
  // Validate id
301
343
  const isValidId = useMemo(() => id.includes('-'), [id]);
302
344
  useEffect(() => {
303
- if (!isValidId) {
304
- setStatus('error');
305
- return;
306
- }
307
- if (tokenService.getPricingToken() === null) {
308
- setStatus('error');
309
- return;
310
- }
311
- setStatus('loading');
312
- setResult(null);
313
- const evaluationResult = tokenService.evaluateFeature(id);
314
- if (evaluationResult === null || evaluationResult === undefined) {
315
- setStatus('error');
316
- }
317
- else {
318
- setResult(evaluationResult);
319
- setStatus('success');
320
- }
321
- }, [id, isValidId, tokenService.tokenPayload]);
345
+ const evaluate = () => {
346
+ if (!isValidId) {
347
+ setStatus('error');
348
+ return;
349
+ }
350
+ if (tokenService.getPayload() === null) {
351
+ setStatus('error');
352
+ return;
353
+ }
354
+ setStatus('loading');
355
+ setResult(null);
356
+ const evaluationResult = tokenService.evaluateFeature(id);
357
+ if (evaluationResult === null || evaluationResult === undefined) {
358
+ setStatus('error');
359
+ }
360
+ else {
361
+ setResult(evaluationResult);
362
+ setStatus('success');
363
+ }
364
+ };
365
+ // Initial evaluation
366
+ evaluate();
367
+ // Subscribe to token changes to re-evaluate
368
+ const unsubscribe = tokenService.subscribe(() => {
369
+ evaluate();
370
+ });
371
+ return () => {
372
+ unsubscribe();
373
+ };
374
+ }, [id, isValidId, tokenPayload, tokenService]);
322
375
  if (status === 'loading') {
323
376
  return jsx(Fragment, { children: getChildrenOfType(children, Loading) });
324
377
  }
@@ -334,4 +387,4 @@ const Feature = ({ id, children }) => {
334
387
  return jsx(Fragment, {});
335
388
  };
336
389
 
337
- export { Default, ErrorFallback, Feature, Loading, On, SpaceProvider, usePricingToken, useSpaceClient };
390
+ export { Default, ErrorFallback, Feature, Loading, On, SpaceProvider, usePricingTokenPayload, useSpaceClient, useTokenService };
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "space-react-client",
3
3
  "type": "module",
4
- "version": "0.2.4",
4
+ "version": "0.3.0",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/index.d.ts",
11
12
  "import": "./dist/index.js",
12
13
  "require": "./dist/index.js"
13
14
  }