@cstar.help/js 0.10.3 → 0.12.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.
@@ -4,13 +4,47 @@ interface ChatConfig {
4
4
  teamSlug: string;
5
5
  /** Override the base URL (defaults to https://www.cstar.help). */
6
6
  baseUrl?: string;
7
- /** Supabase project URL for realtime subscriptions (optional). */
8
- supabaseUrl?: string;
9
- /** Supabase anon key for realtime subscriptions (optional). */
10
- supabaseAnonKey?: string;
11
- /** Polling interval in ms when Supabase Realtime is not available (defaults to 3000). */
7
+ /**
8
+ * Enable real-time messages and typing indicators. Defaults to true.
9
+ * When false, the SDK falls back to polling.
10
+ */
11
+ realtime?: boolean;
12
+ /** Polling interval in ms when realtime is disabled or unavailable (defaults to 3000). */
12
13
  pollingInterval?: number;
13
14
  }
15
+ /**
16
+ * Which auth path the client used to identify the customer. Set
17
+ * automatically from the `X-Cstar-Auth-Mode` response header on the first
18
+ * authenticated response — consumers don't declare it. Useful for runtime
19
+ * branching in apps that wrap multiple ChatClient instances.
20
+ */
21
+ type ChatAuthMode = 'hmac' | 'session';
22
+ /**
23
+ * Parameters for `loginWithSession()` — for integrators that already have
24
+ * a Supabase access token from their own login flow.
25
+ */
26
+ interface LoginWithSessionParams {
27
+ /** Supabase access token (JWT). Used as Bearer for every subsequent call. */
28
+ accessToken: string;
29
+ /** Optional: the team UUID. If omitted, derived server-side from the token. */
30
+ teamId?: string;
31
+ /** Optional: a hint for the customer name (UI only — server stays authoritative). */
32
+ customerName?: string;
33
+ }
34
+ /** Parameters for `loginWithCredentials()` — email/password sign-in. */
35
+ interface LoginWithCredentialsParams {
36
+ email: string;
37
+ password: string;
38
+ }
39
+ /** Result of a credentials login. The client retains the access token internally. */
40
+ interface LoginWithCredentialsResult {
41
+ customer: {
42
+ id: string;
43
+ email: string;
44
+ name: string | null;
45
+ };
46
+ teamId: string;
47
+ }
14
48
  /** Parameters for identifying a customer via HMAC signature verification. */
15
49
  interface IdentifyParams {
16
50
  /** Customer ID from your system. */
@@ -43,14 +77,16 @@ interface IdentifyResult {
43
77
  /** Team UUID — used for realtime token requests. */
44
78
  teamId: string;
45
79
  }
46
- /** Response from the realtime-token endpoint. */
80
+ /** @internal Response from the realtime-token endpoint. */
47
81
  interface RealtimeTokenResponse {
48
82
  token: string;
49
83
  expiresAt: string;
50
84
  refreshAt: string;
85
+ realtimeUrl: string;
86
+ realtimeKey: string;
51
87
  }
52
- /** A customer conversation (maps to a ticket). */
53
- interface Conversation {
88
+ /** A ticket — a customer's support thread. */
89
+ interface Ticket {
54
90
  id: string;
55
91
  ticketNumber: string;
56
92
  subject: string;
@@ -60,14 +96,14 @@ interface Conversation {
60
96
  updatedAt: string;
61
97
  closedAt: string | null;
62
98
  }
63
- /** Paginated conversation list response. */
64
- interface ConversationListResponse {
65
- conversations: Conversation[];
99
+ /** Paginated ticket list response. */
100
+ interface TicketListResponse {
101
+ tickets: Ticket[];
66
102
  cursor: string | null;
67
103
  hasMore: boolean;
68
104
  }
69
- /** A message in a conversation. */
70
- interface WidgetMessage {
105
+ /** A message on a ticket. */
106
+ interface Message {
71
107
  id: string;
72
108
  content: string;
73
109
  sender: 'customer' | 'agent';
@@ -75,25 +111,30 @@ interface WidgetMessage {
75
111
  created_at: string;
76
112
  read_at: string | null;
77
113
  attachments: unknown[];
114
+ /** Raw AI SDK response payload for agent messages. Widgets can use this
115
+ * with `extractToolCallsFromPayload` to render customer-safe tool chips. */
116
+ payload?: Record<string, unknown> | null;
117
+ /** Whether this message was sent by an AI agent. */
118
+ is_bot?: boolean;
78
119
  }
79
- /** Parameters for listing conversations. */
80
- interface ConversationListParams {
120
+ /** Parameters for listing tickets. */
121
+ interface TicketListParams {
81
122
  limit?: number;
82
123
  cursor?: string;
83
124
  }
84
- /** Parameters for creating a new conversation. */
85
- interface ConversationCreateParams {
125
+ /** Parameters for creating a new ticket. */
126
+ interface TicketCreateParams {
86
127
  subject: string;
87
128
  message?: string;
88
129
  }
89
- /** Typing indicator payload from realtime subscription. */
130
+ /** Typing indicator payload. */
90
131
  interface TypingPayload {
91
132
  agent_id: string;
92
133
  agent_name: string;
93
134
  is_typing: boolean;
94
135
  }
95
136
  /** Callback for message events. */
96
- type MessageCallback = (message: WidgetMessage) => void;
137
+ type MessageCallback = (message: Message) => void;
97
138
  /** Callback for typing indicator events. */
98
139
  type TypingCallback = (payload: TypingPayload) => void;
99
140
 
@@ -180,43 +221,142 @@ interface CommunityFollowResult {
180
221
  following: boolean;
181
222
  }
182
223
 
224
+ /**
225
+ * Typed error classes for the cStar widget chat client.
226
+ *
227
+ * Every error thrown by `ChatClient.fetch()` is one of these subclasses,
228
+ * carrying an HTTP status code, a server-supplied error code, and a
229
+ * `requestId` (from the `X-Request-Id` response header) that customer
230
+ * support can match against server-side logs.
231
+ *
232
+ * Consumer code can branch on instanceof checks instead of string-matching
233
+ * error messages, which is a brittle pattern that breaks the moment the
234
+ * server tweaks its message text:
235
+ *
236
+ * ```typescript
237
+ * try {
238
+ * await chat.identify(customer, signature);
239
+ * } catch (err) {
240
+ * if (err instanceof ChatClientAuthError) {
241
+ * // bad signature, expired token, etc.
242
+ * } else if (err instanceof ChatClientRateLimitError) {
243
+ * // back off and retry after err.retryAfter
244
+ * }
245
+ * }
246
+ * ```
247
+ */
248
+ /** Base class. Thrown by `ChatClient.fetch()` for any non-2xx response. */
249
+ declare class ChatClientError extends Error {
250
+ readonly code: string;
251
+ readonly statusCode: number;
252
+ readonly requestId: string | null;
253
+ constructor(message: string, opts: {
254
+ code?: string;
255
+ statusCode: number;
256
+ requestId?: string | null;
257
+ });
258
+ }
259
+ /** 401 / 403 — auth failed (bad/expired token, wrong team, etc.). */
260
+ declare class ChatClientAuthError extends ChatClientError {
261
+ constructor(opts: {
262
+ message: string;
263
+ code?: string;
264
+ statusCode: number;
265
+ requestId?: string | null;
266
+ });
267
+ }
268
+ /** 429 — too many requests. Inspect `retryAfter` (seconds) before retrying. */
269
+ declare class ChatClientRateLimitError extends ChatClientError {
270
+ readonly retryAfter: number | null;
271
+ constructor(opts: {
272
+ message: string;
273
+ code?: string;
274
+ statusCode: number;
275
+ requestId?: string | null;
276
+ retryAfter?: number | null;
277
+ });
278
+ }
279
+ /** 5xx — the cStar server hit an internal error. Usually transient. */
280
+ declare class ChatClientServerError extends ChatClientError {
281
+ constructor(opts: {
282
+ message: string;
283
+ code?: string;
284
+ statusCode: number;
285
+ requestId?: string | null;
286
+ });
287
+ }
288
+ /**
289
+ * `fetch()` itself failed before a response was received — DNS, TLS,
290
+ * abort, offline, CORS misconfiguration, etc. No statusCode or requestId
291
+ * because the request didn't reach the server. The original cause is
292
+ * preserved on `originalError`.
293
+ */
294
+ declare class ChatClientNetworkError extends ChatClientError {
295
+ readonly originalError: unknown;
296
+ constructor(opts: {
297
+ message: string;
298
+ originalError: unknown;
299
+ });
300
+ }
301
+
183
302
  /**
184
303
  * Customer-facing chat client for the cStar widget API.
185
304
  *
186
- * Supports real-time messages and typing indicators when `supabaseUrl`
187
- * and `supabaseAnonKey` are provided and `@supabase/supabase-js` is installed.
188
- * Falls back to polling otherwise.
305
+ * Supports two auth paths:
306
+ *
307
+ * 1. **HMAC** — `identify(customer, signature)` if you sign customer
308
+ * claims with your team's HMAC secret ("Bring Your Own Auth").
309
+ * 2. **Session** — `loginWithCredentials({ email, password })` or
310
+ * `loginWithSession({ accessToken })` if you use cStar's hosted
311
+ * Supabase auth (or already have a Supabase session from your app).
312
+ *
313
+ * The auth mode is detected automatically from the `X-Cstar-Auth-Mode`
314
+ * response header on the first authenticated call — consumers don't
315
+ * declare it. Real-time messages and typing indicators work in both
316
+ * modes; the SDK opens the right channel based on which login path was
317
+ * used.
318
+ *
319
+ * Real-time is enabled by default and falls back to polling. No extra
320
+ * configuration is required — everything the SDK needs is fetched from
321
+ * the cStar API.
189
322
  *
190
323
  * ```typescript
191
- * const chat = new ChatClient({
192
- * teamSlug: 'acme',
193
- * supabaseUrl: 'https://xxx.supabase.co',
194
- * supabaseAnonKey: 'eyJ...',
195
- * });
324
+ * // HMAC mode
325
+ * const chat = new ChatClient({ teamSlug: 'acme' });
196
326
  * await chat.identify(
197
327
  * { externalId: 'usr_123', email: 'jane@co.com', name: 'Jane', timestamp: Date.now() / 1000 },
198
328
  * 'hmac_signature_hex'
199
329
  * );
330
+ *
331
+ * // Session mode (hosted auth)
332
+ * const chat = new ChatClient({ teamSlug: 'acme' });
333
+ * await chat.loginWithCredentials({ email: 'jane@co.com', password: 'hunter2' });
334
+ *
200
335
  * chat.onMessage('tkt_abc', (msg) => console.log(msg));
201
336
  * chat.onTyping('tkt_abc', ({ agent_name }) => console.log(agent_name, 'is typing'));
202
337
  * ```
338
+ *
339
+ * Errors thrown by `fetch()` (and by the public methods that wrap it)
340
+ * are typed: see `ChatClientAuthError`, `ChatClientRateLimitError`,
341
+ * `ChatClientServerError`, `ChatClientNetworkError`. Each carries
342
+ * `requestId` for correlating with server-side logs.
203
343
  */
204
344
  declare class ChatClient {
205
345
  private readonly baseUrl;
206
346
  private readonly teamSlug;
207
347
  private readonly pollingInterval;
208
- private readonly supabaseUrl?;
209
- private readonly supabaseAnonKey?;
348
+ private readonly realtimeEnabled;
210
349
  private sessionToken;
211
350
  private teamId;
212
351
  private customerId;
352
+ private authMode;
213
353
  private pollingTimers;
214
354
  private realtimeManager;
215
355
  private presenceManager;
216
356
  private realtimeReady;
217
357
  private realtimeInitPromise;
218
- /** Sub-resource for managing conversations. */
219
- readonly conversations: ConversationsApi;
358
+ /** Sub-resource for managing tickets. */
359
+ readonly tickets: TicketsApi;
220
360
  /** Sub-resource for managing messages. */
221
361
  readonly messages: MessagesApi;
222
362
  /** Sub-resource for community actions (create posts, vote, comment). Uses the same session as chat. */
@@ -224,10 +364,10 @@ declare class ChatClient {
224
364
  constructor(config: ChatConfig);
225
365
  /**
226
366
  * Identify a customer using HMAC signature verification.
227
- * Must be called before accessing conversations or messages.
367
+ * Must be called before accessing tickets or messages.
228
368
  *
229
- * If `supabaseUrl` and `supabaseAnonKey` were provided in the config,
230
- * this will also initialize real-time subscriptions (non-blocking).
369
+ * Real-time subscriptions are initialized automatically (non-blocking)
370
+ * unless `realtime: false` was passed in config.
231
371
  *
232
372
  * @param customer - Customer identity fields
233
373
  * @param signature - HMAC-SHA256 hex signature of the signed fields
@@ -235,32 +375,78 @@ declare class ChatClient {
235
375
  */
236
376
  identify(customer: IdentifyParams, signature: string, testMode?: boolean): Promise<IdentifyResult>;
237
377
  /**
238
- * Subscribe to new messages on a conversation.
239
- * Uses Supabase Realtime (Postgres Changes) when available, falls back to polling.
378
+ * Sign in with cStar's hosted password auth. The SDK keeps the resulting
379
+ * Supabase access token and uses it as Bearer for every subsequent call.
380
+ * Real-time bootstraps automatically (same flow as `identify()`).
381
+ *
382
+ * Use this when your customers don't have a session in your app and you
383
+ * want cStar to handle login. For the case where your app already has a
384
+ * Supabase session, use `loginWithSession({ accessToken })` instead.
385
+ */
386
+ loginWithCredentials(params: LoginWithCredentialsParams): Promise<LoginWithCredentialsResult>;
387
+ /**
388
+ * Use an existing Supabase access token for widget calls. Skip the cStar
389
+ * login flow entirely; the SDK uses the supplied token as Bearer and
390
+ * bootstraps real-time the same way `identify()` does.
391
+ *
392
+ * The widget API verifies the token via the `widget-session` strategy on
393
+ * every request — there is no client-side validation here.
394
+ */
395
+ loginWithSession(params: LoginWithSessionParams): Promise<void>;
396
+ /**
397
+ * Returns the auth mode the client is currently using, or `null` before
398
+ * any authenticated call has resolved one. Useful for branching UI
399
+ * (e.g. to show a "Sign out" button only in session mode).
400
+ */
401
+ getAuthMode(): ChatAuthMode | null;
402
+ /**
403
+ * Subscribe to new messages on a ticket.
404
+ * Uses real-time delivery when available, falls back to polling.
240
405
  *
241
406
  * @returns Unsubscribe function
242
407
  */
243
408
  onMessage(ticketId: string, callback: MessageCallback): () => void;
244
409
  /**
245
410
  * Subscribe to typing indicator events.
246
- * Requires Supabase Realtime — returns a no-op if unavailable.
411
+ * Requires real-time — returns a no-op when unavailable.
247
412
  *
248
413
  * @returns Unsubscribe function
249
414
  */
250
415
  onTyping(ticketId: string, callback: TypingCallback): () => void;
251
416
  /**
252
- * Send a customer typing indicator (requires realtime).
253
- * No-op if realtime is not available.
417
+ * Send a customer typing indicator (requires real-time).
418
+ * No-op when real-time is unavailable.
254
419
  */
255
420
  sendTyping(ticketId: string, isTyping: boolean): Promise<void>;
256
421
  /** Disconnect all subscriptions and clean up. */
257
422
  disconnect(): void;
258
- /** @internal Make an authenticated request to the widget API. */
423
+ /**
424
+ * @internal Make an authenticated request to the widget API.
425
+ *
426
+ * On any non-2xx response, throws a typed subclass of `ChatClientError`
427
+ * (`ChatClientAuthError`, `ChatClientRateLimitError`, etc.) so consumers
428
+ * can branch on error class instead of parsing message strings.
429
+ *
430
+ * On the way out, reads the `X-Cstar-Auth-Mode` response header (set by
431
+ * widgetHandler on every response) and caches it on the client. After
432
+ * the first authenticated round-trip, `getAuthMode()` returns the
433
+ * server-confirmed mode.
434
+ */
259
435
  fetch<T>(path: string, options: {
260
436
  method: string;
261
437
  body?: unknown;
262
438
  query?: Record<string, string | undefined>;
263
439
  }): Promise<T>;
440
+ /**
441
+ * Look up the team UUID from the configured `teamSlug`. Used by
442
+ * `loginWithCredentials`. HMAC integrators (`identify()`) never hit
443
+ * this path — verify-identity accepts the slug directly.
444
+ *
445
+ * @internal
446
+ */
447
+ private resolveTeamIdFromSlug;
448
+ /** @internal Bootstrap realtime from a session login (no precomputed customerId). */
449
+ private initRealtimeFromSession;
264
450
  /** @internal Get the stored team ID for requests that need it. */
265
451
  getTeamId(): string | null;
266
452
  /** @internal Set team ID from identify response or other source. */
@@ -272,14 +458,14 @@ declare class ChatClient {
272
458
  private pollMessages;
273
459
  private buildUrl;
274
460
  }
275
- /** Conversations sub-resource on ChatClient. */
276
- declare class ConversationsApi {
461
+ /** Tickets sub-resource on ChatClient. */
462
+ declare class TicketsApi {
277
463
  private readonly chat;
278
464
  constructor(chat: ChatClient);
279
- /** List conversations for the identified customer. */
280
- list(params?: ConversationListParams): Promise<ConversationListResponse>;
281
- /** Create a new conversation (ticket). */
282
- create(params: ConversationCreateParams): Promise<Conversation>;
465
+ /** List tickets for the identified customer. */
466
+ list(params?: TicketListParams): Promise<TicketListResponse>;
467
+ /** Create a new ticket. */
468
+ create(params: TicketCreateParams): Promise<Ticket>;
283
469
  }
284
470
  /**
285
471
  * Community sub-resource on ChatClient.
@@ -313,10 +499,10 @@ declare class ChatCommunityApi {
313
499
  declare class MessagesApi {
314
500
  private readonly chat;
315
501
  constructor(chat: ChatClient);
316
- /** List messages for a conversation. */
317
- list(ticketId: string): Promise<WidgetMessage[]>;
318
- /** Send a message to a conversation. */
319
- send(ticketId: string, content: string): Promise<WidgetMessage>;
502
+ /** List messages for a ticket. */
503
+ list(ticketId: string): Promise<Message[]>;
504
+ /** Send a message to a ticket. */
505
+ send(ticketId: string, content: string): Promise<Message>;
320
506
  }
321
507
 
322
- export { ChatClient, type ChatConfig, type Conversation, type ConversationCreateParams, type ConversationListParams, type ConversationListResponse, type IdentifyParams, type IdentifyResult, type MessageCallback, type RealtimeTokenResponse, type TypingCallback, type TypingPayload, type WidgetMessage };
508
+ export { type ChatAuthMode, ChatClient, ChatClientAuthError, ChatClientError, ChatClientNetworkError, ChatClientRateLimitError, ChatClientServerError, type ChatConfig, type IdentifyParams, type IdentifyResult, type LoginWithCredentialsParams, type LoginWithCredentialsResult, type LoginWithSessionParams, type Message, type MessageCallback, type RealtimeTokenResponse, type Ticket, type TicketCreateParams, type TicketListParams, type TicketListResponse, type TypingCallback, type TypingPayload };