@cstar.help/js 0.13.0 → 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/chat/index.cjs +38 -3
- package/dist/chat/index.d.cts +44 -3
- package/dist/chat/index.d.ts +44 -3
- package/dist/chat/index.js +38 -3
- package/dist/index.cjs +42 -2
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +42 -2
- package/dist/quickhelp/index.cjs +45 -6
- package/dist/quickhelp/index.d.cts +26 -4
- package/dist/quickhelp/index.d.ts +26 -4
- package/dist/quickhelp/index.js +45 -6
- package/package.json +1 -1
package/dist/chat/index.cjs
CHANGED
|
@@ -343,6 +343,7 @@ var ChatClient = class {
|
|
|
343
343
|
this.customerId = null;
|
|
344
344
|
this.authMode = null;
|
|
345
345
|
this.pollingTimers = /* @__PURE__ */ new Map();
|
|
346
|
+
this.stateListeners = /* @__PURE__ */ new Set();
|
|
346
347
|
this.realtimeManager = null;
|
|
347
348
|
this.presenceManager = null;
|
|
348
349
|
this.realtimeReady = false;
|
|
@@ -383,6 +384,7 @@ var ChatClient = class {
|
|
|
383
384
|
this.teamId = response.data.teamId;
|
|
384
385
|
this.customerId = response.data.customer.id;
|
|
385
386
|
this.authMode = "hmac";
|
|
387
|
+
this.notifyStateChanged();
|
|
386
388
|
if (this.realtimeEnabled) {
|
|
387
389
|
this.realtimeInitPromise = this.initRealtime(
|
|
388
390
|
response.data.customer.id,
|
|
@@ -442,11 +444,35 @@ var ChatClient = class {
|
|
|
442
444
|
this.teamId = params.teamId;
|
|
443
445
|
}
|
|
444
446
|
this.authMode = "session";
|
|
447
|
+
this.notifyStateChanged();
|
|
445
448
|
if (this.realtimeEnabled) {
|
|
446
449
|
const customerName = params.customerName ?? "";
|
|
447
450
|
this.realtimeInitPromise = this.initRealtimeFromSession(customerName);
|
|
448
451
|
}
|
|
449
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Subscribe to client state changes (auth mode flips, identify, disconnect).
|
|
455
|
+
* Fires after the mutation, so listeners reading `getAuthMode()` /
|
|
456
|
+
* `getCustomerId()` see the new value.
|
|
457
|
+
*
|
|
458
|
+
* Returns an unsubscribe function. Closes F-041 — used by `useChat` (React)
|
|
459
|
+
* and `ChatState` (Svelte) to re-render when consumers mutate the client
|
|
460
|
+
* outside of the framework wrappers.
|
|
461
|
+
*/
|
|
462
|
+
subscribe(listener) {
|
|
463
|
+
this.stateListeners.add(listener);
|
|
464
|
+
return () => {
|
|
465
|
+
this.stateListeners.delete(listener);
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
notifyStateChanged() {
|
|
469
|
+
for (const listener of this.stateListeners) {
|
|
470
|
+
try {
|
|
471
|
+
listener();
|
|
472
|
+
} catch {
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
450
476
|
/**
|
|
451
477
|
* Returns the auth mode the client is currently using, or `null` before
|
|
452
478
|
* any authenticated call has resolved one. Useful for branching UI
|
|
@@ -509,6 +535,7 @@ var ChatClient = class {
|
|
|
509
535
|
this.teamId = null;
|
|
510
536
|
this.customerId = null;
|
|
511
537
|
this.authMode = null;
|
|
538
|
+
this.notifyStateChanged();
|
|
512
539
|
}
|
|
513
540
|
/**
|
|
514
541
|
* @internal Make an authenticated request to the widget API.
|
|
@@ -546,7 +573,9 @@ var ChatClient = class {
|
|
|
546
573
|
const responseHeaders = response.headers && typeof response.headers.get === "function" ? response.headers : null;
|
|
547
574
|
const headerMode = responseHeaders?.get("X-Cstar-Auth-Mode") ?? null;
|
|
548
575
|
if (headerMode === "hmac" || headerMode === "session") {
|
|
576
|
+
const previous = this.authMode;
|
|
549
577
|
this.authMode = headerMode;
|
|
578
|
+
if (previous !== headerMode) this.notifyStateChanged();
|
|
550
579
|
}
|
|
551
580
|
const requestId = responseHeaders?.get("X-Request-Id") ?? null;
|
|
552
581
|
if (!response.ok) {
|
|
@@ -728,10 +757,16 @@ var TicketsApi = class {
|
|
|
728
757
|
cursor: params?.cursor
|
|
729
758
|
}
|
|
730
759
|
});
|
|
760
|
+
const data = response.data.tickets ?? response.data.conversations ?? [];
|
|
761
|
+
const cursor = response.data.cursor;
|
|
762
|
+
const hasMore = response.data.hasMore;
|
|
731
763
|
return {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
hasMore
|
|
764
|
+
data,
|
|
765
|
+
pagination: { cursor, hasMore },
|
|
766
|
+
hasMore,
|
|
767
|
+
// Deprecated aliases — same references, no extra allocation.
|
|
768
|
+
tickets: data,
|
|
769
|
+
cursor
|
|
735
770
|
};
|
|
736
771
|
}
|
|
737
772
|
/** Create a new ticket. */
|
package/dist/chat/index.d.cts
CHANGED
|
@@ -107,11 +107,34 @@ interface Ticket {
|
|
|
107
107
|
*/
|
|
108
108
|
messages?: Message[];
|
|
109
109
|
}
|
|
110
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Paginated ticket list response.
|
|
112
|
+
*
|
|
113
|
+
* As of `@cstar.help/js@0.14.0` the canonical fields match the admin SDK and
|
|
114
|
+
* library SDK envelopes (`{ data, pagination, hasMore }`). The legacy
|
|
115
|
+
* `tickets` and `cursor` fields are kept as aliases — they reference the
|
|
116
|
+
* same data — and will be removed in the next major. Closes F-037.
|
|
117
|
+
*/
|
|
111
118
|
interface TicketListResponse {
|
|
119
|
+
/** Tickets on this page (canonical). */
|
|
120
|
+
data: Ticket[];
|
|
121
|
+
/** Pagination cursor + a flag for whether more pages exist (canonical). */
|
|
122
|
+
pagination: {
|
|
123
|
+
cursor: string | null;
|
|
124
|
+
hasMore: boolean;
|
|
125
|
+
};
|
|
126
|
+
/** Convenience copy of `pagination.hasMore`. */
|
|
127
|
+
hasMore: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* @deprecated since 0.14.0 — use `data`. Same array reference for now.
|
|
130
|
+
* Will be removed in the next major.
|
|
131
|
+
*/
|
|
112
132
|
tickets: Ticket[];
|
|
133
|
+
/**
|
|
134
|
+
* @deprecated since 0.14.0 — use `pagination.cursor`. Same value.
|
|
135
|
+
* Will be removed in the next major.
|
|
136
|
+
*/
|
|
113
137
|
cursor: string | null;
|
|
114
|
-
hasMore: boolean;
|
|
115
138
|
}
|
|
116
139
|
/** A message on a ticket. */
|
|
117
140
|
interface Message {
|
|
@@ -345,6 +368,12 @@ declare class ChatClientNetworkError extends ChatClientError {
|
|
|
345
368
|
* `ChatClientServerError`, `ChatClientNetworkError`. Each carries
|
|
346
369
|
* `requestId` for correlating with server-side logs.
|
|
347
370
|
*/
|
|
371
|
+
/**
|
|
372
|
+
* Listener for client-state changes (auth-mode flips, identity assignment,
|
|
373
|
+
* disconnects). Fires once after the underlying state mutates so a UI layer
|
|
374
|
+
* can re-read `getAuthMode()` / `getCustomerId()` and re-render. Closes F-041.
|
|
375
|
+
*/
|
|
376
|
+
type ChatStateListener = () => void;
|
|
348
377
|
declare class ChatClient {
|
|
349
378
|
private readonly baseUrl;
|
|
350
379
|
private readonly teamSlug;
|
|
@@ -355,6 +384,7 @@ declare class ChatClient {
|
|
|
355
384
|
private customerId;
|
|
356
385
|
private authMode;
|
|
357
386
|
private pollingTimers;
|
|
387
|
+
private stateListeners;
|
|
358
388
|
private realtimeManager;
|
|
359
389
|
private presenceManager;
|
|
360
390
|
private realtimeReady;
|
|
@@ -397,6 +427,17 @@ declare class ChatClient {
|
|
|
397
427
|
* every request — there is no client-side validation here.
|
|
398
428
|
*/
|
|
399
429
|
loginWithSession(params: LoginWithSessionParams): Promise<void>;
|
|
430
|
+
/**
|
|
431
|
+
* Subscribe to client state changes (auth mode flips, identify, disconnect).
|
|
432
|
+
* Fires after the mutation, so listeners reading `getAuthMode()` /
|
|
433
|
+
* `getCustomerId()` see the new value.
|
|
434
|
+
*
|
|
435
|
+
* Returns an unsubscribe function. Closes F-041 — used by `useChat` (React)
|
|
436
|
+
* and `ChatState` (Svelte) to re-render when consumers mutate the client
|
|
437
|
+
* outside of the framework wrappers.
|
|
438
|
+
*/
|
|
439
|
+
subscribe(listener: ChatStateListener): () => void;
|
|
440
|
+
private notifyStateChanged;
|
|
400
441
|
/**
|
|
401
442
|
* Returns the auth mode the client is currently using, or `null` before
|
|
402
443
|
* any authenticated call has resolved one. Useful for branching UI
|
|
@@ -509,4 +550,4 @@ declare class MessagesApi {
|
|
|
509
550
|
send(ticketId: string, content: string): Promise<Message>;
|
|
510
551
|
}
|
|
511
552
|
|
|
512
|
-
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 };
|
|
553
|
+
export { type ChatAuthMode, ChatClient, ChatClientAuthError, ChatClientError, ChatClientNetworkError, ChatClientRateLimitError, ChatClientServerError, type ChatConfig, type ChatStateListener, 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 };
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -107,11 +107,34 @@ interface Ticket {
|
|
|
107
107
|
*/
|
|
108
108
|
messages?: Message[];
|
|
109
109
|
}
|
|
110
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Paginated ticket list response.
|
|
112
|
+
*
|
|
113
|
+
* As of `@cstar.help/js@0.14.0` the canonical fields match the admin SDK and
|
|
114
|
+
* library SDK envelopes (`{ data, pagination, hasMore }`). The legacy
|
|
115
|
+
* `tickets` and `cursor` fields are kept as aliases — they reference the
|
|
116
|
+
* same data — and will be removed in the next major. Closes F-037.
|
|
117
|
+
*/
|
|
111
118
|
interface TicketListResponse {
|
|
119
|
+
/** Tickets on this page (canonical). */
|
|
120
|
+
data: Ticket[];
|
|
121
|
+
/** Pagination cursor + a flag for whether more pages exist (canonical). */
|
|
122
|
+
pagination: {
|
|
123
|
+
cursor: string | null;
|
|
124
|
+
hasMore: boolean;
|
|
125
|
+
};
|
|
126
|
+
/** Convenience copy of `pagination.hasMore`. */
|
|
127
|
+
hasMore: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* @deprecated since 0.14.0 — use `data`. Same array reference for now.
|
|
130
|
+
* Will be removed in the next major.
|
|
131
|
+
*/
|
|
112
132
|
tickets: Ticket[];
|
|
133
|
+
/**
|
|
134
|
+
* @deprecated since 0.14.0 — use `pagination.cursor`. Same value.
|
|
135
|
+
* Will be removed in the next major.
|
|
136
|
+
*/
|
|
113
137
|
cursor: string | null;
|
|
114
|
-
hasMore: boolean;
|
|
115
138
|
}
|
|
116
139
|
/** A message on a ticket. */
|
|
117
140
|
interface Message {
|
|
@@ -345,6 +368,12 @@ declare class ChatClientNetworkError extends ChatClientError {
|
|
|
345
368
|
* `ChatClientServerError`, `ChatClientNetworkError`. Each carries
|
|
346
369
|
* `requestId` for correlating with server-side logs.
|
|
347
370
|
*/
|
|
371
|
+
/**
|
|
372
|
+
* Listener for client-state changes (auth-mode flips, identity assignment,
|
|
373
|
+
* disconnects). Fires once after the underlying state mutates so a UI layer
|
|
374
|
+
* can re-read `getAuthMode()` / `getCustomerId()` and re-render. Closes F-041.
|
|
375
|
+
*/
|
|
376
|
+
type ChatStateListener = () => void;
|
|
348
377
|
declare class ChatClient {
|
|
349
378
|
private readonly baseUrl;
|
|
350
379
|
private readonly teamSlug;
|
|
@@ -355,6 +384,7 @@ declare class ChatClient {
|
|
|
355
384
|
private customerId;
|
|
356
385
|
private authMode;
|
|
357
386
|
private pollingTimers;
|
|
387
|
+
private stateListeners;
|
|
358
388
|
private realtimeManager;
|
|
359
389
|
private presenceManager;
|
|
360
390
|
private realtimeReady;
|
|
@@ -397,6 +427,17 @@ declare class ChatClient {
|
|
|
397
427
|
* every request — there is no client-side validation here.
|
|
398
428
|
*/
|
|
399
429
|
loginWithSession(params: LoginWithSessionParams): Promise<void>;
|
|
430
|
+
/**
|
|
431
|
+
* Subscribe to client state changes (auth mode flips, identify, disconnect).
|
|
432
|
+
* Fires after the mutation, so listeners reading `getAuthMode()` /
|
|
433
|
+
* `getCustomerId()` see the new value.
|
|
434
|
+
*
|
|
435
|
+
* Returns an unsubscribe function. Closes F-041 — used by `useChat` (React)
|
|
436
|
+
* and `ChatState` (Svelte) to re-render when consumers mutate the client
|
|
437
|
+
* outside of the framework wrappers.
|
|
438
|
+
*/
|
|
439
|
+
subscribe(listener: ChatStateListener): () => void;
|
|
440
|
+
private notifyStateChanged;
|
|
400
441
|
/**
|
|
401
442
|
* Returns the auth mode the client is currently using, or `null` before
|
|
402
443
|
* any authenticated call has resolved one. Useful for branching UI
|
|
@@ -509,4 +550,4 @@ declare class MessagesApi {
|
|
|
509
550
|
send(ticketId: string, content: string): Promise<Message>;
|
|
510
551
|
}
|
|
511
552
|
|
|
512
|
-
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 };
|
|
553
|
+
export { type ChatAuthMode, ChatClient, ChatClientAuthError, ChatClientError, ChatClientNetworkError, ChatClientRateLimitError, ChatClientServerError, type ChatConfig, type ChatStateListener, 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 };
|
package/dist/chat/index.js
CHANGED
|
@@ -302,6 +302,7 @@ var ChatClient = class {
|
|
|
302
302
|
this.customerId = null;
|
|
303
303
|
this.authMode = null;
|
|
304
304
|
this.pollingTimers = /* @__PURE__ */ new Map();
|
|
305
|
+
this.stateListeners = /* @__PURE__ */ new Set();
|
|
305
306
|
this.realtimeManager = null;
|
|
306
307
|
this.presenceManager = null;
|
|
307
308
|
this.realtimeReady = false;
|
|
@@ -342,6 +343,7 @@ var ChatClient = class {
|
|
|
342
343
|
this.teamId = response.data.teamId;
|
|
343
344
|
this.customerId = response.data.customer.id;
|
|
344
345
|
this.authMode = "hmac";
|
|
346
|
+
this.notifyStateChanged();
|
|
345
347
|
if (this.realtimeEnabled) {
|
|
346
348
|
this.realtimeInitPromise = this.initRealtime(
|
|
347
349
|
response.data.customer.id,
|
|
@@ -401,11 +403,35 @@ var ChatClient = class {
|
|
|
401
403
|
this.teamId = params.teamId;
|
|
402
404
|
}
|
|
403
405
|
this.authMode = "session";
|
|
406
|
+
this.notifyStateChanged();
|
|
404
407
|
if (this.realtimeEnabled) {
|
|
405
408
|
const customerName = params.customerName ?? "";
|
|
406
409
|
this.realtimeInitPromise = this.initRealtimeFromSession(customerName);
|
|
407
410
|
}
|
|
408
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
|
+
}
|
|
409
435
|
/**
|
|
410
436
|
* Returns the auth mode the client is currently using, or `null` before
|
|
411
437
|
* any authenticated call has resolved one. Useful for branching UI
|
|
@@ -468,6 +494,7 @@ var ChatClient = class {
|
|
|
468
494
|
this.teamId = null;
|
|
469
495
|
this.customerId = null;
|
|
470
496
|
this.authMode = null;
|
|
497
|
+
this.notifyStateChanged();
|
|
471
498
|
}
|
|
472
499
|
/**
|
|
473
500
|
* @internal Make an authenticated request to the widget API.
|
|
@@ -505,7 +532,9 @@ var ChatClient = class {
|
|
|
505
532
|
const responseHeaders = response.headers && typeof response.headers.get === "function" ? response.headers : null;
|
|
506
533
|
const headerMode = responseHeaders?.get("X-Cstar-Auth-Mode") ?? null;
|
|
507
534
|
if (headerMode === "hmac" || headerMode === "session") {
|
|
535
|
+
const previous = this.authMode;
|
|
508
536
|
this.authMode = headerMode;
|
|
537
|
+
if (previous !== headerMode) this.notifyStateChanged();
|
|
509
538
|
}
|
|
510
539
|
const requestId = responseHeaders?.get("X-Request-Id") ?? null;
|
|
511
540
|
if (!response.ok) {
|
|
@@ -687,10 +716,16 @@ var TicketsApi = class {
|
|
|
687
716
|
cursor: params?.cursor
|
|
688
717
|
}
|
|
689
718
|
});
|
|
719
|
+
const data = response.data.tickets ?? response.data.conversations ?? [];
|
|
720
|
+
const cursor = response.data.cursor;
|
|
721
|
+
const hasMore = response.data.hasMore;
|
|
690
722
|
return {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
hasMore
|
|
723
|
+
data,
|
|
724
|
+
pagination: { cursor, hasMore },
|
|
725
|
+
hasMore,
|
|
726
|
+
// Deprecated aliases — same references, no extra allocation.
|
|
727
|
+
tickets: data,
|
|
728
|
+
cursor
|
|
694
729
|
};
|
|
695
730
|
}
|
|
696
731
|
/** Create a new ticket. */
|
package/dist/index.cjs
CHANGED
|
@@ -178,6 +178,18 @@ var CStarFeatureDisabledError = class extends CStarError {
|
|
|
178
178
|
this.enableUrl = resolved.enable_url ?? "";
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
|
+
var CStarRealtimeUnavailableError = class extends CStarError {
|
|
182
|
+
constructor(message) {
|
|
183
|
+
const resolved = message ?? "EventSource is not available in this environment. Real-time subscriptions only work in the browser. Move the call to a client-only path (`useEffect` / `onMount`), or pass an `eventSource` polyfill via the realtime options. See https://cstar.help/developers/sdk/realtime#ssr";
|
|
184
|
+
super(0, {
|
|
185
|
+
type: ErrorType.API_ERROR,
|
|
186
|
+
code: "realtime_unavailable",
|
|
187
|
+
message: resolved,
|
|
188
|
+
doc_url: "https://cstar.help/developers/sdk/realtime#ssr"
|
|
189
|
+
});
|
|
190
|
+
this.name = "CStarRealtimeUnavailableError";
|
|
191
|
+
}
|
|
192
|
+
};
|
|
181
193
|
var STATUS_TO_CLASS = {
|
|
182
194
|
400: CStarValidationError,
|
|
183
195
|
401: CStarAuthenticationError,
|
|
@@ -220,7 +232,7 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
220
232
|
}
|
|
221
233
|
|
|
222
234
|
// src/client.ts
|
|
223
|
-
var SDK_VERSION = "0.
|
|
235
|
+
var SDK_VERSION = "0.14.0";
|
|
224
236
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
225
237
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
226
238
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -465,6 +477,18 @@ var CStarRealtime = class {
|
|
|
465
477
|
this.autoReconnect = options?.autoReconnect ?? true;
|
|
466
478
|
this.maxRetries = options?.maxRetries ?? 10;
|
|
467
479
|
this.types = options?.types;
|
|
480
|
+
this.eventSourceCtor = options?.eventSource;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Resolve the EventSource constructor to use for this connection.
|
|
484
|
+
* Returns the explicit polyfill when one was injected, otherwise the
|
|
485
|
+
* global. Returns `undefined` when neither is available — callers must
|
|
486
|
+
* convert to `CStarRealtimeUnavailableError` before touching it.
|
|
487
|
+
*/
|
|
488
|
+
resolveEventSource() {
|
|
489
|
+
if (this.eventSourceCtor) return this.eventSourceCtor;
|
|
490
|
+
const fromGlobal = globalThis.EventSource;
|
|
491
|
+
return typeof fromGlobal === "function" ? fromGlobal : void 0;
|
|
468
492
|
}
|
|
469
493
|
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
470
494
|
on(eventType, handler) {
|
|
@@ -519,6 +543,9 @@ var CStarRealtime = class {
|
|
|
519
543
|
/** Start the SSE connection. Called automatically on first handler registration. */
|
|
520
544
|
connect() {
|
|
521
545
|
if (this.eventSource || this.destroyed || this.connecting) return;
|
|
546
|
+
if (!this.resolveEventSource()) {
|
|
547
|
+
throw new CStarRealtimeUnavailableError();
|
|
548
|
+
}
|
|
522
549
|
this.connecting = true;
|
|
523
550
|
const headers = this.httpClient.getAuthHeaders();
|
|
524
551
|
const rawKey = headers.Authorization?.replace("Bearer ", "") ?? "";
|
|
@@ -557,7 +584,12 @@ var CStarRealtime = class {
|
|
|
557
584
|
}
|
|
558
585
|
const separator = qs ? "&" : "?";
|
|
559
586
|
const authenticatedUrl = authToken ? `${url}${separator}token=${authToken}` : url;
|
|
560
|
-
|
|
587
|
+
const Ctor = this.resolveEventSource();
|
|
588
|
+
if (!Ctor) {
|
|
589
|
+
this.connecting = false;
|
|
590
|
+
throw new CStarRealtimeUnavailableError();
|
|
591
|
+
}
|
|
592
|
+
this.eventSource = new Ctor(authenticatedUrl);
|
|
561
593
|
this.eventSource.onopen = () => {
|
|
562
594
|
this.connecting = false;
|
|
563
595
|
this.retryCount = 0;
|
|
@@ -976,6 +1008,14 @@ var BaseResource = class {
|
|
|
976
1008
|
async del(id) {
|
|
977
1009
|
return this.client.request("DELETE", `${this.basePath}/${id}`);
|
|
978
1010
|
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Delete a resource by ID. Alias for `del()` — kept because IDE
|
|
1013
|
+
* auto-complete and documentation both surface `delete` first. Closes
|
|
1014
|
+
* NEW-v2-06.
|
|
1015
|
+
*/
|
|
1016
|
+
async delete(id) {
|
|
1017
|
+
return this.del(id);
|
|
1018
|
+
}
|
|
979
1019
|
/**
|
|
980
1020
|
* Auto-paginating async iterator that yields every item across all pages.
|
|
981
1021
|
*
|
package/dist/index.d.cts
CHANGED
|
@@ -134,6 +134,13 @@ declare class HttpClient {
|
|
|
134
134
|
private sleep;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* The subset of the global `EventSource` constructor surface that the
|
|
139
|
+
* realtime client uses. Allows polyfill injection (e.g. the `eventsource`
|
|
140
|
+
* npm package on Node) without forcing the polyfill to satisfy the full
|
|
141
|
+
* lib.dom.d.ts shape.
|
|
142
|
+
*/
|
|
143
|
+
type EventSourceConstructor = new (url: string) => EventSource;
|
|
137
144
|
/** Options for configuring the realtime connection. */
|
|
138
145
|
interface RealtimeOptions {
|
|
139
146
|
/** Event types to subscribe to (e.g., ['ticket.*', 'boss.*']). Omit for all. */
|
|
@@ -142,6 +149,13 @@ interface RealtimeOptions {
|
|
|
142
149
|
autoReconnect?: boolean;
|
|
143
150
|
/** Max reconnection attempts before giving up (default: 10). */
|
|
144
151
|
maxRetries?: number;
|
|
152
|
+
/**
|
|
153
|
+
* EventSource constructor to use. Defaults to `globalThis.EventSource`
|
|
154
|
+
* (browsers and Node 22+). Pass an explicit polyfill (e.g. the
|
|
155
|
+
* `eventsource` npm package) to subscribe from a Node environment that
|
|
156
|
+
* lacks the global. Closes NEW-v2-02.
|
|
157
|
+
*/
|
|
158
|
+
eventSource?: EventSourceConstructor;
|
|
145
159
|
}
|
|
146
160
|
/** A parsed realtime event from the SSE stream. */
|
|
147
161
|
interface RealtimeEvent<T = unknown> {
|
|
@@ -189,7 +203,15 @@ declare class CStarRealtime {
|
|
|
189
203
|
private readonly autoReconnect;
|
|
190
204
|
private readonly maxRetries;
|
|
191
205
|
private readonly types;
|
|
206
|
+
private readonly eventSourceCtor;
|
|
192
207
|
constructor(httpClient: HttpClient, options?: RealtimeOptions);
|
|
208
|
+
/**
|
|
209
|
+
* Resolve the EventSource constructor to use for this connection.
|
|
210
|
+
* Returns the explicit polyfill when one was injected, otherwise the
|
|
211
|
+
* global. Returns `undefined` when neither is available — callers must
|
|
212
|
+
* convert to `CStarRealtimeUnavailableError` before touching it.
|
|
213
|
+
*/
|
|
214
|
+
private resolveEventSource;
|
|
193
215
|
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
194
216
|
on<T = unknown>(eventType: string, handler: EventHandler<T>): () => void;
|
|
195
217
|
/** Subscribe to all events. Returns an unsubscribe function. */
|
|
@@ -393,6 +415,12 @@ declare abstract class BaseResource<TEntity, TListParams extends PaginationParam
|
|
|
393
415
|
update(id: string, params: TUpdateParams, options?: RequestOptions): Promise<TEntity>;
|
|
394
416
|
/** Delete a resource by ID. */
|
|
395
417
|
del(id: string): Promise<DeleteResponse>;
|
|
418
|
+
/**
|
|
419
|
+
* Delete a resource by ID. Alias for `del()` — kept because IDE
|
|
420
|
+
* auto-complete and documentation both surface `delete` first. Closes
|
|
421
|
+
* NEW-v2-06.
|
|
422
|
+
*/
|
|
423
|
+
delete(id: string): Promise<DeleteResponse>;
|
|
396
424
|
/**
|
|
397
425
|
* Auto-paginating async iterator that yields every item across all pages.
|
|
398
426
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -134,6 +134,13 @@ declare class HttpClient {
|
|
|
134
134
|
private sleep;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* The subset of the global `EventSource` constructor surface that the
|
|
139
|
+
* realtime client uses. Allows polyfill injection (e.g. the `eventsource`
|
|
140
|
+
* npm package on Node) without forcing the polyfill to satisfy the full
|
|
141
|
+
* lib.dom.d.ts shape.
|
|
142
|
+
*/
|
|
143
|
+
type EventSourceConstructor = new (url: string) => EventSource;
|
|
137
144
|
/** Options for configuring the realtime connection. */
|
|
138
145
|
interface RealtimeOptions {
|
|
139
146
|
/** Event types to subscribe to (e.g., ['ticket.*', 'boss.*']). Omit for all. */
|
|
@@ -142,6 +149,13 @@ interface RealtimeOptions {
|
|
|
142
149
|
autoReconnect?: boolean;
|
|
143
150
|
/** Max reconnection attempts before giving up (default: 10). */
|
|
144
151
|
maxRetries?: number;
|
|
152
|
+
/**
|
|
153
|
+
* EventSource constructor to use. Defaults to `globalThis.EventSource`
|
|
154
|
+
* (browsers and Node 22+). Pass an explicit polyfill (e.g. the
|
|
155
|
+
* `eventsource` npm package) to subscribe from a Node environment that
|
|
156
|
+
* lacks the global. Closes NEW-v2-02.
|
|
157
|
+
*/
|
|
158
|
+
eventSource?: EventSourceConstructor;
|
|
145
159
|
}
|
|
146
160
|
/** A parsed realtime event from the SSE stream. */
|
|
147
161
|
interface RealtimeEvent<T = unknown> {
|
|
@@ -189,7 +203,15 @@ declare class CStarRealtime {
|
|
|
189
203
|
private readonly autoReconnect;
|
|
190
204
|
private readonly maxRetries;
|
|
191
205
|
private readonly types;
|
|
206
|
+
private readonly eventSourceCtor;
|
|
192
207
|
constructor(httpClient: HttpClient, options?: RealtimeOptions);
|
|
208
|
+
/**
|
|
209
|
+
* Resolve the EventSource constructor to use for this connection.
|
|
210
|
+
* Returns the explicit polyfill when one was injected, otherwise the
|
|
211
|
+
* global. Returns `undefined` when neither is available — callers must
|
|
212
|
+
* convert to `CStarRealtimeUnavailableError` before touching it.
|
|
213
|
+
*/
|
|
214
|
+
private resolveEventSource;
|
|
193
215
|
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
194
216
|
on<T = unknown>(eventType: string, handler: EventHandler<T>): () => void;
|
|
195
217
|
/** Subscribe to all events. Returns an unsubscribe function. */
|
|
@@ -393,6 +415,12 @@ declare abstract class BaseResource<TEntity, TListParams extends PaginationParam
|
|
|
393
415
|
update(id: string, params: TUpdateParams, options?: RequestOptions): Promise<TEntity>;
|
|
394
416
|
/** Delete a resource by ID. */
|
|
395
417
|
del(id: string): Promise<DeleteResponse>;
|
|
418
|
+
/**
|
|
419
|
+
* Delete a resource by ID. Alias for `del()` — kept because IDE
|
|
420
|
+
* auto-complete and documentation both surface `delete` first. Closes
|
|
421
|
+
* NEW-v2-06.
|
|
422
|
+
*/
|
|
423
|
+
delete(id: string): Promise<DeleteResponse>;
|
|
396
424
|
/**
|
|
397
425
|
* Auto-paginating async iterator that yields every item across all pages.
|
|
398
426
|
*
|
package/dist/index.js
CHANGED
|
@@ -139,6 +139,18 @@ var CStarFeatureDisabledError = class extends CStarError {
|
|
|
139
139
|
this.enableUrl = resolved.enable_url ?? "";
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
|
+
var CStarRealtimeUnavailableError = class extends CStarError {
|
|
143
|
+
constructor(message) {
|
|
144
|
+
const resolved = message ?? "EventSource is not available in this environment. Real-time subscriptions only work in the browser. Move the call to a client-only path (`useEffect` / `onMount`), or pass an `eventSource` polyfill via the realtime options. See https://cstar.help/developers/sdk/realtime#ssr";
|
|
145
|
+
super(0, {
|
|
146
|
+
type: ErrorType.API_ERROR,
|
|
147
|
+
code: "realtime_unavailable",
|
|
148
|
+
message: resolved,
|
|
149
|
+
doc_url: "https://cstar.help/developers/sdk/realtime#ssr"
|
|
150
|
+
});
|
|
151
|
+
this.name = "CStarRealtimeUnavailableError";
|
|
152
|
+
}
|
|
153
|
+
};
|
|
142
154
|
var STATUS_TO_CLASS = {
|
|
143
155
|
400: CStarValidationError,
|
|
144
156
|
401: CStarAuthenticationError,
|
|
@@ -181,7 +193,7 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
181
193
|
}
|
|
182
194
|
|
|
183
195
|
// src/client.ts
|
|
184
|
-
var SDK_VERSION = "0.
|
|
196
|
+
var SDK_VERSION = "0.14.0";
|
|
185
197
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
186
198
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
187
199
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -426,6 +438,18 @@ var CStarRealtime = class {
|
|
|
426
438
|
this.autoReconnect = options?.autoReconnect ?? true;
|
|
427
439
|
this.maxRetries = options?.maxRetries ?? 10;
|
|
428
440
|
this.types = options?.types;
|
|
441
|
+
this.eventSourceCtor = options?.eventSource;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Resolve the EventSource constructor to use for this connection.
|
|
445
|
+
* Returns the explicit polyfill when one was injected, otherwise the
|
|
446
|
+
* global. Returns `undefined` when neither is available — callers must
|
|
447
|
+
* convert to `CStarRealtimeUnavailableError` before touching it.
|
|
448
|
+
*/
|
|
449
|
+
resolveEventSource() {
|
|
450
|
+
if (this.eventSourceCtor) return this.eventSourceCtor;
|
|
451
|
+
const fromGlobal = globalThis.EventSource;
|
|
452
|
+
return typeof fromGlobal === "function" ? fromGlobal : void 0;
|
|
429
453
|
}
|
|
430
454
|
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
431
455
|
on(eventType, handler) {
|
|
@@ -480,6 +504,9 @@ var CStarRealtime = class {
|
|
|
480
504
|
/** Start the SSE connection. Called automatically on first handler registration. */
|
|
481
505
|
connect() {
|
|
482
506
|
if (this.eventSource || this.destroyed || this.connecting) return;
|
|
507
|
+
if (!this.resolveEventSource()) {
|
|
508
|
+
throw new CStarRealtimeUnavailableError();
|
|
509
|
+
}
|
|
483
510
|
this.connecting = true;
|
|
484
511
|
const headers = this.httpClient.getAuthHeaders();
|
|
485
512
|
const rawKey = headers.Authorization?.replace("Bearer ", "") ?? "";
|
|
@@ -518,7 +545,12 @@ var CStarRealtime = class {
|
|
|
518
545
|
}
|
|
519
546
|
const separator = qs ? "&" : "?";
|
|
520
547
|
const authenticatedUrl = authToken ? `${url}${separator}token=${authToken}` : url;
|
|
521
|
-
|
|
548
|
+
const Ctor = this.resolveEventSource();
|
|
549
|
+
if (!Ctor) {
|
|
550
|
+
this.connecting = false;
|
|
551
|
+
throw new CStarRealtimeUnavailableError();
|
|
552
|
+
}
|
|
553
|
+
this.eventSource = new Ctor(authenticatedUrl);
|
|
522
554
|
this.eventSource.onopen = () => {
|
|
523
555
|
this.connecting = false;
|
|
524
556
|
this.retryCount = 0;
|
|
@@ -937,6 +969,14 @@ var BaseResource = class {
|
|
|
937
969
|
async del(id) {
|
|
938
970
|
return this.client.request("DELETE", `${this.basePath}/${id}`);
|
|
939
971
|
}
|
|
972
|
+
/**
|
|
973
|
+
* Delete a resource by ID. Alias for `del()` — kept because IDE
|
|
974
|
+
* auto-complete and documentation both surface `delete` first. Closes
|
|
975
|
+
* NEW-v2-06.
|
|
976
|
+
*/
|
|
977
|
+
async delete(id) {
|
|
978
|
+
return this.del(id);
|
|
979
|
+
}
|
|
940
980
|
/**
|
|
941
981
|
* Auto-paginating async iterator that yields every item across all pages.
|
|
942
982
|
*
|
package/dist/quickhelp/index.cjs
CHANGED
|
@@ -181,11 +181,15 @@ var TYPE_TO_CLASS = {
|
|
|
181
181
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
182
182
|
var QuickHelpClient = class {
|
|
183
183
|
constructor(config) {
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
this.resolveTeamIdPromise = null;
|
|
185
|
+
if (!config?.teamId && !config?.teamSlug) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
'QuickHelpClient: either `teamId` or `teamSlug` is required. Pass `teamSlug` (e.g. "acme") for the easiest setup.'
|
|
188
|
+
);
|
|
186
189
|
}
|
|
187
190
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
188
|
-
this.teamId = config.teamId;
|
|
191
|
+
this.teamId = config.teamId ?? null;
|
|
192
|
+
this.teamSlug = config.teamSlug ?? null;
|
|
189
193
|
this.publishableKey = config.publishableKey ?? null;
|
|
190
194
|
this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
|
|
191
195
|
if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
|
|
@@ -197,6 +201,39 @@ var QuickHelpClient = class {
|
|
|
197
201
|
authHeaders() {
|
|
198
202
|
return this.publishableKey ? { Authorization: `Bearer ${this.publishableKey}` } : {};
|
|
199
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Resolve `teamSlug` → `teamId` lazily on the first call that needs it.
|
|
206
|
+
* Cached for the life of the client. NEW-v2-05.
|
|
207
|
+
*/
|
|
208
|
+
async resolveTeamId() {
|
|
209
|
+
if (this.teamId) return this.teamId;
|
|
210
|
+
if (!this.teamSlug) throw new Error("QuickHelpClient: no teamId or teamSlug set");
|
|
211
|
+
if (this.resolveTeamIdPromise) return this.resolveTeamIdPromise;
|
|
212
|
+
this.resolveTeamIdPromise = (async () => {
|
|
213
|
+
const url = `${this.baseUrl}/api/widget/team-by-slug?teamSlug=${encodeURIComponent(this.teamSlug)}`;
|
|
214
|
+
const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
|
|
215
|
+
if (!res.ok) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`QuickHelpClient: could not resolve teamSlug "${this.teamSlug}" (HTTP ${res.status}).`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const body = await res.json();
|
|
221
|
+
const id = body?.data?.teamId;
|
|
222
|
+
if (!id) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`QuickHelpClient: lookup for teamSlug "${this.teamSlug}" returned no teamId.`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
this.teamId = id;
|
|
228
|
+
return id;
|
|
229
|
+
})();
|
|
230
|
+
try {
|
|
231
|
+
return await this.resolveTeamIdPromise;
|
|
232
|
+
} catch (err) {
|
|
233
|
+
this.resolveTeamIdPromise = null;
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
200
237
|
/**
|
|
201
238
|
* Fetch the sanitized public config (title, subtitle, placeholder,
|
|
202
239
|
* theme overrides). Never includes agent id or escalation contact
|
|
@@ -205,7 +242,8 @@ var QuickHelpClient = class {
|
|
|
205
242
|
* Returns `null` if Quick Help is disabled on the team (404).
|
|
206
243
|
*/
|
|
207
244
|
async config() {
|
|
208
|
-
const
|
|
245
|
+
const teamId = await this.resolveTeamId();
|
|
246
|
+
const url = `${this.baseUrl}/api/quickhelp/config/${encodeURIComponent(teamId)}`;
|
|
209
247
|
const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
|
|
210
248
|
if (res.status === 404) return null;
|
|
211
249
|
if (!res.ok) {
|
|
@@ -222,12 +260,13 @@ var QuickHelpClient = class {
|
|
|
222
260
|
* property set from the response body.
|
|
223
261
|
*/
|
|
224
262
|
async ask(params) {
|
|
263
|
+
const teamId = await this.resolveTeamId();
|
|
225
264
|
const url = `${this.baseUrl}/api/quickhelp/ask`;
|
|
226
265
|
const res = await globalThis.fetch(url, {
|
|
227
266
|
method: "POST",
|
|
228
267
|
headers: { "Content-Type": "application/json", ...this.authHeaders() },
|
|
229
268
|
body: JSON.stringify({
|
|
230
|
-
team_id:
|
|
269
|
+
team_id: teamId,
|
|
231
270
|
session_id: params.sessionId ?? null,
|
|
232
271
|
question: params.question
|
|
233
272
|
})
|
|
@@ -241,7 +280,7 @@ var QuickHelpClient = class {
|
|
|
241
280
|
message: body?.error?.message ?? "Quick Help is not available on this team. Enable an AI Agent (Settings \u2192 AI Agents) and configure Quick Help (Settings \u2192 Quick Help) before calling .ask().",
|
|
242
281
|
doc_url: "https://cstar.help/developers/api/errors#feature-not-configured",
|
|
243
282
|
feature: "quickHelp",
|
|
244
|
-
enable_url: `${this.baseUrl}/team/${encodeURIComponent(
|
|
283
|
+
enable_url: `${this.baseUrl}/team/${encodeURIComponent(teamId)}/settings?section=quickHelp`
|
|
245
284
|
});
|
|
246
285
|
}
|
|
247
286
|
const message = body?.error?.message ?? `Quick Help ask failed: ${res.status}`;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Options for the QuickHelpClient. No auth — Quick Help is anonymous by
|
|
3
|
+
* design.
|
|
4
|
+
*
|
|
5
|
+
* NEW-v2-05: pass either `teamId` (UUID, fastest — no roundtrip) or the
|
|
6
|
+
* easier-to-discover `teamSlug`. The slug is resolved to a UUID on the
|
|
7
|
+
* first network call and cached for the life of the client. At least one
|
|
8
|
+
* is required.
|
|
9
|
+
*/
|
|
2
10
|
interface QuickHelpConfig {
|
|
3
|
-
/** Your team's UUID.
|
|
4
|
-
teamId
|
|
11
|
+
/** Your team's UUID. Either this or `teamSlug` is required. */
|
|
12
|
+
teamId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Your team's slug (e.g. `acme`). Resolved to `teamId` lazily via the
|
|
15
|
+
* public `/api/widget/team-by-slug` endpoint on the first call. Cached
|
|
16
|
+
* thereafter. Either this or `teamId` is required.
|
|
17
|
+
*/
|
|
18
|
+
teamSlug?: string;
|
|
5
19
|
/**
|
|
6
20
|
* Optional publishable key (`pk_test_*` or `pk_live_*`). Sent as
|
|
7
21
|
* `Authorization: Bearer` so test-mode dev sessions can be scoped
|
|
@@ -67,12 +81,20 @@ interface QuickHelpAskResult {
|
|
|
67
81
|
*/
|
|
68
82
|
declare class QuickHelpClient {
|
|
69
83
|
private readonly baseUrl;
|
|
70
|
-
|
|
84
|
+
/** Cached resolved teamId. `null` until the first lookup completes. */
|
|
85
|
+
private teamId;
|
|
86
|
+
private readonly teamSlug;
|
|
87
|
+
private resolveTeamIdPromise;
|
|
71
88
|
private readonly publishableKey;
|
|
72
89
|
/** Resolved mode based on the publishable-key prefix. */
|
|
73
90
|
readonly mode: 'live' | 'test';
|
|
74
91
|
constructor(config: QuickHelpConfig);
|
|
75
92
|
private authHeaders;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve `teamSlug` → `teamId` lazily on the first call that needs it.
|
|
95
|
+
* Cached for the life of the client. NEW-v2-05.
|
|
96
|
+
*/
|
|
97
|
+
private resolveTeamId;
|
|
76
98
|
/**
|
|
77
99
|
* Fetch the sanitized public config (title, subtitle, placeholder,
|
|
78
100
|
* theme overrides). Never includes agent id or escalation contact
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Options for the QuickHelpClient. No auth — Quick Help is anonymous by
|
|
3
|
+
* design.
|
|
4
|
+
*
|
|
5
|
+
* NEW-v2-05: pass either `teamId` (UUID, fastest — no roundtrip) or the
|
|
6
|
+
* easier-to-discover `teamSlug`. The slug is resolved to a UUID on the
|
|
7
|
+
* first network call and cached for the life of the client. At least one
|
|
8
|
+
* is required.
|
|
9
|
+
*/
|
|
2
10
|
interface QuickHelpConfig {
|
|
3
|
-
/** Your team's UUID.
|
|
4
|
-
teamId
|
|
11
|
+
/** Your team's UUID. Either this or `teamSlug` is required. */
|
|
12
|
+
teamId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Your team's slug (e.g. `acme`). Resolved to `teamId` lazily via the
|
|
15
|
+
* public `/api/widget/team-by-slug` endpoint on the first call. Cached
|
|
16
|
+
* thereafter. Either this or `teamId` is required.
|
|
17
|
+
*/
|
|
18
|
+
teamSlug?: string;
|
|
5
19
|
/**
|
|
6
20
|
* Optional publishable key (`pk_test_*` or `pk_live_*`). Sent as
|
|
7
21
|
* `Authorization: Bearer` so test-mode dev sessions can be scoped
|
|
@@ -67,12 +81,20 @@ interface QuickHelpAskResult {
|
|
|
67
81
|
*/
|
|
68
82
|
declare class QuickHelpClient {
|
|
69
83
|
private readonly baseUrl;
|
|
70
|
-
|
|
84
|
+
/** Cached resolved teamId. `null` until the first lookup completes. */
|
|
85
|
+
private teamId;
|
|
86
|
+
private readonly teamSlug;
|
|
87
|
+
private resolveTeamIdPromise;
|
|
71
88
|
private readonly publishableKey;
|
|
72
89
|
/** Resolved mode based on the publishable-key prefix. */
|
|
73
90
|
readonly mode: 'live' | 'test';
|
|
74
91
|
constructor(config: QuickHelpConfig);
|
|
75
92
|
private authHeaders;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve `teamSlug` → `teamId` lazily on the first call that needs it.
|
|
95
|
+
* Cached for the life of the client. NEW-v2-05.
|
|
96
|
+
*/
|
|
97
|
+
private resolveTeamId;
|
|
76
98
|
/**
|
|
77
99
|
* Fetch the sanitized public config (title, subtitle, placeholder,
|
|
78
100
|
* theme overrides). Never includes agent id or escalation contact
|
package/dist/quickhelp/index.js
CHANGED
|
@@ -154,11 +154,15 @@ var TYPE_TO_CLASS = {
|
|
|
154
154
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
155
155
|
var QuickHelpClient = class {
|
|
156
156
|
constructor(config) {
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
this.resolveTeamIdPromise = null;
|
|
158
|
+
if (!config?.teamId && !config?.teamSlug) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
'QuickHelpClient: either `teamId` or `teamSlug` is required. Pass `teamSlug` (e.g. "acme") for the easiest setup.'
|
|
161
|
+
);
|
|
159
162
|
}
|
|
160
163
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
161
|
-
this.teamId = config.teamId;
|
|
164
|
+
this.teamId = config.teamId ?? null;
|
|
165
|
+
this.teamSlug = config.teamSlug ?? null;
|
|
162
166
|
this.publishableKey = config.publishableKey ?? null;
|
|
163
167
|
this.mode = this.publishableKey?.startsWith("pk_test_") ? "test" : "live";
|
|
164
168
|
if (this.publishableKey && !/^pk_(test|live)_/.test(this.publishableKey)) {
|
|
@@ -170,6 +174,39 @@ var QuickHelpClient = class {
|
|
|
170
174
|
authHeaders() {
|
|
171
175
|
return this.publishableKey ? { Authorization: `Bearer ${this.publishableKey}` } : {};
|
|
172
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Resolve `teamSlug` → `teamId` lazily on the first call that needs it.
|
|
179
|
+
* Cached for the life of the client. NEW-v2-05.
|
|
180
|
+
*/
|
|
181
|
+
async resolveTeamId() {
|
|
182
|
+
if (this.teamId) return this.teamId;
|
|
183
|
+
if (!this.teamSlug) throw new Error("QuickHelpClient: no teamId or teamSlug set");
|
|
184
|
+
if (this.resolveTeamIdPromise) return this.resolveTeamIdPromise;
|
|
185
|
+
this.resolveTeamIdPromise = (async () => {
|
|
186
|
+
const url = `${this.baseUrl}/api/widget/team-by-slug?teamSlug=${encodeURIComponent(this.teamSlug)}`;
|
|
187
|
+
const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`QuickHelpClient: could not resolve teamSlug "${this.teamSlug}" (HTTP ${res.status}).`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const body = await res.json();
|
|
194
|
+
const id = body?.data?.teamId;
|
|
195
|
+
if (!id) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`QuickHelpClient: lookup for teamSlug "${this.teamSlug}" returned no teamId.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
this.teamId = id;
|
|
201
|
+
return id;
|
|
202
|
+
})();
|
|
203
|
+
try {
|
|
204
|
+
return await this.resolveTeamIdPromise;
|
|
205
|
+
} catch (err) {
|
|
206
|
+
this.resolveTeamIdPromise = null;
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
173
210
|
/**
|
|
174
211
|
* Fetch the sanitized public config (title, subtitle, placeholder,
|
|
175
212
|
* theme overrides). Never includes agent id or escalation contact
|
|
@@ -178,7 +215,8 @@ var QuickHelpClient = class {
|
|
|
178
215
|
* Returns `null` if Quick Help is disabled on the team (404).
|
|
179
216
|
*/
|
|
180
217
|
async config() {
|
|
181
|
-
const
|
|
218
|
+
const teamId = await this.resolveTeamId();
|
|
219
|
+
const url = `${this.baseUrl}/api/quickhelp/config/${encodeURIComponent(teamId)}`;
|
|
182
220
|
const res = await globalThis.fetch(url, { method: "GET", headers: this.authHeaders() });
|
|
183
221
|
if (res.status === 404) return null;
|
|
184
222
|
if (!res.ok) {
|
|
@@ -195,12 +233,13 @@ var QuickHelpClient = class {
|
|
|
195
233
|
* property set from the response body.
|
|
196
234
|
*/
|
|
197
235
|
async ask(params) {
|
|
236
|
+
const teamId = await this.resolveTeamId();
|
|
198
237
|
const url = `${this.baseUrl}/api/quickhelp/ask`;
|
|
199
238
|
const res = await globalThis.fetch(url, {
|
|
200
239
|
method: "POST",
|
|
201
240
|
headers: { "Content-Type": "application/json", ...this.authHeaders() },
|
|
202
241
|
body: JSON.stringify({
|
|
203
|
-
team_id:
|
|
242
|
+
team_id: teamId,
|
|
204
243
|
session_id: params.sessionId ?? null,
|
|
205
244
|
question: params.question
|
|
206
245
|
})
|
|
@@ -214,7 +253,7 @@ var QuickHelpClient = class {
|
|
|
214
253
|
message: body?.error?.message ?? "Quick Help is not available on this team. Enable an AI Agent (Settings \u2192 AI Agents) and configure Quick Help (Settings \u2192 Quick Help) before calling .ask().",
|
|
215
254
|
doc_url: "https://cstar.help/developers/api/errors#feature-not-configured",
|
|
216
255
|
feature: "quickHelp",
|
|
217
|
-
enable_url: `${this.baseUrl}/team/${encodeURIComponent(
|
|
256
|
+
enable_url: `${this.baseUrl}/team/${encodeURIComponent(teamId)}/settings?section=quickHelp`
|
|
218
257
|
});
|
|
219
258
|
}
|
|
220
259
|
const message = body?.error?.message ?? `Quick Help ask failed: ${res.status}`;
|