@cstar.help/js 0.12.0 → 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.
@@ -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
  }
@@ -9,6 +9,14 @@ interface CommunityAttachment {
9
9
  interface CommunityConfig {
10
10
  /** Team slug (e.g., 'acme'). Identifies the community to query. */
11
11
  teamSlug: string;
12
+ /**
13
+ * Optional publishable key (`pk_test_*` or `pk_live_*`). When provided,
14
+ * the client switches data scope to match the key's mode (test vs live).
15
+ * Without a key, defaults to live — backwards-compatible with existing
16
+ * widget integrations. See `LibraryConfig.publishableKey` for the full
17
+ * test/live story.
18
+ */
19
+ publishableKey?: string;
12
20
  /** Override the base URL (defaults to https://www.cstar.help). */
13
21
  baseUrl?: string;
14
22
  }
@@ -193,6 +201,9 @@ interface CommunityPostDetail {
193
201
  declare class CommunityClient {
194
202
  private readonly baseUrl;
195
203
  private readonly teamSlug;
204
+ private readonly publishableKey;
205
+ /** Resolved mode based on the publishable-key prefix (`pk_test_*` => `test`). */
206
+ readonly mode: 'live' | 'test';
196
207
  private sessionToken;
197
208
  private teamId;
198
209
  constructor(config: CommunityConfig);
@@ -9,6 +9,14 @@ interface CommunityAttachment {
9
9
  interface CommunityConfig {
10
10
  /** Team slug (e.g., 'acme'). Identifies the community to query. */
11
11
  teamSlug: string;
12
+ /**
13
+ * Optional publishable key (`pk_test_*` or `pk_live_*`). When provided,
14
+ * the client switches data scope to match the key's mode (test vs live).
15
+ * Without a key, defaults to live — backwards-compatible with existing
16
+ * widget integrations. See `LibraryConfig.publishableKey` for the full
17
+ * test/live story.
18
+ */
19
+ publishableKey?: string;
12
20
  /** Override the base URL (defaults to https://www.cstar.help). */
13
21
  baseUrl?: string;
14
22
  }
@@ -193,6 +201,9 @@ interface CommunityPostDetail {
193
201
  declare class CommunityClient {
194
202
  private readonly baseUrl;
195
203
  private readonly teamSlug;
204
+ private readonly publishableKey;
205
+ /** Resolved mode based on the publishable-key prefix (`pk_test_*` => `test`). */
206
+ readonly mode: 'live' | 'test';
196
207
  private sessionToken;
197
208
  private teamId;
198
209
  constructor(config: CommunityConfig);
@@ -1,3 +1,216 @@
1
+ // src/errors.ts
2
+ var ErrorType = {
3
+ AUTHENTICATION_ERROR: "authentication_error",
4
+ PERMISSION_ERROR: "permission_error",
5
+ INVALID_REQUEST_ERROR: "invalid_request_error",
6
+ NOT_FOUND_ERROR: "not_found_error",
7
+ CONFLICT_ERROR: "conflict_error",
8
+ RATE_LIMIT_ERROR: "rate_limit_error",
9
+ FEATURE_NOT_CONFIGURED: "feature_not_configured",
10
+ API_ERROR: "api_error",
11
+ SERVER_ERROR: "server_error"
12
+ };
13
+ var DEFAULT_DOC_URL = "https://cstar.help/developers/api/errors";
14
+ function normalizeArgs(messageOrStatus, maybeBody, defaults) {
15
+ if (typeof messageOrStatus === "string") {
16
+ return {
17
+ statusCode: defaults?.code === "rate_limit_error" ? 429 : 0,
18
+ body: {
19
+ type: defaults?.type ?? ErrorType.API_ERROR,
20
+ code: defaults?.code ?? "sdk_error",
21
+ message: messageOrStatus,
22
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL,
23
+ ...defaults?.feature ? { feature: defaults.feature } : {},
24
+ ...defaults?.enable_url ? { enable_url: defaults.enable_url } : {}
25
+ }
26
+ };
27
+ }
28
+ return {
29
+ statusCode: messageOrStatus,
30
+ body: maybeBody ?? {
31
+ type: defaults?.type ?? ErrorType.API_ERROR,
32
+ code: defaults?.code ?? "unknown_error",
33
+ message: defaults?.message ?? `Request failed with status ${messageOrStatus}`,
34
+ doc_url: defaults?.doc_url ?? DEFAULT_DOC_URL
35
+ }
36
+ };
37
+ }
38
+ var CStarError = class extends Error {
39
+ constructor(messageOrStatus, body) {
40
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body);
41
+ super(resolved.message);
42
+ this.name = "CStarError";
43
+ this.type = resolved.type;
44
+ this.code = resolved.code;
45
+ this.param = resolved.param;
46
+ this.docUrl = resolved.doc_url;
47
+ this.requestId = resolved.request_id;
48
+ this.statusCode = statusCode;
49
+ }
50
+ };
51
+ var CStarAuthenticationError = class extends CStarError {
52
+ constructor(messageOrStatus, body) {
53
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
54
+ type: ErrorType.AUTHENTICATION_ERROR,
55
+ code: "authentication_failed",
56
+ doc_url: `${DEFAULT_DOC_URL}#authentication-error`
57
+ });
58
+ super(statusCode, resolved);
59
+ this.name = "CStarAuthenticationError";
60
+ }
61
+ };
62
+ var CStarRateLimitError = class extends CStarError {
63
+ constructor(messageOrStatus, body, retryAfter) {
64
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
65
+ type: ErrorType.RATE_LIMIT_ERROR,
66
+ code: "rate_limit_exceeded",
67
+ doc_url: `${DEFAULT_DOC_URL}#rate-limit-error`
68
+ });
69
+ super(statusCode === 0 ? 429 : statusCode, resolved);
70
+ this.name = "CStarRateLimitError";
71
+ this.retryAfter = retryAfter ?? 60;
72
+ }
73
+ };
74
+ var CStarNotFoundError = class extends CStarError {
75
+ constructor(messageOrStatus, body) {
76
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
77
+ type: ErrorType.NOT_FOUND_ERROR,
78
+ code: "resource_not_found",
79
+ doc_url: `${DEFAULT_DOC_URL}#not-found-error`
80
+ });
81
+ super(statusCode, resolved);
82
+ this.name = "CStarNotFoundError";
83
+ }
84
+ };
85
+ var CStarValidationError = class extends CStarError {
86
+ constructor(messageOrStatus, body) {
87
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
88
+ type: ErrorType.INVALID_REQUEST_ERROR,
89
+ code: "validation_error",
90
+ doc_url: `${DEFAULT_DOC_URL}#validation-error`
91
+ });
92
+ super(statusCode, resolved);
93
+ this.name = "CStarValidationError";
94
+ }
95
+ };
96
+ var CStarPermissionError = class extends CStarError {
97
+ constructor(messageOrStatus, body) {
98
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
99
+ type: ErrorType.PERMISSION_ERROR,
100
+ code: "permission_denied",
101
+ doc_url: `${DEFAULT_DOC_URL}#permission-error`
102
+ });
103
+ super(statusCode, resolved);
104
+ this.name = "CStarPermissionError";
105
+ }
106
+ };
107
+ var CStarConflictError = class extends CStarError {
108
+ constructor(messageOrStatus, body) {
109
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
110
+ type: ErrorType.CONFLICT_ERROR,
111
+ code: "conflict",
112
+ doc_url: `${DEFAULT_DOC_URL}#conflict-error`
113
+ });
114
+ super(statusCode, resolved);
115
+ this.name = "CStarConflictError";
116
+ }
117
+ };
118
+ var CStarServerError = class extends CStarError {
119
+ constructor(messageOrStatus, body) {
120
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
121
+ type: ErrorType.SERVER_ERROR,
122
+ code: "server_error",
123
+ doc_url: `${DEFAULT_DOC_URL}#server-error`
124
+ });
125
+ super(statusCode, resolved);
126
+ this.name = "CStarServerError";
127
+ }
128
+ };
129
+ var CStarFeatureDisabledError = class extends CStarError {
130
+ constructor(messageOrStatus, body) {
131
+ const { statusCode, body: resolved } = normalizeArgs(messageOrStatus, body, {
132
+ type: ErrorType.FEATURE_NOT_CONFIGURED,
133
+ code: "feature_not_configured",
134
+ doc_url: `${DEFAULT_DOC_URL}#feature-not-configured`
135
+ });
136
+ super(statusCode, resolved);
137
+ this.name = "CStarFeatureDisabledError";
138
+ this.feature = resolved.feature ?? "unknown";
139
+ this.enableUrl = resolved.enable_url ?? "";
140
+ }
141
+ };
142
+ function parsePublicError(status, body, fallbackMessage) {
143
+ const obj = body && typeof body === "object" ? body : null;
144
+ const errVal = obj?.error;
145
+ let message = fallbackMessage;
146
+ let structured = null;
147
+ if (typeof errVal === "string") {
148
+ message = errVal;
149
+ } else if (errVal && typeof errVal === "object") {
150
+ const e = errVal;
151
+ if (typeof e.message === "string") message = e.message;
152
+ structured = {
153
+ type: typeof e.type === "string" ? e.type : ErrorType.API_ERROR,
154
+ code: typeof e.code === "string" ? e.code : "api_error",
155
+ message,
156
+ param: typeof e.param === "string" ? e.param : void 0,
157
+ doc_url: typeof e.doc_url === "string" ? e.doc_url : DEFAULT_DOC_URL,
158
+ request_id: typeof e.request_id === "string" ? e.request_id : void 0,
159
+ feature: typeof e.feature === "string" ? e.feature : void 0,
160
+ enable_url: typeof e.enable_url === "string" ? e.enable_url : void 0
161
+ };
162
+ } else if (typeof obj?.message === "string") {
163
+ message = obj.message;
164
+ }
165
+ const errorBody = structured ?? {
166
+ type: ErrorType.API_ERROR,
167
+ code: status >= 500 ? "server_error" : "request_failed",
168
+ message,
169
+ doc_url: DEFAULT_DOC_URL
170
+ };
171
+ return parseError(status, { error: errorBody });
172
+ }
173
+ var STATUS_TO_CLASS = {
174
+ 400: CStarValidationError,
175
+ 401: CStarAuthenticationError,
176
+ 403: CStarPermissionError,
177
+ 404: CStarNotFoundError,
178
+ 409: CStarConflictError,
179
+ 422: CStarValidationError,
180
+ 429: CStarRateLimitError
181
+ };
182
+ var TYPE_TO_CLASS = {
183
+ [ErrorType.AUTHENTICATION_ERROR]: CStarAuthenticationError,
184
+ [ErrorType.PERMISSION_ERROR]: CStarPermissionError,
185
+ [ErrorType.RATE_LIMIT_ERROR]: CStarRateLimitError,
186
+ [ErrorType.NOT_FOUND_ERROR]: CStarNotFoundError,
187
+ [ErrorType.INVALID_REQUEST_ERROR]: CStarValidationError,
188
+ [ErrorType.CONFLICT_ERROR]: CStarConflictError,
189
+ [ErrorType.SERVER_ERROR]: CStarServerError,
190
+ [ErrorType.FEATURE_NOT_CONFIGURED]: CStarFeatureDisabledError
191
+ };
192
+ function parseError(statusCode, body, retryAfter) {
193
+ const errorBody = body?.error ?? {
194
+ type: ErrorType.API_ERROR,
195
+ code: "unknown_error",
196
+ message: `Request failed with status ${statusCode}`,
197
+ doc_url: DEFAULT_DOC_URL
198
+ };
199
+ if (statusCode === 429) {
200
+ return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
201
+ }
202
+ const StatusCls = STATUS_TO_CLASS[statusCode];
203
+ if (StatusCls) {
204
+ return new StatusCls(statusCode, errorBody);
205
+ }
206
+ const TypeCls = TYPE_TO_CLASS[errorBody.type];
207
+ if (TypeCls) {
208
+ return new TypeCls(statusCode, errorBody);
209
+ }
210
+ if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
211
+ return new CStarError(statusCode, errorBody);
212
+ }
213
+
1
214
  // src/community/index.ts
2
215
  var DEFAULT_BASE_URL = "https://www.cstar.help";
3
216
  var CommunityClient = class {
@@ -6,6 +219,13 @@ var CommunityClient = class {
6
219
  this.teamId = null;
7
220
  this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
8
221
  this.teamSlug = config.teamSlug;
222
+ this.publishableKey = config.publishableKey ?? null;
223
+ this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
224
+ if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
225
+ throw new Error(
226
+ "[cStar] CommunityClient: publishableKey must start with `pk_test_` or `pk_live_`. Secret keys (`sk_*`) cannot be used in browser/anonymous contexts."
227
+ );
228
+ }
9
229
  }
10
230
  /**
11
231
  * Set a session token from identity verification.
@@ -222,13 +442,13 @@ var CommunityClient = class {
222
442
  */
223
443
  resolveTeamId() {
224
444
  if (this.teamId) return this.teamId;
225
- throw new Error(
445
+ throw new CStarValidationError(
226
446
  "Team ID is required for authenticated operations. Pass it via setSessionToken(token, teamId) or use ChatClient.identify() which provides it automatically."
227
447
  );
228
448
  }
229
449
  async authenticatedFetch(path, options) {
230
450
  if (!this.sessionToken) {
231
- throw new Error(
451
+ throw new CStarValidationError(
232
452
  "Session token required. Call setSessionToken() or use ChatClient.identify() first."
233
453
  );
234
454
  }
@@ -251,9 +471,8 @@ var CommunityClient = class {
251
471
  body: options.body ? JSON.stringify(options.body) : void 0
252
472
  });
253
473
  if (!response.ok) {
254
- const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
255
- const message = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message ?? `Community API error: ${response.status}`;
256
- throw new Error(message);
474
+ const errorBody = await response.json().catch(() => null);
475
+ throw parsePublicError(response.status, errorBody, `Community API error: ${response.status}`);
257
476
  }
258
477
  return response.json();
259
478
  }
@@ -267,14 +486,17 @@ var CommunityClient = class {
267
486
  const qs = params.toString();
268
487
  if (qs) url += `?${qs}`;
269
488
  }
489
+ const headers = { "Content-Type": "application/json" };
490
+ if (this.publishableKey) {
491
+ headers["Authorization"] = `Bearer ${this.publishableKey}`;
492
+ }
270
493
  const response = await globalThis.fetch(url, {
271
494
  method: "GET",
272
- headers: { "Content-Type": "application/json" }
495
+ headers
273
496
  });
274
497
  if (!response.ok) {
275
- const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
276
- const message = typeof errorBody.error === "string" ? errorBody.error : errorBody.error?.message ?? `Community API error: ${response.status}`;
277
- throw new Error(message);
498
+ const errorBody = await response.json().catch(() => null);
499
+ throw parsePublicError(response.status, errorBody, `Community API error: ${response.status}`);
278
500
  }
279
501
  return response.json();
280
502
  }