@cstar.help/react 0.7.0 → 0.8.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.cjs CHANGED
@@ -102,19 +102,26 @@ function CStarChatProvider({ children, ...config }) {
102
102
  var import_react3 = require("react");
103
103
  function useChat() {
104
104
  const client = useChatClient();
105
- const [isIdentified, setIsIdentified] = (0, import_react3.useState)(false);
105
+ const [isAuthenticated, setIsAuthenticated] = (0, import_react3.useState)(false);
106
+ const [authMode, setAuthMode] = (0, import_react3.useState)(() => client.getAuthMode());
106
107
  const [isRealtimeReady, setIsRealtimeReady] = (0, import_react3.useState)(false);
107
108
  const [error, setError] = (0, import_react3.useState)(null);
109
+ (0, import_react3.useEffect)(() => {
110
+ const current = client.getAuthMode();
111
+ if (current !== authMode) setAuthMode(current);
112
+ }, [client, authMode]);
113
+ const wireRealtime = (0, import_react3.useCallback)(() => {
114
+ client.waitForRealtime().then(() => setIsRealtimeReady(true)).catch(() => {
115
+ });
116
+ }, [client]);
108
117
  const identify = (0, import_react3.useCallback)(
109
118
  async (customer, signature) => {
110
119
  setError(null);
111
120
  try {
112
121
  const result = await client.identify(customer, signature);
113
- setIsIdentified(true);
114
- client.waitForRealtime().then(() => {
115
- setIsRealtimeReady(true);
116
- }).catch(() => {
117
- });
122
+ setIsAuthenticated(true);
123
+ setAuthMode(client.getAuthMode());
124
+ wireRealtime();
118
125
  return result;
119
126
  } catch (err) {
120
127
  const e = err instanceof Error ? err : new Error(String(err));
@@ -122,15 +129,59 @@ function useChat() {
122
129
  throw e;
123
130
  }
124
131
  },
125
- [client]
132
+ [client, wireRealtime]
133
+ );
134
+ const loginWithCredentials = (0, import_react3.useCallback)(
135
+ async (params) => {
136
+ setError(null);
137
+ try {
138
+ const result = await client.loginWithCredentials(params);
139
+ setIsAuthenticated(true);
140
+ setAuthMode(client.getAuthMode());
141
+ wireRealtime();
142
+ return result;
143
+ } catch (err) {
144
+ const e = err instanceof Error ? err : new Error(String(err));
145
+ setError(e);
146
+ throw e;
147
+ }
148
+ },
149
+ [client, wireRealtime]
150
+ );
151
+ const loginWithSession = (0, import_react3.useCallback)(
152
+ async (params) => {
153
+ setError(null);
154
+ try {
155
+ await client.loginWithSession(params);
156
+ setIsAuthenticated(true);
157
+ setAuthMode(client.getAuthMode());
158
+ wireRealtime();
159
+ } catch (err) {
160
+ const e = err instanceof Error ? err : new Error(String(err));
161
+ setError(e);
162
+ throw e;
163
+ }
164
+ },
165
+ [client, wireRealtime]
126
166
  );
127
167
  const disconnect = (0, import_react3.useCallback)(() => {
128
168
  client.disconnect();
129
- setIsIdentified(false);
169
+ setIsAuthenticated(false);
170
+ setAuthMode(null);
130
171
  setIsRealtimeReady(false);
131
172
  setError(null);
132
173
  }, [client]);
133
- return { identify, disconnect, isIdentified, isRealtimeReady, error };
174
+ return {
175
+ identify,
176
+ loginWithCredentials,
177
+ loginWithSession,
178
+ disconnect,
179
+ isIdentified: isAuthenticated,
180
+ isAuthenticated,
181
+ authMode,
182
+ isRealtimeReady,
183
+ error
184
+ };
134
185
  }
135
186
 
136
187
  // src/chat/useTickets.ts
@@ -679,10 +730,15 @@ var import_react17 = require("react");
679
730
  var import_jsx_runtime4 = require("react/jsx-runtime");
680
731
  var CStarDashboardContext = (0, import_react17.createContext)(null);
681
732
  function CStarDashboardProvider({ client, children }) {
733
+ if (!client) {
734
+ throw new Error(
735
+ "CStarDashboardProvider requires a `client` prop. Build it server-side with `new CStarClient({ apiKey: process.env.CSTAR_API_KEY, teamId })` and pass via SSR. See https://cstar.help/developers/sdk/react#dashboard"
736
+ );
737
+ }
682
738
  const contextValue = (0, import_react17.useRef)({ client }).current;
683
739
  (0, import_react17.useEffect)(() => {
684
740
  return () => {
685
- client.realtime.disconnect();
741
+ client?.realtime?.disconnect?.();
686
742
  };
687
743
  }, [client]);
688
744
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CStarDashboardContext.Provider, { value: contextValue, children });
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { ReactNode } from 'react';
3
- import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Ticket, TicketCreateParams, Message } from '@cstar.help/js/chat';
3
+ import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, LoginWithCredentialsParams, LoginWithCredentialsResult, LoginWithSessionParams, ChatAuthMode, Ticket, TicketCreateParams, Message } from '@cstar.help/js/chat';
4
4
  export { ChatConfig, IdentifyParams, IdentifyResult, Message, Ticket, TicketCreateParams, TicketListResponse, TypingPayload } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
6
6
  export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, LibrarySearchParams, LibrarySearchResult } from '@cstar.help/js/library';
@@ -46,25 +46,58 @@ declare function CStarChatProvider({ children, ...config }: CStarChatProviderPro
46
46
  interface UseChatReturn {
47
47
  /** Identify a customer with HMAC signature. */
48
48
  identify: (customer: IdentifyParams, signature: string) => Promise<IdentifyResult>;
49
+ /**
50
+ * Log in with cStar's hosted password auth (no separate auth backend
51
+ * required). Updates `isAuthenticated` and `authMode` to `'session'`.
52
+ */
53
+ loginWithCredentials: (params: LoginWithCredentialsParams) => Promise<LoginWithCredentialsResult>;
54
+ /**
55
+ * Use an existing Supabase access token (e.g. forwarded from your own
56
+ * auth). Updates `isAuthenticated` and `authMode` to `'session'`.
57
+ */
58
+ loginWithSession: (params: LoginWithSessionParams) => Promise<void>;
49
59
  /** Disconnect the client and clean up all subscriptions. */
50
60
  disconnect: () => void;
51
- /** Whether a customer has been successfully identified. */
61
+ /**
62
+ * `true` once the client has authenticated through any of the four modes
63
+ * (HMAC `identify`, credentials login, session login, or anonymous). Use
64
+ * this for "is the customer signed in?" branches.
65
+ *
66
+ * @deprecated since 0.8.0 — use `isAuthenticated`. Kept as an alias.
67
+ */
52
68
  isIdentified: boolean;
69
+ /** Same as `isIdentified` — kept under the more accurate name. */
70
+ isAuthenticated: boolean;
71
+ /**
72
+ * The auth mode the underlying ChatClient is using right now. `null`
73
+ * before any auth call has resolved (or after `disconnect()`).
74
+ */
75
+ authMode: ChatAuthMode | null;
53
76
  /** Whether the real-time connection is ready. */
54
77
  isRealtimeReady: boolean;
55
- /** The last error from identify, or null. */
78
+ /** The last error from any auth call, or null. */
56
79
  error: Error | null;
57
80
  }
58
81
  /**
59
- * Core chat hook — manages identity verification and realtime readiness.
82
+ * Core chat hook — manages identity verification (any of four auth modes)
83
+ * and realtime readiness. Closes F-041 / F-044: previously the hook only
84
+ * tracked HMAC `identify()` and ignored `loginWithCredentials` /
85
+ * `loginWithSession`, leaving consumer UI stuck on the unauthenticated
86
+ * branch even after a successful session login.
60
87
  *
61
88
  * ```tsx
62
- * const { identify, disconnect, isIdentified, isRealtimeReady, error } = useChat();
89
+ * const { identify, loginWithCredentials, isAuthenticated, authMode } = useChat();
63
90
  *
64
91
  * await identify(
65
92
  * { externalId: 'usr_123', email: 'jane@co.com', timestamp: Date.now() / 1000 },
66
93
  * hmacSignature
67
94
  * );
95
+ * // OR
96
+ * await loginWithCredentials({ email: 'jane@co.com', password: 'hunter2' });
97
+ *
98
+ * if (isAuthenticated && authMode === 'session') {
99
+ * // show "Sign out" button
100
+ * }
68
101
  * ```
69
102
  */
70
103
  declare function useChat(): UseChatReturn;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { ReactNode } from 'react';
3
- import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Ticket, TicketCreateParams, Message } from '@cstar.help/js/chat';
3
+ import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, LoginWithCredentialsParams, LoginWithCredentialsResult, LoginWithSessionParams, ChatAuthMode, Ticket, TicketCreateParams, Message } from '@cstar.help/js/chat';
4
4
  export { ChatConfig, IdentifyParams, IdentifyResult, Message, Ticket, TicketCreateParams, TicketListResponse, TypingPayload } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
6
6
  export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, LibrarySearchParams, LibrarySearchResult } from '@cstar.help/js/library';
@@ -46,25 +46,58 @@ declare function CStarChatProvider({ children, ...config }: CStarChatProviderPro
46
46
  interface UseChatReturn {
47
47
  /** Identify a customer with HMAC signature. */
48
48
  identify: (customer: IdentifyParams, signature: string) => Promise<IdentifyResult>;
49
+ /**
50
+ * Log in with cStar's hosted password auth (no separate auth backend
51
+ * required). Updates `isAuthenticated` and `authMode` to `'session'`.
52
+ */
53
+ loginWithCredentials: (params: LoginWithCredentialsParams) => Promise<LoginWithCredentialsResult>;
54
+ /**
55
+ * Use an existing Supabase access token (e.g. forwarded from your own
56
+ * auth). Updates `isAuthenticated` and `authMode` to `'session'`.
57
+ */
58
+ loginWithSession: (params: LoginWithSessionParams) => Promise<void>;
49
59
  /** Disconnect the client and clean up all subscriptions. */
50
60
  disconnect: () => void;
51
- /** Whether a customer has been successfully identified. */
61
+ /**
62
+ * `true` once the client has authenticated through any of the four modes
63
+ * (HMAC `identify`, credentials login, session login, or anonymous). Use
64
+ * this for "is the customer signed in?" branches.
65
+ *
66
+ * @deprecated since 0.8.0 — use `isAuthenticated`. Kept as an alias.
67
+ */
52
68
  isIdentified: boolean;
69
+ /** Same as `isIdentified` — kept under the more accurate name. */
70
+ isAuthenticated: boolean;
71
+ /**
72
+ * The auth mode the underlying ChatClient is using right now. `null`
73
+ * before any auth call has resolved (or after `disconnect()`).
74
+ */
75
+ authMode: ChatAuthMode | null;
53
76
  /** Whether the real-time connection is ready. */
54
77
  isRealtimeReady: boolean;
55
- /** The last error from identify, or null. */
78
+ /** The last error from any auth call, or null. */
56
79
  error: Error | null;
57
80
  }
58
81
  /**
59
- * Core chat hook — manages identity verification and realtime readiness.
82
+ * Core chat hook — manages identity verification (any of four auth modes)
83
+ * and realtime readiness. Closes F-041 / F-044: previously the hook only
84
+ * tracked HMAC `identify()` and ignored `loginWithCredentials` /
85
+ * `loginWithSession`, leaving consumer UI stuck on the unauthenticated
86
+ * branch even after a successful session login.
60
87
  *
61
88
  * ```tsx
62
- * const { identify, disconnect, isIdentified, isRealtimeReady, error } = useChat();
89
+ * const { identify, loginWithCredentials, isAuthenticated, authMode } = useChat();
63
90
  *
64
91
  * await identify(
65
92
  * { externalId: 'usr_123', email: 'jane@co.com', timestamp: Date.now() / 1000 },
66
93
  * hmacSignature
67
94
  * );
95
+ * // OR
96
+ * await loginWithCredentials({ email: 'jane@co.com', password: 'hunter2' });
97
+ *
98
+ * if (isAuthenticated && authMode === 'session') {
99
+ * // show "Sign out" button
100
+ * }
68
101
  * ```
69
102
  */
70
103
  declare function useChat(): UseChatReturn;
package/dist/index.js CHANGED
@@ -44,22 +44,29 @@ function CStarChatProvider({ children, ...config }) {
44
44
  }
45
45
 
46
46
  // src/chat/useChat.ts
47
- import { useState, useCallback } from "react";
47
+ import { useState, useCallback, useEffect as useEffect2 } from "react";
48
48
  function useChat() {
49
49
  const client = useChatClient();
50
- const [isIdentified, setIsIdentified] = useState(false);
50
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
51
+ const [authMode, setAuthMode] = useState(() => client.getAuthMode());
51
52
  const [isRealtimeReady, setIsRealtimeReady] = useState(false);
52
53
  const [error, setError] = useState(null);
54
+ useEffect2(() => {
55
+ const current = client.getAuthMode();
56
+ if (current !== authMode) setAuthMode(current);
57
+ }, [client, authMode]);
58
+ const wireRealtime = useCallback(() => {
59
+ client.waitForRealtime().then(() => setIsRealtimeReady(true)).catch(() => {
60
+ });
61
+ }, [client]);
53
62
  const identify = useCallback(
54
63
  async (customer, signature) => {
55
64
  setError(null);
56
65
  try {
57
66
  const result = await client.identify(customer, signature);
58
- setIsIdentified(true);
59
- client.waitForRealtime().then(() => {
60
- setIsRealtimeReady(true);
61
- }).catch(() => {
62
- });
67
+ setIsAuthenticated(true);
68
+ setAuthMode(client.getAuthMode());
69
+ wireRealtime();
63
70
  return result;
64
71
  } catch (err) {
65
72
  const e = err instanceof Error ? err : new Error(String(err));
@@ -67,19 +74,63 @@ function useChat() {
67
74
  throw e;
68
75
  }
69
76
  },
70
- [client]
77
+ [client, wireRealtime]
78
+ );
79
+ const loginWithCredentials = useCallback(
80
+ async (params) => {
81
+ setError(null);
82
+ try {
83
+ const result = await client.loginWithCredentials(params);
84
+ setIsAuthenticated(true);
85
+ setAuthMode(client.getAuthMode());
86
+ wireRealtime();
87
+ return result;
88
+ } catch (err) {
89
+ const e = err instanceof Error ? err : new Error(String(err));
90
+ setError(e);
91
+ throw e;
92
+ }
93
+ },
94
+ [client, wireRealtime]
95
+ );
96
+ const loginWithSession = useCallback(
97
+ async (params) => {
98
+ setError(null);
99
+ try {
100
+ await client.loginWithSession(params);
101
+ setIsAuthenticated(true);
102
+ setAuthMode(client.getAuthMode());
103
+ wireRealtime();
104
+ } catch (err) {
105
+ const e = err instanceof Error ? err : new Error(String(err));
106
+ setError(e);
107
+ throw e;
108
+ }
109
+ },
110
+ [client, wireRealtime]
71
111
  );
72
112
  const disconnect = useCallback(() => {
73
113
  client.disconnect();
74
- setIsIdentified(false);
114
+ setIsAuthenticated(false);
115
+ setAuthMode(null);
75
116
  setIsRealtimeReady(false);
76
117
  setError(null);
77
118
  }, [client]);
78
- return { identify, disconnect, isIdentified, isRealtimeReady, error };
119
+ return {
120
+ identify,
121
+ loginWithCredentials,
122
+ loginWithSession,
123
+ disconnect,
124
+ isIdentified: isAuthenticated,
125
+ isAuthenticated,
126
+ authMode,
127
+ isRealtimeReady,
128
+ error
129
+ };
79
130
  }
80
131
 
81
132
  // src/chat/useTickets.ts
82
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
133
+ import { useState as useState2, useEffect as useEffect3, useCallback as useCallback2, useRef as useRef2 } from "react";
83
134
  function useTickets() {
84
135
  const client = useChatClient();
85
136
  const [tickets, setTickets] = useState2([]);
@@ -106,7 +157,7 @@ function useTickets() {
106
157
  }
107
158
  }
108
159
  }, [client]);
109
- useEffect2(() => {
160
+ useEffect3(() => {
110
161
  mountedRef.current = true;
111
162
  refresh();
112
163
  return () => {
@@ -127,7 +178,7 @@ function useTickets() {
127
178
  }
128
179
 
129
180
  // src/chat/useMessages.ts
130
- import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3, useRef as useRef3 } from "react";
181
+ import { useState as useState3, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef3 } from "react";
131
182
  function useMessages(ticketId) {
132
183
  const client = useChatClient();
133
184
  const [messages, setMessages] = useState3([]);
@@ -155,7 +206,7 @@ function useMessages(ticketId) {
155
206
  }
156
207
  }
157
208
  }, [client, ticketId]);
158
- useEffect3(() => {
209
+ useEffect4(() => {
159
210
  mountedRef.current = true;
160
211
  if (!ticketId) {
161
212
  setMessages([]);
@@ -213,14 +264,14 @@ function useMessages(ticketId) {
213
264
  }
214
265
 
215
266
  // src/chat/useTyping.ts
216
- import { useState as useState4, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef4 } from "react";
267
+ import { useState as useState4, useEffect as useEffect5, useCallback as useCallback4, useRef as useRef4 } from "react";
217
268
  var TYPING_TIMEOUT = 4e3;
218
269
  function useTyping(ticketId) {
219
270
  const client = useChatClient();
220
271
  const [typingAgents, setTypingAgents] = useState4([]);
221
272
  const timersRef = useRef4(/* @__PURE__ */ new Map());
222
273
  const mountedRef = useRef4(true);
223
- useEffect4(() => {
274
+ useEffect5(() => {
224
275
  mountedRef.current = true;
225
276
  if (!ticketId) {
226
277
  setTypingAgents([]);
@@ -280,7 +331,7 @@ function CStarLibraryProvider({ children, ...config }) {
280
331
  }
281
332
 
282
333
  // src/library/useCategories.ts
283
- import { useState as useState5, useEffect as useEffect5, useCallback as useCallback5, useRef as useRef6 } from "react";
334
+ import { useState as useState5, useEffect as useEffect6, useCallback as useCallback5, useRef as useRef6 } from "react";
284
335
  function useCategories() {
285
336
  const client = useLibraryClient();
286
337
  const [categories, setCategories] = useState5([]);
@@ -305,7 +356,7 @@ function useCategories() {
305
356
  }
306
357
  }
307
358
  }, [client]);
308
- useEffect5(() => {
359
+ useEffect6(() => {
309
360
  mountedRef.current = true;
310
361
  refresh();
311
362
  return () => {
@@ -316,7 +367,7 @@ function useCategories() {
316
367
  }
317
368
 
318
369
  // src/library/useArticles.ts
319
- import { useState as useState6, useEffect as useEffect6, useCallback as useCallback6, useRef as useRef7 } from "react";
370
+ import { useState as useState6, useEffect as useEffect7, useCallback as useCallback6, useRef as useRef7 } from "react";
320
371
  function useArticles(params) {
321
372
  const client = useLibraryClient();
322
373
  const [articles, setArticles] = useState6([]);
@@ -344,7 +395,7 @@ function useArticles(params) {
344
395
  }
345
396
  }
346
397
  }, [client, categorySlug, limit, offset]);
347
- useEffect6(() => {
398
+ useEffect7(() => {
348
399
  mountedRef.current = true;
349
400
  refresh();
350
401
  return () => {
@@ -355,7 +406,7 @@ function useArticles(params) {
355
406
  }
356
407
 
357
408
  // src/library/useArticle.ts
358
- import { useState as useState7, useEffect as useEffect7, useCallback as useCallback7, useRef as useRef8 } from "react";
409
+ import { useState as useState7, useEffect as useEffect8, useCallback as useCallback7, useRef as useRef8 } from "react";
359
410
  function useArticle(slug) {
360
411
  const client = useLibraryClient();
361
412
  const [article, setArticle] = useState7(null);
@@ -388,7 +439,7 @@ function useArticle(slug) {
388
439
  } catch {
389
440
  }
390
441
  }, [client, slug]);
391
- useEffect7(() => {
442
+ useEffect8(() => {
392
443
  mountedRef.current = true;
393
444
  if (!slug) {
394
445
  setArticle(null);
@@ -404,7 +455,7 @@ function useArticle(slug) {
404
455
  }
405
456
 
406
457
  // src/library/useArticleSearch.ts
407
- import { useState as useState8, useEffect as useEffect8, useRef as useRef9 } from "react";
458
+ import { useState as useState8, useEffect as useEffect9, useRef as useRef9 } from "react";
408
459
  var DEBOUNCE_MS = 300;
409
460
  function useArticleSearch(query) {
410
461
  const client = useLibraryClient();
@@ -413,7 +464,7 @@ function useArticleSearch(query) {
413
464
  const [isLoading, setIsLoading] = useState8(false);
414
465
  const [error, setError] = useState8(null);
415
466
  const mountedRef = useRef9(true);
416
- useEffect8(() => {
467
+ useEffect9(() => {
417
468
  mountedRef.current = true;
418
469
  const trimmed = query.trim();
419
470
  if (!trimmed) {
@@ -463,7 +514,7 @@ function CStarCommunityProvider({ children, ...config }) {
463
514
  }
464
515
 
465
516
  // src/community/useTopics.ts
466
- import { useState as useState9, useEffect as useEffect9, useCallback as useCallback8, useRef as useRef11 } from "react";
517
+ import { useState as useState9, useEffect as useEffect10, useCallback as useCallback8, useRef as useRef11 } from "react";
467
518
  function useTopics() {
468
519
  const client = useCommunityClient();
469
520
  const [topics, setTopics] = useState9([]);
@@ -488,7 +539,7 @@ function useTopics() {
488
539
  }
489
540
  }
490
541
  }, [client]);
491
- useEffect9(() => {
542
+ useEffect10(() => {
492
543
  mountedRef.current = true;
493
544
  refresh();
494
545
  return () => {
@@ -499,7 +550,7 @@ function useTopics() {
499
550
  }
500
551
 
501
552
  // src/community/usePosts.ts
502
- import { useState as useState10, useEffect as useEffect10, useCallback as useCallback9, useRef as useRef12 } from "react";
553
+ import { useState as useState10, useEffect as useEffect11, useCallback as useCallback9, useRef as useRef12 } from "react";
503
554
  function usePosts(params) {
504
555
  const client = useCommunityClient();
505
556
  const [posts, setPosts] = useState10([]);
@@ -531,7 +582,7 @@ function usePosts(params) {
531
582
  }
532
583
  }
533
584
  }, [client, topicSlug, status, sort, limit, offset]);
534
- useEffect10(() => {
585
+ useEffect11(() => {
535
586
  mountedRef.current = true;
536
587
  refresh();
537
588
  return () => {
@@ -542,7 +593,7 @@ function usePosts(params) {
542
593
  }
543
594
 
544
595
  // src/community/usePost.ts
545
- import { useState as useState11, useEffect as useEffect11, useCallback as useCallback10, useRef as useRef13 } from "react";
596
+ import { useState as useState11, useEffect as useEffect12, useCallback as useCallback10, useRef as useRef13 } from "react";
546
597
  function usePost(slug) {
547
598
  const client = useCommunityClient();
548
599
  const [data, setData] = useState11(null);
@@ -567,7 +618,7 @@ function usePost(slug) {
567
618
  }
568
619
  }
569
620
  }, [client, slug]);
570
- useEffect11(() => {
621
+ useEffect12(() => {
571
622
  mountedRef.current = true;
572
623
  refresh();
573
624
  return () => {
@@ -578,14 +629,14 @@ function usePost(slug) {
578
629
  }
579
630
 
580
631
  // src/community/useCommunitySearch.ts
581
- import { useState as useState12, useEffect as useEffect12, useCallback as useCallback11, useRef as useRef14 } from "react";
632
+ import { useState as useState12, useEffect as useEffect13, useCallback as useCallback11, useRef as useRef14 } from "react";
582
633
  function useCommunitySearch() {
583
634
  const client = useCommunityClient();
584
635
  const [results, setResults] = useState12(null);
585
636
  const [isLoading, setIsLoading] = useState12(false);
586
637
  const [error, setError] = useState12(null);
587
638
  const mountedRef = useRef14(true);
588
- useEffect12(() => {
639
+ useEffect13(() => {
589
640
  mountedRef.current = true;
590
641
  return () => {
591
642
  mountedRef.current = false;
@@ -620,14 +671,19 @@ function useCommunitySearch() {
620
671
  }
621
672
 
622
673
  // src/dashboard/CStarDashboardProvider.tsx
623
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect13, useRef as useRef15 } from "react";
674
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect14, useRef as useRef15 } from "react";
624
675
  import { jsx as jsx4 } from "react/jsx-runtime";
625
676
  var CStarDashboardContext = createContext2(null);
626
677
  function CStarDashboardProvider({ client, children }) {
678
+ if (!client) {
679
+ throw new Error(
680
+ "CStarDashboardProvider requires a `client` prop. Build it server-side with `new CStarClient({ apiKey: process.env.CSTAR_API_KEY, teamId })` and pass via SSR. See https://cstar.help/developers/sdk/react#dashboard"
681
+ );
682
+ }
627
683
  const contextValue = useRef15({ client }).current;
628
- useEffect13(() => {
684
+ useEffect14(() => {
629
685
  return () => {
630
- client.realtime.disconnect();
686
+ client?.realtime?.disconnect?.();
631
687
  };
632
688
  }, [client]);
633
689
  return /* @__PURE__ */ jsx4(CStarDashboardContext.Provider, { value: contextValue, children });
@@ -641,7 +697,7 @@ function useDashboardClient() {
641
697
  }
642
698
 
643
699
  // src/dashboard/useCStarQuery.ts
644
- import { useState as useState13, useEffect as useEffect14, useRef as useRef16, useCallback as useCallback12 } from "react";
700
+ import { useState as useState13, useEffect as useEffect15, useRef as useRef16, useCallback as useCallback12 } from "react";
645
701
  function resolveResource(client, path) {
646
702
  const parts = path.split(".");
647
703
  if (parts.length < 2 || parts[1] !== "list") return null;
@@ -659,7 +715,7 @@ function useCStarQuery(resource, params) {
659
715
  const [error, setError] = useState13(null);
660
716
  const subscriptionRef = useRef16(null);
661
717
  const paramsKey = params ? JSON.stringify(params) : "";
662
- useEffect14(() => {
718
+ useEffect15(() => {
663
719
  const resolved = resolveResource(client, resource);
664
720
  if (!resolved) {
665
721
  setError(new Error(`Invalid resource path: ${resource}. Expected format: 'tickets.list'`));
@@ -746,14 +802,14 @@ function useCStarMutation(resource) {
746
802
  }
747
803
 
748
804
  // src/dashboard/useCStarEvents.ts
749
- import { useState as useState15, useEffect as useEffect15 } from "react";
805
+ import { useState as useState15, useEffect as useEffect16 } from "react";
750
806
  var MAX_EVENTS = 100;
751
807
  function useCStarEvents(types) {
752
808
  const client = useDashboardClient();
753
809
  const [events, setEvents] = useState15([]);
754
810
  const [connected, setConnected] = useState15(false);
755
811
  const typesKey = types ? types.join(",") : "";
756
- useEffect15(() => {
812
+ useEffect16(() => {
757
813
  const realtime = client.realtime;
758
814
  const handler = (event) => {
759
815
  if (types && types.length > 0) {
@@ -785,14 +841,14 @@ function useCStarEvents(types) {
785
841
  }
786
842
 
787
843
  // src/dashboard/useBossBattle.ts
788
- import { useState as useState16, useEffect as useEffect16, useRef as useRef17, useCallback as useCallback14 } from "react";
844
+ import { useState as useState16, useEffect as useEffect17, useRef as useRef17, useCallback as useCallback14 } from "react";
789
845
  function useBossBattle() {
790
846
  const client = useDashboardClient();
791
847
  const [boss, setBoss] = useState16(null);
792
848
  const [loading, setLoading] = useState16(true);
793
849
  const [error, setError] = useState16(null);
794
850
  const subRef = useRef17(null);
795
- useEffect16(() => {
851
+ useEffect17(() => {
796
852
  const sub = client.game.subscribeBoss(client.realtime);
797
853
  subRef.current = sub;
798
854
  sub.on("change", (data) => {
@@ -816,14 +872,14 @@ function useBossBattle() {
816
872
  }
817
873
 
818
874
  // src/dashboard/usePlayerStats.ts
819
- import { useState as useState17, useEffect as useEffect17, useRef as useRef18, useCallback as useCallback15 } from "react";
875
+ import { useState as useState17, useEffect as useEffect18, useRef as useRef18, useCallback as useCallback15 } from "react";
820
876
  function usePlayerStats(memberId) {
821
877
  const client = useDashboardClient();
822
878
  const [player, setPlayer] = useState17(null);
823
879
  const [loading, setLoading] = useState17(true);
824
880
  const [error, setError] = useState17(null);
825
881
  const subRef = useRef18(null);
826
- useEffect17(() => {
882
+ useEffect18(() => {
827
883
  const sub = client.game.subscribePlayer(client.realtime, memberId);
828
884
  subRef.current = sub;
829
885
  sub.on("change", (data) => {
@@ -847,14 +903,14 @@ function usePlayerStats(memberId) {
847
903
  }
848
904
 
849
905
  // src/dashboard/useLeaderboard.ts
850
- import { useState as useState18, useEffect as useEffect18, useRef as useRef19, useCallback as useCallback16 } from "react";
906
+ import { useState as useState18, useEffect as useEffect19, useRef as useRef19, useCallback as useCallback16 } from "react";
851
907
  function useLeaderboard(period) {
852
908
  const client = useDashboardClient();
853
909
  const [entries, setEntries] = useState18([]);
854
910
  const [loading, setLoading] = useState18(true);
855
911
  const [error, setError] = useState18(null);
856
912
  const subRef = useRef19(null);
857
- useEffect18(() => {
913
+ useEffect19(() => {
858
914
  const sub = client.game.subscribeLeaderboard(client.realtime, period ? { period } : void 0);
859
915
  subRef.current = sub;
860
916
  sub.on("change", (data) => {
@@ -878,14 +934,14 @@ function useLeaderboard(period) {
878
934
  }
879
935
 
880
936
  // src/dashboard/useGameActivity.ts
881
- import { useState as useState19, useEffect as useEffect19, useRef as useRef20, useCallback as useCallback17 } from "react";
937
+ import { useState as useState19, useEffect as useEffect20, useRef as useRef20, useCallback as useCallback17 } from "react";
882
938
  function useGameActivity() {
883
939
  const client = useDashboardClient();
884
940
  const [events, setEvents] = useState19([]);
885
941
  const [loading, setLoading] = useState19(true);
886
942
  const [error, setError] = useState19(null);
887
943
  const subRef = useRef20(null);
888
- useEffect19(() => {
944
+ useEffect20(() => {
889
945
  const sub = client.game.subscribeActivity(client.realtime);
890
946
  subRef.current = sub;
891
947
  sub.on("change", (data) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cstar.help/react",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "React hooks for the cStar customer support platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",