@cstar.help/js 0.12.1 → 0.13.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.
@@ -88,13 +88,24 @@ interface RealtimeTokenResponse {
88
88
  /** A ticket — a customer's support thread. */
89
89
  interface Ticket {
90
90
  id: string;
91
- ticketNumber: string;
91
+ /**
92
+ * Server-issued display ID (e.g. `CST-1024`). `null` immediately after
93
+ * `tickets.create` (the prefix is assigned asynchronously); populated on
94
+ * subsequent `tickets.list` / single fetches.
95
+ */
96
+ ticketNumber: string | null;
92
97
  subject: string;
93
98
  status: string;
94
99
  messageCount: number;
95
100
  createdAt: string;
96
101
  updatedAt: string;
97
102
  closedAt: string | null;
103
+ /**
104
+ * Set on `tickets.create` responses — contains the first message the
105
+ * widget sent, so callers don't need a round-trip `messages.list(...)`
106
+ * to echo it back. `undefined` on list/get responses.
107
+ */
108
+ messages?: Message[];
98
109
  }
99
110
  /** Paginated ticket list response. */
100
111
  interface TicketListResponse {
@@ -244,46 +255,39 @@ interface CommunityFollowResult {
244
255
  * }
245
256
  * }
246
257
  * ```
258
+ *
259
+ * Constructors accept either a full opts object (used by the SDK's HTTP
260
+ * paths) or a bare message string — handy for SDK precondition checks
261
+ * (`throw new ChatClientAuthError('Not identified...')`) and tests.
262
+ * Closes F-032's spirit for the chat-client classes.
247
263
  */
264
+ interface ChatErrorOpts {
265
+ message: string;
266
+ code?: string;
267
+ statusCode?: number;
268
+ requestId?: string | null;
269
+ }
248
270
  /** Base class. Thrown by `ChatClient.fetch()` for any non-2xx response. */
249
271
  declare class ChatClientError extends Error {
250
272
  readonly code: string;
251
273
  readonly statusCode: number;
252
274
  readonly requestId: string | null;
253
- constructor(message: string, opts: {
254
- code?: string;
255
- statusCode: number;
256
- requestId?: string | null;
257
- });
275
+ constructor(arg: ChatErrorOpts | string);
258
276
  }
259
277
  /** 401 / 403 — auth failed (bad/expired token, wrong team, etc.). */
260
278
  declare class ChatClientAuthError extends ChatClientError {
261
- constructor(opts: {
262
- message: string;
263
- code?: string;
264
- statusCode: number;
265
- requestId?: string | null;
266
- });
279
+ constructor(arg: ChatErrorOpts | string);
267
280
  }
268
281
  /** 429 — too many requests. Inspect `retryAfter` (seconds) before retrying. */
269
282
  declare class ChatClientRateLimitError extends ChatClientError {
270
283
  readonly retryAfter: number | null;
271
- constructor(opts: {
272
- message: string;
273
- code?: string;
274
- statusCode: number;
275
- requestId?: string | null;
284
+ constructor(arg: (ChatErrorOpts & {
276
285
  retryAfter?: number | null;
277
- });
286
+ }) | string);
278
287
  }
279
288
  /** 5xx — the cStar server hit an internal error. Usually transient. */
280
289
  declare class ChatClientServerError extends ChatClientError {
281
- constructor(opts: {
282
- message: string;
283
- code?: string;
284
- statusCode: number;
285
- requestId?: string | null;
286
- });
290
+ constructor(arg: ChatErrorOpts | string);
287
291
  }
288
292
  /**
289
293
  * `fetch()` itself failed before a response was received — DNS, TLS,
@@ -88,13 +88,24 @@ interface RealtimeTokenResponse {
88
88
  /** A ticket — a customer's support thread. */
89
89
  interface Ticket {
90
90
  id: string;
91
- ticketNumber: string;
91
+ /**
92
+ * Server-issued display ID (e.g. `CST-1024`). `null` immediately after
93
+ * `tickets.create` (the prefix is assigned asynchronously); populated on
94
+ * subsequent `tickets.list` / single fetches.
95
+ */
96
+ ticketNumber: string | null;
92
97
  subject: string;
93
98
  status: string;
94
99
  messageCount: number;
95
100
  createdAt: string;
96
101
  updatedAt: string;
97
102
  closedAt: string | null;
103
+ /**
104
+ * Set on `tickets.create` responses — contains the first message the
105
+ * widget sent, so callers don't need a round-trip `messages.list(...)`
106
+ * to echo it back. `undefined` on list/get responses.
107
+ */
108
+ messages?: Message[];
98
109
  }
99
110
  /** Paginated ticket list response. */
100
111
  interface TicketListResponse {
@@ -244,46 +255,39 @@ interface CommunityFollowResult {
244
255
  * }
245
256
  * }
246
257
  * ```
258
+ *
259
+ * Constructors accept either a full opts object (used by the SDK's HTTP
260
+ * paths) or a bare message string — handy for SDK precondition checks
261
+ * (`throw new ChatClientAuthError('Not identified...')`) and tests.
262
+ * Closes F-032's spirit for the chat-client classes.
247
263
  */
264
+ interface ChatErrorOpts {
265
+ message: string;
266
+ code?: string;
267
+ statusCode?: number;
268
+ requestId?: string | null;
269
+ }
248
270
  /** Base class. Thrown by `ChatClient.fetch()` for any non-2xx response. */
249
271
  declare class ChatClientError extends Error {
250
272
  readonly code: string;
251
273
  readonly statusCode: number;
252
274
  readonly requestId: string | null;
253
- constructor(message: string, opts: {
254
- code?: string;
255
- statusCode: number;
256
- requestId?: string | null;
257
- });
275
+ constructor(arg: ChatErrorOpts | string);
258
276
  }
259
277
  /** 401 / 403 — auth failed (bad/expired token, wrong team, etc.). */
260
278
  declare class ChatClientAuthError extends ChatClientError {
261
- constructor(opts: {
262
- message: string;
263
- code?: string;
264
- statusCode: number;
265
- requestId?: string | null;
266
- });
279
+ constructor(arg: ChatErrorOpts | string);
267
280
  }
268
281
  /** 429 — too many requests. Inspect `retryAfter` (seconds) before retrying. */
269
282
  declare class ChatClientRateLimitError extends ChatClientError {
270
283
  readonly retryAfter: number | null;
271
- constructor(opts: {
272
- message: string;
273
- code?: string;
274
- statusCode: number;
275
- requestId?: string | null;
284
+ constructor(arg: (ChatErrorOpts & {
276
285
  retryAfter?: number | null;
277
- });
286
+ }) | string);
278
287
  }
279
288
  /** 5xx — the cStar server hit an internal error. Usually transient. */
280
289
  declare class ChatClientServerError extends ChatClientError {
281
- constructor(opts: {
282
- message: string;
283
- code?: string;
284
- statusCode: number;
285
- requestId?: string | null;
286
- });
290
+ constructor(arg: ChatErrorOpts | string);
287
291
  }
288
292
  /**
289
293
  * `fetch()` itself failed before a response was received — DNS, TLS,
@@ -248,37 +248,45 @@ var PresenceManager = class {
248
248
  };
249
249
 
250
250
  // src/chat/errors.ts
251
+ function normalizeChatArgs(arg, fallbackCode) {
252
+ if (typeof arg === "string") {
253
+ return { message: arg, code: fallbackCode, statusCode: 0, requestId: null };
254
+ }
255
+ return { ...arg, code: arg.code ?? fallbackCode };
256
+ }
251
257
  var ChatClientError = class extends Error {
252
- constructor(message, opts) {
253
- super(message);
258
+ constructor(arg) {
259
+ const opts = normalizeChatArgs(arg, "UNKNOWN");
260
+ super(opts.message);
254
261
  this.name = "ChatClientError";
255
262
  this.code = opts.code ?? "UNKNOWN";
256
- this.statusCode = opts.statusCode;
263
+ this.statusCode = opts.statusCode ?? 0;
257
264
  this.requestId = opts.requestId ?? null;
258
265
  }
259
266
  };
260
267
  var ChatClientAuthError = class extends ChatClientError {
261
- constructor(opts) {
262
- super(opts.message, opts);
268
+ constructor(arg) {
269
+ super(normalizeChatArgs(arg, "AUTH_ERROR"));
263
270
  this.name = "ChatClientAuthError";
264
271
  }
265
272
  };
266
273
  var ChatClientRateLimitError = class extends ChatClientError {
267
- constructor(opts) {
268
- super(opts.message, opts);
274
+ constructor(arg) {
275
+ const opts = typeof arg === "string" ? { message: arg, code: "RATE_LIMIT", statusCode: 429, requestId: null, retryAfter: null } : { ...arg, code: arg.code ?? "RATE_LIMIT" };
276
+ super(opts);
269
277
  this.name = "ChatClientRateLimitError";
270
278
  this.retryAfter = opts.retryAfter ?? null;
271
279
  }
272
280
  };
273
281
  var ChatClientServerError = class extends ChatClientError {
274
- constructor(opts) {
275
- super(opts.message, opts);
282
+ constructor(arg) {
283
+ super(normalizeChatArgs(arg, "SERVER_ERROR"));
276
284
  this.name = "ChatClientServerError";
277
285
  }
278
286
  };
279
287
  var ChatClientNetworkError = class extends ChatClientError {
280
288
  constructor(opts) {
281
- super(opts.message, { code: "NETWORK_ERROR", statusCode: 0, requestId: null });
289
+ super({ message: opts.message, code: "NETWORK_ERROR", statusCode: 0, requestId: null });
282
290
  this.name = "ChatClientNetworkError";
283
291
  this.originalError = opts.originalError;
284
292
  }
@@ -333,6 +341,7 @@ var ChatClient = class {
333
341
  this.sessionToken = response.data.sessionToken;
334
342
  this.teamId = response.data.teamId;
335
343
  this.customerId = response.data.customer.id;
344
+ this.authMode = "hmac";
336
345
  if (this.realtimeEnabled) {
337
346
  this.realtimeInitPromise = this.initRealtime(
338
347
  response.data.customer.id,
@@ -530,7 +539,8 @@ var ChatClient = class {
530
539
  requestId
531
540
  });
532
541
  }
533
- throw new ChatClientError(message, {
542
+ throw new ChatClientError({
543
+ message,
534
544
  code,
535
545
  statusCode: response.status,
536
546
  requestId
@@ -668,7 +678,7 @@ var TicketsApi = class {
668
678
  /** List tickets for the identified customer. */
669
679
  async list(params) {
670
680
  const teamId = this.chat.getTeamId();
671
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
681
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
672
682
  const response = await this.chat.fetch("/api/widget/conversations", {
673
683
  method: "GET",
674
684
  query: {
@@ -686,7 +696,7 @@ var TicketsApi = class {
686
696
  /** Create a new ticket. */
687
697
  async create(params) {
688
698
  const teamId = this.chat.getTeamId();
689
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
699
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
690
700
  const response = await this.chat.fetch("/api/widget/tickets", {
691
701
  method: "POST",
692
702
  body: {
@@ -695,15 +705,25 @@ var TicketsApi = class {
695
705
  message: params.message
696
706
  }
697
707
  });
708
+ const messages = (response.data.messages ?? []).map((m) => ({
709
+ id: m.id,
710
+ content: m.content,
711
+ sender: m.sender,
712
+ sender_name: m.sender_name,
713
+ created_at: m.created_at,
714
+ read_at: m.read_at,
715
+ attachments: m.attachments ?? []
716
+ }));
698
717
  return {
699
718
  id: response.data.id,
700
- ticketNumber: "",
719
+ ticketNumber: null,
701
720
  subject: response.data.subject,
702
721
  status: response.data.status,
703
- messageCount: params.message ? 1 : 0,
722
+ messageCount: messages.length,
704
723
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
705
724
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
706
- closedAt: null
725
+ closedAt: null,
726
+ messages
707
727
  };
708
728
  }
709
729
  };
@@ -714,7 +734,7 @@ var ChatCommunityApi = class {
714
734
  /** Create a new community post as the identified customer. */
715
735
  async createPost(params) {
716
736
  const teamId = this.chat.getTeamId();
717
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
737
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
718
738
  const response = await this.chat.fetch("/api/widget/community", {
719
739
  method: "POST",
720
740
  body: {
@@ -731,7 +751,7 @@ var ChatCommunityApi = class {
731
751
  /** Update a post. Only the original author can edit. */
732
752
  async updatePost(postId, params) {
733
753
  const teamId = this.chat.getTeamId();
734
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
754
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
735
755
  const response = await this.chat.fetch("/api/widget/community", {
736
756
  method: "POST",
737
757
  body: {
@@ -748,7 +768,7 @@ var ChatCommunityApi = class {
748
768
  /** Delete a post (soft delete). Only the original author can delete. */
749
769
  async deletePost(postId) {
750
770
  const teamId = this.chat.getTeamId();
751
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
771
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
752
772
  await this.chat.fetch("/api/widget/community", {
753
773
  method: "POST",
754
774
  body: { teamId, action: "deletePost", postId }
@@ -757,7 +777,7 @@ var ChatCommunityApi = class {
757
777
  /** Update a comment. Only the original author can edit. */
758
778
  async updateComment(postId, commentId, params) {
759
779
  const teamId = this.chat.getTeamId();
760
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
780
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
761
781
  const response = await this.chat.fetch("/api/widget/community", {
762
782
  method: "POST",
763
783
  body: {
@@ -773,7 +793,7 @@ var ChatCommunityApi = class {
773
793
  /** Delete a comment (soft delete). Only the original author can delete. */
774
794
  async deleteComment(postId, commentId) {
775
795
  const teamId = this.chat.getTeamId();
776
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
796
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
777
797
  await this.chat.fetch("/api/widget/community", {
778
798
  method: "POST",
779
799
  body: { teamId, action: "deleteComment", postId, commentId }
@@ -782,7 +802,7 @@ var ChatCommunityApi = class {
782
802
  /** Toggle vote on a post. Returns new vote state and count. */
783
803
  async vote(postId) {
784
804
  const teamId = this.chat.getTeamId();
785
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
805
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
786
806
  const response = await this.chat.fetch("/api/widget/community", {
787
807
  method: "POST",
788
808
  body: { teamId, action: "vote", postId }
@@ -792,7 +812,7 @@ var ChatCommunityApi = class {
792
812
  /** Add a comment to a post. Supports 1-level nesting via parentCommentId. */
793
813
  async comment(postId, params) {
794
814
  const teamId = this.chat.getTeamId();
795
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
815
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
796
816
  const response = await this.chat.fetch("/api/widget/community", {
797
817
  method: "POST",
798
818
  body: {
@@ -808,7 +828,7 @@ var ChatCommunityApi = class {
808
828
  /** Toggle follow on a post. */
809
829
  async follow(postId) {
810
830
  const teamId = this.chat.getTeamId();
811
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
831
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
812
832
  const response = await this.chat.fetch("/api/widget/community", {
813
833
  method: "POST",
814
834
  body: { teamId, action: "follow", postId }
@@ -818,7 +838,7 @@ var ChatCommunityApi = class {
818
838
  /** Check if the current user has voted on a post. */
819
839
  async getVoteStatus(postId) {
820
840
  const teamId = this.chat.getTeamId();
821
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
841
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
822
842
  const response = await this.chat.fetch("/api/widget/community", {
823
843
  method: "GET",
824
844
  query: { teamId, postId }
@@ -829,7 +849,7 @@ var ChatCommunityApi = class {
829
849
  async bulkVoteStatus(postIds) {
830
850
  if (postIds.length === 0) return {};
831
851
  const teamId = this.chat.getTeamId();
832
- if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
852
+ if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
833
853
  const response = await this.chat.fetch("/api/widget/community", {
834
854
  method: "GET",
835
855
  query: { teamId, postIds: postIds.join(",") }
@@ -23,6 +23,221 @@ __export(community_exports, {
23
23
  CommunityClient: () => CommunityClient
24
24
  });
25
25
  module.exports = __toCommonJS(community_exports);
26
+
27
+ // src/errors.ts
28
+ var ErrorType = {
29
+ AUTHENTICATION_ERROR: "authentication_error",
30
+ PERMISSION_ERROR: "permission_error",
31
+ INVALID_REQUEST_ERROR: "invalid_request_error",
32
+ NOT_FOUND_ERROR: "not_found_error",
33
+ CONFLICT_ERROR: "conflict_error",
34
+ RATE_LIMIT_ERROR: "rate_limit_error",
35
+ FEATURE_NOT_CONFIGURED: "feature_not_configured",
36
+ API_ERROR: "api_error",
37
+ SERVER_ERROR: "server_error"
38
+ };
39
+ var DEFAULT_DOC_URL = "https://cstar.help/developers/api/errors";
40
+ function normalizeArgs(messageOrStatus, maybeBody, defaults) {
41
+ if (typeof messageOrStatus === "string") {
42
+ return {
43
+ statusCode: defaults?.code === "rate_limit_error" ? 429 : 0,
44
+ body: {
45
+ type: defaults?.type ?? ErrorType.API_ERROR,
46
+ code: defaults?.code ?? "sdk_error",
47
+ message: messageOrStatus,
48
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL,
49
+ ...defaults?.feature ? { feature: defaults.feature } : {},
50
+ ...defaults?.enable_url ? { enable_url: defaults.enable_url } : {}
51
+ }
52
+ };
53
+ }
54
+ return {
55
+ statusCode: messageOrStatus,
56
+ body: maybeBody ?? {
57
+ type: defaults?.type ?? ErrorType.API_ERROR,
58
+ code: defaults?.code ?? "unknown_error",
59
+ message: defaults?.message ?? `Request failed with status ${messageOrStatus}`,
60
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL
61
+ }
62
+ };
63
+ }
64
+ var CStarError = class extends Error {
65
+ constructor(messageOrStatus, body) {
66
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body);
67
+ super(resolved.message);
68
+ this.name = "CStarError";
69
+ this.type = resolved.type;
70
+ this.code = resolved.code;
71
+ this.param = resolved.param;
72
+ this.docUrl = resolved.doc_url;
73
+ this.requestId = resolved.request_id;
74
+ this.statusCode = statusCode;
75
+ }
76
+ };
77
+ var CStarAuthenticationError = class extends CStarError {
78
+ constructor(messageOrStatus, body) {
79
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
80
+ type: ErrorType.AUTHENTICATION_ERROR,
81
+ code: "authentication_failed",
82
+ doc_url: `${DEFAULT_DOC_URL}#authentication-error`
83
+ });
84
+ super(statusCode, resolved);
85
+ this.name = "CStarAuthenticationError";
86
+ }
87
+ };
88
+ var CStarRateLimitError = class extends CStarError {
89
+ constructor(messageOrStatus, body, retryAfter) {
90
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
91
+ type: ErrorType.RATE_LIMIT_ERROR,
92
+ code: "rate_limit_exceeded",
93
+ doc_url: `${DEFAULT_DOC_URL}#rate-limit-error`
94
+ });
95
+ super(statusCode === 0 ? 429 : statusCode, resolved);
96
+ this.name = "CStarRateLimitError";
97
+ this.retryAfter = retryAfter ?? 60;
98
+ }
99
+ };
100
+ var CStarNotFoundError = class extends CStarError {
101
+ constructor(messageOrStatus, body) {
102
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
103
+ type: ErrorType.NOT_FOUND_ERROR,
104
+ code: "resource_not_found",
105
+ doc_url: `${DEFAULT_DOC_URL}#not-found-error`
106
+ });
107
+ super(statusCode, resolved);
108
+ this.name = "CStarNotFoundError";
109
+ }
110
+ };
111
+ var CStarValidationError = class extends CStarError {
112
+ constructor(messageOrStatus, body) {
113
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
114
+ type: ErrorType.INVALID_REQUEST_ERROR,
115
+ code: "validation_error",
116
+ doc_url: `${DEFAULT_DOC_URL}#validation-error`
117
+ });
118
+ super(statusCode, resolved);
119
+ this.name = "CStarValidationError";
120
+ }
121
+ };
122
+ var CStarPermissionError = class extends CStarError {
123
+ constructor(messageOrStatus, body) {
124
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
125
+ type: ErrorType.PERMISSION_ERROR,
126
+ code: "permission_denied",
127
+ doc_url: `${DEFAULT_DOC_URL}#permission-error`
128
+ });
129
+ super(statusCode, resolved);
130
+ this.name = "CStarPermissionError";
131
+ }
132
+ };
133
+ var CStarConflictError = class extends CStarError {
134
+ constructor(messageOrStatus, body) {
135
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
136
+ type: ErrorType.CONFLICT_ERROR,
137
+ code: "conflict",
138
+ doc_url: `${DEFAULT_DOC_URL}#conflict-error`
139
+ });
140
+ super(statusCode, resolved);
141
+ this.name = "CStarConflictError";
142
+ }
143
+ };
144
+ var CStarServerError = class extends CStarError {
145
+ constructor(messageOrStatus, body) {
146
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
147
+ type: ErrorType.SERVER_ERROR,
148
+ code: "server_error",
149
+ doc_url: `${DEFAULT_DOC_URL}#server-error`
150
+ });
151
+ super(statusCode, resolved);
152
+ this.name = "CStarServerError";
153
+ }
154
+ };
155
+ var CStarFeatureDisabledError = class extends CStarError {
156
+ constructor(messageOrStatus, body) {
157
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
158
+ type: ErrorType.FEATURE_NOT_CONFIGURED,
159
+ code: "feature_not_configured",
160
+ doc_url: `${DEFAULT_DOC_URL}#feature-not-configured`
161
+ });
162
+ super(statusCode, resolved);
163
+ this.name = "CStarFeatureDisabledError";
164
+ this.feature = resolved.feature ?? "unknown";
165
+ this.enableUrl = resolved.enable_url ?? "";
166
+ }
167
+ };
168
+ function parsePublicError(status, body, fallbackMessage) {
169
+ const obj = body && typeof body === "object" ? body : null;
170
+ const errVal = obj?.error;
171
+ let message = fallbackMessage;
172
+ let structured = null;
173
+ if (typeof errVal === "string") {
174
+ message = errVal;
175
+ } else if (errVal && typeof errVal === "object") {
176
+ const e = errVal;
177
+ if (typeof e.message === "string") message = e.message;
178
+ structured = {
179
+ type: typeof e.type === "string" ? e.type : ErrorType.API_ERROR,
180
+ code: typeof e.code === "string" ? e.code : "api_error",
181
+ message,
182
+ param: typeof e.param === "string" ? e.param : void 0,
183
+ doc_url: typeof e.doc_url === "string" ? e.doc_url : DEFAULT_DOC_URL,
184
+ request_id: typeof e.request_id === "string" ? e.request_id : void 0,
185
+ feature: typeof e.feature === "string" ? e.feature : void 0,
186
+ enable_url: typeof e.enable_url === "string" ? e.enable_url : void 0
187
+ };
188
+ } else if (typeof obj?.message === "string") {
189
+ message = obj.message;
190
+ }
191
+ const errorBody = structured ?? {
192
+ type: ErrorType.API_ERROR,
193
+ code: status >= 500 ? "server_error" : "request_failed",
194
+ message,
195
+ doc_url: DEFAULT_DOC_URL
196
+ };
197
+ return parseError(status, { error: errorBody });
198
+ }
199
+ var STATUS_TO_CLASS = {
200
+ 400: CStarValidationError,
201
+ 401: CStarAuthenticationError,
202
+ 403: CStarPermissionError,
203
+ 404: CStarNotFoundError,
204
+ 409: CStarConflictError,
205
+ 422: CStarValidationError,
206
+ 429: CStarRateLimitError
207
+ };
208
+ var TYPE_TO_CLASS = {
209
+ [ErrorType.AUTHENTICATION_ERROR]: CStarAuthenticationError,
210
+ [ErrorType.PERMISSION_ERROR]: CStarPermissionError,
211
+ [ErrorType.RATE_LIMIT_ERROR]: CStarRateLimitError,
212
+ [ErrorType.NOT_FOUND_ERROR]: CStarNotFoundError,
213
+ [ErrorType.INVALID_REQUEST_ERROR]: CStarValidationError,
214
+ [ErrorType.CONFLICT_ERROR]: CStarConflictError,
215
+ [ErrorType.SERVER_ERROR]: CStarServerError,
216
+ [ErrorType.FEATURE_NOT_CONFIGURED]: CStarFeatureDisabledError
217
+ };
218
+ function parseError(statusCode, body, retryAfter) {
219
+ const errorBody = body?.error ?? {
220
+ type: ErrorType.API_ERROR,
221
+ code: "unknown_error",
222
+ message: `Request failed with status ${statusCode}`,
223
+ doc_url: DEFAULT_DOC_URL
224
+ };
225
+ if (statusCode === 429) {
226
+ return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
227
+ }
228
+ const StatusCls = STATUS_TO_CLASS[statusCode];
229
+ if (StatusCls) {
230
+ return new StatusCls(statusCode, errorBody);
231
+ }
232
+ const TypeCls = TYPE_TO_CLASS[errorBody.type];
233
+ if (TypeCls) {
234
+ return new TypeCls(statusCode, errorBody);
235
+ }
236
+ if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
237
+ return new CStarError(statusCode, errorBody);
238
+ }
239
+
240
+ // src/community/index.ts
26
241
  var DEFAULT_BASE_URL = "https://www.cstar.help";
27
242
  var CommunityClient = class {
28
243
  constructor(config) {
@@ -30,6 +245,13 @@ var CommunityClient = class {
30
245
  this.teamId = null;
31
246
  this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
32
247
  this.teamSlug = config.teamSlug;
248
+ this.publishableKey = config.publishableKey ?? null;
249
+ this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
250
+ if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
251
+ throw new Error(
252
+ "[cStar] CommunityClient: publishableKey must start with `pk_test_` or `pk_live_`. Secret keys (`sk_*`) cannot be used in browser/anonymous contexts."
253
+ );
254
+ }
33
255
  }
34
256
  /**
35
257
  * Set a session token from identity verification.
@@ -246,13 +468,13 @@ var CommunityClient = class {
246
468
  */
247
469
  resolveTeamId() {
248
470
  if (this.teamId) return this.teamId;
249
- throw new Error(
471
+ throw new CStarValidationError(
250
472
  "Team ID is required for authenticated operations. Pass it via setSessionToken(token, teamId) or use ChatClient.identify() which provides it automatically."
251
473
  );
252
474
  }
253
475
  async authenticatedFetch(path, options) {
254
476
  if (!this.sessionToken) {
255
- throw new Error(
477
+ throw new CStarValidationError(
256
478
  "Session token required. Call setSessionToken() or use ChatClient.identify() first."
257
479
  );
258
480
  }
@@ -275,9 +497,8 @@ var CommunityClient = class {
275
497
  body: options.body ? JSON.stringify(options.body) : void 0
276
498
  });
277
499
  if (!response.ok) {
278
- const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
279
- const message = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message ?? `Community API error: ${response.status}`;
280
- throw new Error(message);
500
+ const errorBody = await response.json().catch(() => null);
501
+ throw parsePublicError(response.status, errorBody, `Community API error: ${response.status}`);
281
502
  }
282
503
  return response.json();
283
504
  }
@@ -291,14 +512,17 @@ var CommunityClient = class {
291
512
  const qs = params.toString();
292
513
  if (qs) url += `?${qs}`;
293
514
  }
515
+ const headers = { "Content-Type": "application/json" };
516
+ if (this.publishableKey) {
517
+ headers["Authorization"] = `Bearer ${this.publishableKey}`;
518
+ }
294
519
  const response = await globalThis.fetch(url, {
295
520
  method: "GET",
296
- headers: { "Content-Type": "application/json" }
521
+ headers
297
522
  });
298
523
  if (!response.ok) {
299
- const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
300
- const message = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message ?? `Community API error: ${response.status}`;
301
- throw new Error(message);
524
+ const errorBody = await response.json().catch(() => null);
525
+ throw parsePublicError(response.status, errorBody, `Community API error: ${response.status}`);
302
526
  }
303
527
  return response.json();
304
528
  }