@cstar.help/react 0.9.0 → 0.10.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/README.md CHANGED
@@ -300,6 +300,69 @@ function CommunitySearch() {
300
300
  | `useLibraryClient()` | `LibraryClient` | Raw client from nearest `CStarLibraryProvider` |
301
301
  | `useCommunityClient()` | `CommunityClient` | Raw client from nearest `CStarCommunityProvider` |
302
302
 
303
+ ## Using with Next.js App Router (RSC)
304
+
305
+ The dashboard provider (`CStarDashboardProvider`) wraps a `CStarClient`,
306
+ which is a class instance — and React Server Components can't serialize
307
+ class instances across the server/client boundary. The provider therefore
308
+ takes one of two prop shapes:
309
+
310
+ **Recommended — pass a plain config object.** The provider builds (and
311
+ disposes) the client inside the `'use client'` boundary, so nothing
312
+ class-shaped crosses the RSC boundary:
313
+
314
+ ```tsx
315
+ // app/providers.tsx
316
+ 'use client';
317
+ import { CStarDashboardProvider } from '@cstar.help/react';
318
+
319
+ export function Providers({ children }: { children: React.ReactNode }) {
320
+ return (
321
+ <CStarDashboardProvider
322
+ config={{
323
+ apiKey: process.env.NEXT_PUBLIC_CSTAR_PUBLISHABLE_KEY!,
324
+ teamId: process.env.NEXT_PUBLIC_CSTAR_TEAM_ID!
325
+ }}
326
+ >
327
+ {children}
328
+ </CStarDashboardProvider>
329
+ );
330
+ }
331
+ ```
332
+
333
+ Use the `apiKey` field for a publishable key (`pk_live_*` / `pk_test_*`),
334
+ NOT a secret key — the bundle ships to the browser. For server-only flows
335
+ (an admin route handler, a server action), construct `CStarClient` with the
336
+ secret key inline; never thread it into a provider.
337
+
338
+ **Legacy — pass a pre-built client (deprecated).** If you already have a
339
+ client instance you want to share, the `client` prop still works, but you
340
+ have to construct it inside `'use client'` and you take on responsibility
341
+ for tearing down the realtime subscription:
342
+
343
+ ```tsx
344
+ 'use client';
345
+ import { useMemo } from 'react';
346
+ import { CStarClient } from '@cstar.help/js';
347
+ import { CStarDashboardProvider } from '@cstar.help/react';
348
+
349
+ export function Providers({ children }) {
350
+ const client = useMemo(
351
+ () =>
352
+ new CStarClient({
353
+ apiKey: process.env.NEXT_PUBLIC_CSTAR_PUBLISHABLE_KEY!,
354
+ teamId: process.env.NEXT_PUBLIC_CSTAR_TEAM_ID!
355
+ }),
356
+ []
357
+ );
358
+ return <CStarDashboardProvider client={client}>{children}</CStarDashboardProvider>;
359
+ }
360
+ ```
361
+
362
+ Either way, mark the file `'use client'`. Server components can sit above
363
+ the provider in the tree — only the provider boundary needs to be a client
364
+ component.
365
+
303
366
  ## Requirements
304
367
 
305
368
  - React 18+
package/dist/index.cjs CHANGED
@@ -107,9 +107,14 @@ function useChat() {
107
107
  const [isRealtimeReady, setIsRealtimeReady] = (0, import_react3.useState)(false);
108
108
  const [error, setError] = (0, import_react3.useState)(null);
109
109
  (0, import_react3.useEffect)(() => {
110
- const current = client.getAuthMode();
111
- if (current !== authMode) setAuthMode(current);
112
- }, [client, authMode]);
110
+ const subscribe = client.subscribe?.bind(client);
111
+ if (!subscribe) return;
112
+ return subscribe(() => {
113
+ const mode = client.getAuthMode();
114
+ setAuthMode(mode);
115
+ setIsAuthenticated(mode !== null);
116
+ });
117
+ }, [client]);
113
118
  const wireRealtime = (0, import_react3.useCallback)(() => {
114
119
  client.waitForRealtime().then(() => setIsRealtimeReady(true)).catch(() => {
115
120
  });
@@ -199,7 +204,7 @@ function useTickets() {
199
204
  try {
200
205
  const result = await client.tickets.list();
201
206
  if (mountedRef.current) {
202
- setTickets(result.tickets);
207
+ setTickets(result.data);
203
208
  setHasMore(result.hasMore);
204
209
  }
205
210
  } catch (err) {
@@ -727,20 +732,38 @@ function useCommunitySearch() {
727
732
 
728
733
  // src/dashboard/CStarDashboardProvider.tsx
729
734
  var import_react17 = require("react");
735
+ var import_js = require("@cstar.help/js");
730
736
  var import_jsx_runtime4 = require("react/jsx-runtime");
731
737
  var CStarDashboardContext = (0, import_react17.createContext)(null);
732
- function CStarDashboardProvider({ client, children }) {
733
- if (!client) {
738
+ function CStarDashboardProvider({
739
+ config,
740
+ client: providedClient,
741
+ children
742
+ }) {
743
+ if (!config && !providedClient) {
734
744
  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"
745
+ "CStarDashboardProvider requires either a `config` prop (`{ apiKey, teamId }` \u2014 recommended) or a `client` prop (a pre-built CStarClient instance, deprecated). See https://cstar.help/developers/sdk/react#dashboard"
736
746
  );
737
747
  }
738
- const contextValue = (0, import_react17.useRef)({ client }).current;
748
+ const client = (0, import_react17.useMemo)(() => {
749
+ if (providedClient) return providedClient;
750
+ return new import_js.CStarClient(config);
751
+ }, [
752
+ providedClient,
753
+ config?.apiKey,
754
+ config?.teamId,
755
+ config?.baseUrl,
756
+ config?.version,
757
+ config?.maxRetries,
758
+ config?.timeout
759
+ ]);
760
+ const contextValue = (0, import_react17.useMemo)(() => ({ client }), [client]);
739
761
  (0, import_react17.useEffect)(() => {
762
+ if (providedClient) return;
740
763
  return () => {
741
764
  client?.realtime?.disconnect?.();
742
765
  };
743
- }, [client]);
766
+ }, [client, providedClient]);
744
767
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CStarDashboardContext.Provider, { value: contextValue, children });
745
768
  }
746
769
  function useDashboardClient() {
@@ -753,6 +776,7 @@ function useDashboardClient() {
753
776
 
754
777
  // src/dashboard/useCStarQuery.ts
755
778
  var import_react18 = require("react");
779
+ var import_js2 = require("@cstar.help/js");
756
780
  function resolveResource(client, path) {
757
781
  const parts = path.split(".");
758
782
  if (parts.length < 2 || parts[1] !== "list") return null;
@@ -764,6 +788,11 @@ function resolveResource(client, path) {
764
788
  return { resource, basePath: `/${resourceName}` };
765
789
  }
766
790
  function useCStarQuery(resource, params) {
791
+ if (typeof resource !== "string") {
792
+ throw new import_js2.CStarValidationError(
793
+ `useCStarQuery: \`resource\` must be a string (e.g. 'tickets.list'). Received ${resource === null ? "null" : typeof resource}. The signature is useCStarQuery(resource: string, params?: object).`
794
+ );
795
+ }
767
796
  const client = useDashboardClient();
768
797
  const [data, setData] = (0, import_react18.useState)(null);
769
798
  const [loading, setLoading] = (0, import_react18.useState)(true);
package/dist/index.d.cts CHANGED
@@ -7,7 +7,7 @@ export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, Libr
7
7
  import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
8
  export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
9
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
10
- import { CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
10
+ import { CStarClientConfig, CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
11
11
 
12
12
  declare const CStarChatContext: react.Context<ChatClient | null>;
13
13
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
@@ -355,28 +355,44 @@ interface UseCommunitySearchReturn {
355
355
  declare function useCommunitySearch(): UseCommunitySearchReturn;
356
356
 
357
357
  interface CStarDashboardProviderProps {
358
- /** A pre-configured CStarClient instance (server-side, secret key). */
359
- client: CStarClient;
358
+ /**
359
+ * Serializable client config — `{ apiKey, teamId, baseUrl?, ... }`. The
360
+ * provider builds the `CStarClient` internally so this prop can be passed
361
+ * across the React Server Components boundary without tripping the
362
+ * "Classes or null prototypes are not supported" runtime error.
363
+ *
364
+ * Either `config` or `client` is required.
365
+ */
366
+ config?: CStarClientConfig;
367
+ /**
368
+ * A pre-built `CStarClient` instance.
369
+ *
370
+ * @deprecated since 0.9.0 — pass `config` instead. Class instances cannot
371
+ * cross the RSC boundary, which makes this prop unsafe to use from a
372
+ * Next.js App Router server component. Kept as an escape hatch for
373
+ * integrators who already wired it; will be removed in 1.0.
374
+ */
375
+ client?: CStarClient;
360
376
  children: react__default.ReactNode;
361
377
  }
362
378
  /**
363
- * Provides a CStarClient to dashboard hooks.
379
+ * Provides a `CStarClient` to dashboard hooks.
364
380
  *
365
381
  * Unlike `CStarChatProvider` (which uses a publishable key for customer-facing
366
382
  * chat), this provider wraps a full admin client with a secret key for building
367
383
  * custom dashboards.
368
384
  *
369
385
  * ```tsx
370
- * const client = new CStarClient({ apiKey: 'sk_test_...', teamId: '...' });
371
- *
372
- * <CStarDashboardProvider client={client}>
386
+ * // Recommended: pass serializable config — works in any React tree, including
387
+ * // App Router server components that hand the props to a client component.
388
+ * <CStarDashboardProvider config={{ apiKey: process.env.CSTAR_API_KEY!, teamId }}>
373
389
  * <TicketList />
374
390
  * </CStarDashboardProvider>
375
391
  * ```
376
392
  */
377
- declare function CStarDashboardProvider({ client, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
393
+ declare function CStarDashboardProvider({ config, client: providedClient, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
378
394
  /**
379
- * Access the CStarClient from the nearest CStarDashboardProvider.
395
+ * Access the `CStarClient` from the nearest `CStarDashboardProvider`.
380
396
  * Throws if used outside a provider.
381
397
  */
382
398
  declare function useDashboardClient(): CStarClient;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, Libr
7
7
  import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
8
  export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
9
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
10
- import { CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
10
+ import { CStarClientConfig, CStarClient, RealtimeEvent, Boss, Player, LeaderboardEntry } from '@cstar.help/js';
11
11
 
12
12
  declare const CStarChatContext: react.Context<ChatClient | null>;
13
13
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
@@ -355,28 +355,44 @@ interface UseCommunitySearchReturn {
355
355
  declare function useCommunitySearch(): UseCommunitySearchReturn;
356
356
 
357
357
  interface CStarDashboardProviderProps {
358
- /** A pre-configured CStarClient instance (server-side, secret key). */
359
- client: CStarClient;
358
+ /**
359
+ * Serializable client config — `{ apiKey, teamId, baseUrl?, ... }`. The
360
+ * provider builds the `CStarClient` internally so this prop can be passed
361
+ * across the React Server Components boundary without tripping the
362
+ * "Classes or null prototypes are not supported" runtime error.
363
+ *
364
+ * Either `config` or `client` is required.
365
+ */
366
+ config?: CStarClientConfig;
367
+ /**
368
+ * A pre-built `CStarClient` instance.
369
+ *
370
+ * @deprecated since 0.9.0 — pass `config` instead. Class instances cannot
371
+ * cross the RSC boundary, which makes this prop unsafe to use from a
372
+ * Next.js App Router server component. Kept as an escape hatch for
373
+ * integrators who already wired it; will be removed in 1.0.
374
+ */
375
+ client?: CStarClient;
360
376
  children: react__default.ReactNode;
361
377
  }
362
378
  /**
363
- * Provides a CStarClient to dashboard hooks.
379
+ * Provides a `CStarClient` to dashboard hooks.
364
380
  *
365
381
  * Unlike `CStarChatProvider` (which uses a publishable key for customer-facing
366
382
  * chat), this provider wraps a full admin client with a secret key for building
367
383
  * custom dashboards.
368
384
  *
369
385
  * ```tsx
370
- * const client = new CStarClient({ apiKey: 'sk_test_...', teamId: '...' });
371
- *
372
- * <CStarDashboardProvider client={client}>
386
+ * // Recommended: pass serializable config — works in any React tree, including
387
+ * // App Router server components that hand the props to a client component.
388
+ * <CStarDashboardProvider config={{ apiKey: process.env.CSTAR_API_KEY!, teamId }}>
373
389
  * <TicketList />
374
390
  * </CStarDashboardProvider>
375
391
  * ```
376
392
  */
377
- declare function CStarDashboardProvider({ client, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
393
+ declare function CStarDashboardProvider({ config, client: providedClient, children }: CStarDashboardProviderProps): react_jsx_runtime.JSX.Element;
378
394
  /**
379
- * Access the CStarClient from the nearest CStarDashboardProvider.
395
+ * Access the `CStarClient` from the nearest `CStarDashboardProvider`.
380
396
  * Throws if used outside a provider.
381
397
  */
382
398
  declare function useDashboardClient(): CStarClient;
package/dist/index.js CHANGED
@@ -52,9 +52,14 @@ function useChat() {
52
52
  const [isRealtimeReady, setIsRealtimeReady] = useState(false);
53
53
  const [error, setError] = useState(null);
54
54
  useEffect2(() => {
55
- const current = client.getAuthMode();
56
- if (current !== authMode) setAuthMode(current);
57
- }, [client, authMode]);
55
+ const subscribe = client.subscribe?.bind(client);
56
+ if (!subscribe) return;
57
+ return subscribe(() => {
58
+ const mode = client.getAuthMode();
59
+ setAuthMode(mode);
60
+ setIsAuthenticated(mode !== null);
61
+ });
62
+ }, [client]);
58
63
  const wireRealtime = useCallback(() => {
59
64
  client.waitForRealtime().then(() => setIsRealtimeReady(true)).catch(() => {
60
65
  });
@@ -144,7 +149,7 @@ function useTickets() {
144
149
  try {
145
150
  const result = await client.tickets.list();
146
151
  if (mountedRef.current) {
147
- setTickets(result.tickets);
152
+ setTickets(result.data);
148
153
  setHasMore(result.hasMore);
149
154
  }
150
155
  } catch (err) {
@@ -671,21 +676,39 @@ function useCommunitySearch() {
671
676
  }
672
677
 
673
678
  // src/dashboard/CStarDashboardProvider.tsx
674
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect14, useRef as useRef15 } from "react";
679
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect14, useMemo } from "react";
680
+ import { CStarClient } from "@cstar.help/js";
675
681
  import { jsx as jsx4 } from "react/jsx-runtime";
676
682
  var CStarDashboardContext = createContext2(null);
677
- function CStarDashboardProvider({ client, children }) {
678
- if (!client) {
683
+ function CStarDashboardProvider({
684
+ config,
685
+ client: providedClient,
686
+ children
687
+ }) {
688
+ if (!config && !providedClient) {
679
689
  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"
690
+ "CStarDashboardProvider requires either a `config` prop (`{ apiKey, teamId }` \u2014 recommended) or a `client` prop (a pre-built CStarClient instance, deprecated). See https://cstar.help/developers/sdk/react#dashboard"
681
691
  );
682
692
  }
683
- const contextValue = useRef15({ client }).current;
693
+ const client = useMemo(() => {
694
+ if (providedClient) return providedClient;
695
+ return new CStarClient(config);
696
+ }, [
697
+ providedClient,
698
+ config?.apiKey,
699
+ config?.teamId,
700
+ config?.baseUrl,
701
+ config?.version,
702
+ config?.maxRetries,
703
+ config?.timeout
704
+ ]);
705
+ const contextValue = useMemo(() => ({ client }), [client]);
684
706
  useEffect14(() => {
707
+ if (providedClient) return;
685
708
  return () => {
686
709
  client?.realtime?.disconnect?.();
687
710
  };
688
- }, [client]);
711
+ }, [client, providedClient]);
689
712
  return /* @__PURE__ */ jsx4(CStarDashboardContext.Provider, { value: contextValue, children });
690
713
  }
691
714
  function useDashboardClient() {
@@ -697,7 +720,8 @@ function useDashboardClient() {
697
720
  }
698
721
 
699
722
  // src/dashboard/useCStarQuery.ts
700
- import { useState as useState13, useEffect as useEffect15, useRef as useRef16, useCallback as useCallback12 } from "react";
723
+ import { useState as useState13, useEffect as useEffect15, useRef as useRef15, useCallback as useCallback12 } from "react";
724
+ import { CStarValidationError } from "@cstar.help/js";
701
725
  function resolveResource(client, path) {
702
726
  const parts = path.split(".");
703
727
  if (parts.length < 2 || parts[1] !== "list") return null;
@@ -709,11 +733,16 @@ function resolveResource(client, path) {
709
733
  return { resource, basePath: `/${resourceName}` };
710
734
  }
711
735
  function useCStarQuery(resource, params) {
736
+ if (typeof resource !== "string") {
737
+ throw new CStarValidationError(
738
+ `useCStarQuery: \`resource\` must be a string (e.g. 'tickets.list'). Received ${resource === null ? "null" : typeof resource}. The signature is useCStarQuery(resource: string, params?: object).`
739
+ );
740
+ }
712
741
  const client = useDashboardClient();
713
742
  const [data, setData] = useState13(null);
714
743
  const [loading, setLoading] = useState13(true);
715
744
  const [error, setError] = useState13(null);
716
- const subscriptionRef = useRef16(null);
745
+ const subscriptionRef = useRef15(null);
717
746
  const paramsKey = params ? JSON.stringify(params) : "";
718
747
  useEffect15(() => {
719
748
  const resolved = resolveResource(client, resource);
@@ -841,13 +870,13 @@ function useCStarEvents(types) {
841
870
  }
842
871
 
843
872
  // src/dashboard/useBossBattle.ts
844
- import { useState as useState16, useEffect as useEffect17, useRef as useRef17, useCallback as useCallback14 } from "react";
873
+ import { useState as useState16, useEffect as useEffect17, useRef as useRef16, useCallback as useCallback14 } from "react";
845
874
  function useBossBattle() {
846
875
  const client = useDashboardClient();
847
876
  const [boss, setBoss] = useState16(null);
848
877
  const [loading, setLoading] = useState16(true);
849
878
  const [error, setError] = useState16(null);
850
- const subRef = useRef17(null);
879
+ const subRef = useRef16(null);
851
880
  useEffect17(() => {
852
881
  const sub = client.game.subscribeBoss(client.realtime);
853
882
  subRef.current = sub;
@@ -872,13 +901,13 @@ function useBossBattle() {
872
901
  }
873
902
 
874
903
  // src/dashboard/usePlayerStats.ts
875
- import { useState as useState17, useEffect as useEffect18, useRef as useRef18, useCallback as useCallback15 } from "react";
904
+ import { useState as useState17, useEffect as useEffect18, useRef as useRef17, useCallback as useCallback15 } from "react";
876
905
  function usePlayerStats(memberId) {
877
906
  const client = useDashboardClient();
878
907
  const [player, setPlayer] = useState17(null);
879
908
  const [loading, setLoading] = useState17(true);
880
909
  const [error, setError] = useState17(null);
881
- const subRef = useRef18(null);
910
+ const subRef = useRef17(null);
882
911
  useEffect18(() => {
883
912
  const sub = client.game.subscribePlayer(client.realtime, memberId);
884
913
  subRef.current = sub;
@@ -903,13 +932,13 @@ function usePlayerStats(memberId) {
903
932
  }
904
933
 
905
934
  // src/dashboard/useLeaderboard.ts
906
- import { useState as useState18, useEffect as useEffect19, useRef as useRef19, useCallback as useCallback16 } from "react";
935
+ import { useState as useState18, useEffect as useEffect19, useRef as useRef18, useCallback as useCallback16 } from "react";
907
936
  function useLeaderboard(period) {
908
937
  const client = useDashboardClient();
909
938
  const [entries, setEntries] = useState18([]);
910
939
  const [loading, setLoading] = useState18(true);
911
940
  const [error, setError] = useState18(null);
912
- const subRef = useRef19(null);
941
+ const subRef = useRef18(null);
913
942
  useEffect19(() => {
914
943
  const sub = client.game.subscribeLeaderboard(client.realtime, period ? { period } : void 0);
915
944
  subRef.current = sub;
@@ -934,13 +963,13 @@ function useLeaderboard(period) {
934
963
  }
935
964
 
936
965
  // src/dashboard/useGameActivity.ts
937
- import { useState as useState19, useEffect as useEffect20, useRef as useRef20, useCallback as useCallback17 } from "react";
966
+ import { useState as useState19, useEffect as useEffect20, useRef as useRef19, useCallback as useCallback17 } from "react";
938
967
  function useGameActivity() {
939
968
  const client = useDashboardClient();
940
969
  const [events, setEvents] = useState19([]);
941
970
  const [loading, setLoading] = useState19(true);
942
971
  const [error, setError] = useState19(null);
943
- const subRef = useRef20(null);
972
+ const subRef = useRef19(null);
944
973
  useEffect20(() => {
945
974
  const sub = client.game.subscribeActivity(client.realtime);
946
975
  subRef.current = sub;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cstar.help/react",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "React hooks for the cStar customer support platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=18.0.0",
33
- "@cstar.help/js": ">=0.14.0"
33
+ "@cstar.help/js": ">=0.15.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "react": "^18.3.1",