@cstar.help/js 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.cjs +217 -3
- package/dist/auth/index.js +215 -3
- package/dist/chat/index.cjs +84 -29
- package/dist/chat/index.d.cts +72 -27
- package/dist/chat/index.d.ts +72 -27
- package/dist/chat/index.js +84 -29
- package/dist/community/index.cjs +233 -9
- package/dist/community/index.d.cts +11 -0
- package/dist/community/index.d.ts +11 -0
- package/dist/community/index.js +231 -9
- package/dist/index.cjs +309 -74
- package/dist/index.d.cts +3287 -0
- package/dist/index.d.ts +3287 -0
- package/dist/index.js +308 -74
- package/dist/library/index.cjs +254 -9
- package/dist/library/index.d.cts +26 -3
- package/dist/library/index.d.ts +26 -3
- package/dist/library/index.js +252 -9
- package/dist/quickhelp/index.cjs +220 -7
- package/dist/quickhelp/index.d.cts +37 -4
- package/dist/quickhelp/index.d.ts +37 -4
- package/dist/quickhelp/index.js +218 -7
- package/dist/webhook/index.cjs +0 -18
- package/dist/webhook/index.d.cts +440 -0
- package/dist/webhook/index.d.ts +440 -0
- package/dist/webhook/index.js +0 -23
- package/dist/webhook/node.cjs +57 -0
- package/dist/webhook/node.d.cts +440 -0
- package/dist/webhook/node.d.ts +440 -0
- package/dist/webhook/node.js +30 -0
- package/package.json +14 -2
package/dist/chat/index.js
CHANGED
|
@@ -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(
|
|
253
|
-
|
|
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(
|
|
262
|
-
super(
|
|
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(
|
|
268
|
-
|
|
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(
|
|
275
|
-
super(
|
|
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,
|
|
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
|
}
|
|
@@ -294,6 +302,7 @@ var ChatClient = class {
|
|
|
294
302
|
this.customerId = null;
|
|
295
303
|
this.authMode = null;
|
|
296
304
|
this.pollingTimers = /* @__PURE__ */ new Map();
|
|
305
|
+
this.stateListeners = /* @__PURE__ */ new Set();
|
|
297
306
|
this.realtimeManager = null;
|
|
298
307
|
this.presenceManager = null;
|
|
299
308
|
this.realtimeReady = false;
|
|
@@ -333,6 +342,8 @@ var ChatClient = class {
|
|
|
333
342
|
this.sessionToken = response.data.sessionToken;
|
|
334
343
|
this.teamId = response.data.teamId;
|
|
335
344
|
this.customerId = response.data.customer.id;
|
|
345
|
+
this.authMode = "hmac";
|
|
346
|
+
this.notifyStateChanged();
|
|
336
347
|
if (this.realtimeEnabled) {
|
|
337
348
|
this.realtimeInitPromise = this.initRealtime(
|
|
338
349
|
response.data.customer.id,
|
|
@@ -392,11 +403,35 @@ var ChatClient = class {
|
|
|
392
403
|
this.teamId = params.teamId;
|
|
393
404
|
}
|
|
394
405
|
this.authMode = "session";
|
|
406
|
+
this.notifyStateChanged();
|
|
395
407
|
if (this.realtimeEnabled) {
|
|
396
408
|
const customerName = params.customerName ?? "";
|
|
397
409
|
this.realtimeInitPromise = this.initRealtimeFromSession(customerName);
|
|
398
410
|
}
|
|
399
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Subscribe to client state changes (auth mode flips, identify, disconnect).
|
|
414
|
+
* Fires after the mutation, so listeners reading `getAuthMode()` /
|
|
415
|
+
* `getCustomerId()` see the new value.
|
|
416
|
+
*
|
|
417
|
+
* Returns an unsubscribe function. Closes F-041 — used by `useChat` (React)
|
|
418
|
+
* and `ChatState` (Svelte) to re-render when consumers mutate the client
|
|
419
|
+
* outside of the framework wrappers.
|
|
420
|
+
*/
|
|
421
|
+
subscribe(listener) {
|
|
422
|
+
this.stateListeners.add(listener);
|
|
423
|
+
return () => {
|
|
424
|
+
this.stateListeners.delete(listener);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
notifyStateChanged() {
|
|
428
|
+
for (const listener of this.stateListeners) {
|
|
429
|
+
try {
|
|
430
|
+
listener();
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
400
435
|
/**
|
|
401
436
|
* Returns the auth mode the client is currently using, or `null` before
|
|
402
437
|
* any authenticated call has resolved one. Useful for branching UI
|
|
@@ -459,6 +494,7 @@ var ChatClient = class {
|
|
|
459
494
|
this.teamId = null;
|
|
460
495
|
this.customerId = null;
|
|
461
496
|
this.authMode = null;
|
|
497
|
+
this.notifyStateChanged();
|
|
462
498
|
}
|
|
463
499
|
/**
|
|
464
500
|
* @internal Make an authenticated request to the widget API.
|
|
@@ -496,7 +532,9 @@ var ChatClient = class {
|
|
|
496
532
|
const responseHeaders = response.headers && typeof response.headers.get === "function" ? response.headers : null;
|
|
497
533
|
const headerMode = responseHeaders?.get("X-Cstar-Auth-Mode") ?? null;
|
|
498
534
|
if (headerMode === "hmac" || headerMode === "session") {
|
|
535
|
+
const previous = this.authMode;
|
|
499
536
|
this.authMode = headerMode;
|
|
537
|
+
if (previous !== headerMode) this.notifyStateChanged();
|
|
500
538
|
}
|
|
501
539
|
const requestId = responseHeaders?.get("X-Request-Id") ?? null;
|
|
502
540
|
if (!response.ok) {
|
|
@@ -530,7 +568,8 @@ var ChatClient = class {
|
|
|
530
568
|
requestId
|
|
531
569
|
});
|
|
532
570
|
}
|
|
533
|
-
throw new ChatClientError(
|
|
571
|
+
throw new ChatClientError({
|
|
572
|
+
message,
|
|
534
573
|
code,
|
|
535
574
|
statusCode: response.status,
|
|
536
575
|
requestId
|
|
@@ -668,7 +707,7 @@ var TicketsApi = class {
|
|
|
668
707
|
/** List tickets for the identified customer. */
|
|
669
708
|
async list(params) {
|
|
670
709
|
const teamId = this.chat.getTeamId();
|
|
671
|
-
if (!teamId) throw new
|
|
710
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
672
711
|
const response = await this.chat.fetch("/api/widget/conversations", {
|
|
673
712
|
method: "GET",
|
|
674
713
|
query: {
|
|
@@ -677,16 +716,22 @@ var TicketsApi = class {
|
|
|
677
716
|
cursor: params?.cursor
|
|
678
717
|
}
|
|
679
718
|
});
|
|
719
|
+
const data = response.data.tickets ?? response.data.conversations ?? [];
|
|
720
|
+
const cursor = response.data.cursor;
|
|
721
|
+
const hasMore = response.data.hasMore;
|
|
680
722
|
return {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
hasMore
|
|
723
|
+
data,
|
|
724
|
+
pagination: { cursor, hasMore },
|
|
725
|
+
hasMore,
|
|
726
|
+
// Deprecated aliases — same references, no extra allocation.
|
|
727
|
+
tickets: data,
|
|
728
|
+
cursor
|
|
684
729
|
};
|
|
685
730
|
}
|
|
686
731
|
/** Create a new ticket. */
|
|
687
732
|
async create(params) {
|
|
688
733
|
const teamId = this.chat.getTeamId();
|
|
689
|
-
if (!teamId) throw new
|
|
734
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
690
735
|
const response = await this.chat.fetch("/api/widget/tickets", {
|
|
691
736
|
method: "POST",
|
|
692
737
|
body: {
|
|
@@ -695,15 +740,25 @@ var TicketsApi = class {
|
|
|
695
740
|
message: params.message
|
|
696
741
|
}
|
|
697
742
|
});
|
|
743
|
+
const messages = (response.data.messages ?? []).map((m) => ({
|
|
744
|
+
id: m.id,
|
|
745
|
+
content: m.content,
|
|
746
|
+
sender: m.sender,
|
|
747
|
+
sender_name: m.sender_name,
|
|
748
|
+
created_at: m.created_at,
|
|
749
|
+
read_at: m.read_at,
|
|
750
|
+
attachments: m.attachments ?? []
|
|
751
|
+
}));
|
|
698
752
|
return {
|
|
699
753
|
id: response.data.id,
|
|
700
|
-
ticketNumber:
|
|
754
|
+
ticketNumber: null,
|
|
701
755
|
subject: response.data.subject,
|
|
702
756
|
status: response.data.status,
|
|
703
|
-
messageCount:
|
|
757
|
+
messageCount: messages.length,
|
|
704
758
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
705
759
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
706
|
-
closedAt: null
|
|
760
|
+
closedAt: null,
|
|
761
|
+
messages
|
|
707
762
|
};
|
|
708
763
|
}
|
|
709
764
|
};
|
|
@@ -714,7 +769,7 @@ var ChatCommunityApi = class {
|
|
|
714
769
|
/** Create a new community post as the identified customer. */
|
|
715
770
|
async createPost(params) {
|
|
716
771
|
const teamId = this.chat.getTeamId();
|
|
717
|
-
if (!teamId) throw new
|
|
772
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
718
773
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
719
774
|
method: "POST",
|
|
720
775
|
body: {
|
|
@@ -731,7 +786,7 @@ var ChatCommunityApi = class {
|
|
|
731
786
|
/** Update a post. Only the original author can edit. */
|
|
732
787
|
async updatePost(postId, params) {
|
|
733
788
|
const teamId = this.chat.getTeamId();
|
|
734
|
-
if (!teamId) throw new
|
|
789
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
735
790
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
736
791
|
method: "POST",
|
|
737
792
|
body: {
|
|
@@ -748,7 +803,7 @@ var ChatCommunityApi = class {
|
|
|
748
803
|
/** Delete a post (soft delete). Only the original author can delete. */
|
|
749
804
|
async deletePost(postId) {
|
|
750
805
|
const teamId = this.chat.getTeamId();
|
|
751
|
-
if (!teamId) throw new
|
|
806
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
752
807
|
await this.chat.fetch("/api/widget/community", {
|
|
753
808
|
method: "POST",
|
|
754
809
|
body: { teamId, action: "deletePost", postId }
|
|
@@ -757,7 +812,7 @@ var ChatCommunityApi = class {
|
|
|
757
812
|
/** Update a comment. Only the original author can edit. */
|
|
758
813
|
async updateComment(postId, commentId, params) {
|
|
759
814
|
const teamId = this.chat.getTeamId();
|
|
760
|
-
if (!teamId) throw new
|
|
815
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
761
816
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
762
817
|
method: "POST",
|
|
763
818
|
body: {
|
|
@@ -773,7 +828,7 @@ var ChatCommunityApi = class {
|
|
|
773
828
|
/** Delete a comment (soft delete). Only the original author can delete. */
|
|
774
829
|
async deleteComment(postId, commentId) {
|
|
775
830
|
const teamId = this.chat.getTeamId();
|
|
776
|
-
if (!teamId) throw new
|
|
831
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
777
832
|
await this.chat.fetch("/api/widget/community", {
|
|
778
833
|
method: "POST",
|
|
779
834
|
body: { teamId, action: "deleteComment", postId, commentId }
|
|
@@ -782,7 +837,7 @@ var ChatCommunityApi = class {
|
|
|
782
837
|
/** Toggle vote on a post. Returns new vote state and count. */
|
|
783
838
|
async vote(postId) {
|
|
784
839
|
const teamId = this.chat.getTeamId();
|
|
785
|
-
if (!teamId) throw new
|
|
840
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
786
841
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
787
842
|
method: "POST",
|
|
788
843
|
body: { teamId, action: "vote", postId }
|
|
@@ -792,7 +847,7 @@ var ChatCommunityApi = class {
|
|
|
792
847
|
/** Add a comment to a post. Supports 1-level nesting via parentCommentId. */
|
|
793
848
|
async comment(postId, params) {
|
|
794
849
|
const teamId = this.chat.getTeamId();
|
|
795
|
-
if (!teamId) throw new
|
|
850
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
796
851
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
797
852
|
method: "POST",
|
|
798
853
|
body: {
|
|
@@ -808,7 +863,7 @@ var ChatCommunityApi = class {
|
|
|
808
863
|
/** Toggle follow on a post. */
|
|
809
864
|
async follow(postId) {
|
|
810
865
|
const teamId = this.chat.getTeamId();
|
|
811
|
-
if (!teamId) throw new
|
|
866
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
812
867
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
813
868
|
method: "POST",
|
|
814
869
|
body: { teamId, action: "follow", postId }
|
|
@@ -818,7 +873,7 @@ var ChatCommunityApi = class {
|
|
|
818
873
|
/** Check if the current user has voted on a post. */
|
|
819
874
|
async getVoteStatus(postId) {
|
|
820
875
|
const teamId = this.chat.getTeamId();
|
|
821
|
-
if (!teamId) throw new
|
|
876
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
822
877
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
823
878
|
method: "GET",
|
|
824
879
|
query: { teamId, postId }
|
|
@@ -829,7 +884,7 @@ var ChatCommunityApi = class {
|
|
|
829
884
|
async bulkVoteStatus(postIds) {
|
|
830
885
|
if (postIds.length === 0) return {};
|
|
831
886
|
const teamId = this.chat.getTeamId();
|
|
832
|
-
if (!teamId) throw new
|
|
887
|
+
if (!teamId) throw new ChatClientAuthError("Not identified. Call chat.identify() first.");
|
|
833
888
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
834
889
|
method: "GET",
|
|
835
890
|
query: { teamId, postIds: postIds.join(",") }
|
package/dist/community/index.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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(() =>
|
|
279
|
-
|
|
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
|
|
521
|
+
headers
|
|
297
522
|
});
|
|
298
523
|
if (!response.ok) {
|
|
299
|
-
const errorBody = await response.json().catch(() =>
|
|
300
|
-
|
|
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);
|