space-react-client 0.1.0 → 0.2.1

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.d.ts CHANGED
@@ -40,10 +40,11 @@ declare class SpaceClient$1 {
40
40
  readonly tokenService: TokenService$1;
41
41
  private userId;
42
42
  constructor(config: SpaceConfiguration);
43
+ disconnectWebSocket(): void;
43
44
  /**
44
45
  * Connects to the SPACE WebSocket and handles incoming events.
45
46
  */
46
- private connectWebSocket;
47
+ connectWebSocket(): void;
47
48
  /**
48
49
  * Listen to SPACE and connection events.
49
50
  * @param event The event key to listen for.
@@ -108,9 +109,8 @@ interface SpaceClientContext{
108
109
  /**
109
110
  * SpaceProvider initializes and provides the SpaceClient instance to children.
110
111
  */
111
- declare const SpaceProvider: ({ config, loader, children, }: {
112
+ declare const SpaceProvider: ({ config, children, }: {
112
113
  config: SpaceConfiguration;
113
- loader?: React.ReactNode;
114
114
  children: React.ReactNode;
115
115
  }) => JSX.Element;
116
116
 
@@ -130,7 +130,19 @@ interface FeatureProps {
130
130
  id: string;
131
131
  children: React.ReactNode;
132
132
  }
133
+ declare function On({ children }: {
134
+ children: React.ReactNode;
135
+ }): JSX.Element;
136
+ declare function Default({ children }: {
137
+ children: React.ReactNode;
138
+ }): JSX.Element;
139
+ declare function Loading({ children }: {
140
+ children: React.ReactNode;
141
+ }): JSX.Element;
142
+ declare function ErrorFallback({ children }: {
143
+ children: React.ReactNode;
144
+ }): JSX.Element;
133
145
  declare const Feature: ({ id, children }: FeatureProps) => JSX.Element;
134
146
 
135
- export { Feature, SpaceClient$1 as SpaceClient, SpaceProvider, TokenService$1 as TokenService, usePricingToken, useSpaceClient };
147
+ export { Default, ErrorFallback, Feature, Loading, On, SpaceClient$1 as SpaceClient, SpaceProvider, TokenService$1 as TokenService, usePricingToken, useSpaceClient };
136
148
  export type { EventMessage, SpaceClientContext, SpaceConfiguration, SpaceEvents };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import React, { createContext, useMemo, useContext, useState, useEffect } from 'react';
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import React, { createContext, useMemo, useEffect, useContext, useState } from 'react';
3
3
  import { TinyEmitter } from 'tiny-emitter';
4
4
  import axios from 'axios';
5
5
  import { io } from 'socket.io-client';
@@ -105,6 +105,12 @@ class SpaceClient {
105
105
  });
106
106
  this.connectWebSocket();
107
107
  }
108
+ disconnectWebSocket() {
109
+ if (this.pricingSocketNamespace.connected) {
110
+ this.pricingSocketNamespace.disconnect();
111
+ this.emitter.off(); // Remove all listeners
112
+ }
113
+ }
108
114
  /**
109
115
  * Connects to the SPACE WebSocket and handles incoming events.
110
116
  */
@@ -113,11 +119,11 @@ class SpaceClient {
113
119
  console.log('Connected to SPACE');
114
120
  this.emitter.emit('synchronized', 'WebSocket connection established');
115
121
  });
116
- this.pricingSocketNamespace.on('message', data => {
122
+ this.pricingSocketNamespace.on('message', (data) => {
117
123
  const event = data.code.toLowerCase();
118
124
  this.emitter.emit(event, data.details);
119
125
  });
120
- this.pricingSocketNamespace.on('connect_error', error => {
126
+ this.pricingSocketNamespace.on('connect_error', (error) => {
121
127
  this.emitter.emit('error', error);
122
128
  });
123
129
  }
@@ -165,7 +171,8 @@ class SpaceClient {
165
171
  throw new Error('User ID must be a non-empty string.');
166
172
  }
167
173
  this.userId = userId;
168
- this.tokenService.updatePricingToken(await this.generateUserPricingToken());
174
+ const userPricingToken = await this.generateUserPricingToken();
175
+ this.tokenService.updatePricingToken(userPricingToken);
169
176
  }
170
177
  /**
171
178
  * Performs a request to SPACE to retrieve a new pricing token for the user with the given userId.
@@ -178,7 +185,7 @@ class SpaceClient {
178
185
  throw new Error('User ID is not set. Please set the user ID with `setUserId(userId)` before trying to generate a pricing token.');
179
186
  }
180
187
  return this.axios
181
- .post(`/features/${this.userId}`)
188
+ .post(`/features/${this.userId}/pricing-token`)
182
189
  .then((response) => {
183
190
  return response.data.pricingToken;
184
191
  })
@@ -194,10 +201,11 @@ const SpaceContext = createContext(undefined);
194
201
  /**
195
202
  * SpaceProvider initializes and provides the SpaceClient instance to children.
196
203
  */
197
- const SpaceProvider = ({ config, loader, children, }) => {
204
+ const SpaceProvider = ({ config, children, }) => {
198
205
  // Memorize the client to avoid unnecessary re-instantiation
199
206
  const context = useMemo(() => {
200
- const client = config.allowConnectionWithSpace ? new SpaceClient(config) : undefined;
207
+ const denyConnectionWithSpace = config.allowConnectionWithSpace === false;
208
+ const client = denyConnectionWithSpace ? undefined : new SpaceClient(config);
201
209
  let tokenService;
202
210
  if (!client) {
203
211
  tokenService = new TokenService();
@@ -210,29 +218,13 @@ const SpaceProvider = ({ config, loader, children, }) => {
210
218
  tokenService: tokenService,
211
219
  };
212
220
  }, [config.url, config.apiKey]);
213
- const [connected, setConnected] = React.useState(false);
214
- React.useEffect(() => {
215
- if (!context.client) {
216
- setConnected(true); // No connection needed if client is undefined
217
- return;
218
- }
219
- const handleSync = () => setConnected(true);
220
- context.client.on('synchronized', handleSync);
221
+ useEffect(() => {
222
+ return () => {
223
+ if (context.client && typeof context.client.disconnectWebSocket === 'function') {
224
+ context.client.disconnectWebSocket();
225
+ }
226
+ };
221
227
  }, [context.client]);
222
- if (!connected)
223
- return (jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', width: '100%' }, children: [loader ?? (jsx("div", { style: {
224
- border: '4px solid #e0e0e0',
225
- borderTop: '4px solid #1976d2',
226
- borderRadius: '50%',
227
- width: 48,
228
- height: 48,
229
- animation: 'spin 1s linear infinite',
230
- } })), jsx("style", { children: `
231
- @keyframes spin {
232
- 0% { transform: rotate(0deg); }
233
- 100% { transform: rotate(360deg); }
234
- }
235
- ` })] }));
236
228
  return jsx(SpaceContext.Provider, { value: context, children: children });
237
229
  };
238
230
 
@@ -266,10 +258,23 @@ function usePricingToken() {
266
258
  return spaceContext.tokenService;
267
259
  }
268
260
 
269
- // Helper to get a child by type name
270
- function getChildByType(children, typeName) {
271
- const arr = React.Children.toArray(children);
272
- return arr.find(child => child.type && child.type.name === typeName) || null;
261
+ // Generic wrapper for feature children
262
+ function On({ children }) {
263
+ return jsx(Fragment, { children: children });
264
+ }
265
+ function Default({ children }) {
266
+ return jsx(Fragment, { children: children });
267
+ }
268
+ function Loading({ children }) {
269
+ return jsx(Fragment, { children: children });
270
+ }
271
+ function ErrorFallback({ children }) {
272
+ return jsx(Fragment, { children: children });
273
+ }
274
+ // Helper to get the children of a specific subcomponent type
275
+ function getChildrenOfType(children, type) {
276
+ const match = React.Children.toArray(children).find((child) => React.isValidElement(child) && child.type === type);
277
+ return match ? match.props.children : null;
273
278
  }
274
279
  const Feature = ({ id, children }) => {
275
280
  const tokenService = usePricingToken();
@@ -279,12 +284,10 @@ const Feature = ({ id, children }) => {
279
284
  const isValidId = useMemo(() => id.includes('-'), [id]);
280
285
  useEffect(() => {
281
286
  if (!isValidId) {
282
- console.error(`Invalid feature ID: ‘${id}’. A valid feature ID must contain a hyphen (’-’) and follow the format: ‘{serviceName in lowercase}-{featureName as defined in the pricing}’.`);
283
287
  setStatus('error');
284
288
  return;
285
289
  }
286
290
  if (tokenService.getPricingToken() === null) {
287
- console.error(`Pricing token is either not set or expired. Please ensure the token is initialized and not expired before using the Feature component.`);
288
291
  setStatus('error');
289
292
  return;
290
293
  }
@@ -300,18 +303,18 @@ const Feature = ({ id, children }) => {
300
303
  }
301
304
  }, [id, isValidId]);
302
305
  if (status === 'loading') {
303
- return getChildByType(children, 'Loading') || jsx(Fragment, {});
306
+ return jsx(Fragment, { children: getChildrenOfType(children, Loading) });
304
307
  }
305
308
  if (status === 'error') {
306
- return getChildByType(children, 'ErrorFallback') || jsx(Fragment, {});
309
+ return jsx(Fragment, { children: getChildrenOfType(children, ErrorFallback) });
307
310
  }
308
311
  if (status === 'success' && result === true) {
309
- return getChildByType(children, 'On') || jsx(Fragment, {});
312
+ return jsx(Fragment, { children: getChildrenOfType(children, On) });
310
313
  }
311
314
  if (status === 'success' && result === false) {
312
- return getChildByType(children, 'Default') || jsx(Fragment, {});
315
+ return jsx(Fragment, { children: getChildrenOfType(children, Default) });
313
316
  }
314
317
  return jsx(Fragment, {});
315
318
  };
316
319
 
317
- export { Feature, SpaceProvider, usePricingToken, useSpaceClient };
320
+ export { Default, ErrorFallback, Feature, Loading, On, SpaceProvider, usePricingToken, useSpaceClient };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "space-react-client",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -28,13 +28,14 @@
28
28
  "license": "ISC",
29
29
  "packageManager": "pnpm@10.10.0",
30
30
  "peerDependencies": {
31
- "react": "^19.1.0"
31
+ "react": "^18.3.1",
32
+ "react-dom": "^18.3.1"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@rollup/plugin-alias": "^5.1.1",
35
36
  "@rollup/plugin-typescript": "^12.1.2",
36
37
  "@testing-library/react": "^16.3.0",
37
- "@types/react": "^19.1.6",
38
+ "@types/react": "^18.3.1",
38
39
  "@vitejs/plugin-react": "^4.5.0",
39
40
  "eslint": "^9.28.0",
40
41
  "eslint-config-prettier": "^10.1.5",