@cstar.help/js 0.10.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -7
- package/dist/chat/index.cjs +279 -47
- package/dist/chat/index.d.cts +237 -51
- package/dist/chat/index.d.ts +237 -51
- package/dist/chat/index.js +273 -46
- package/dist/index.cjs +14 -2
- package/dist/index.d.cts +399 -15
- package/dist/index.d.ts +399 -15
- package/dist/index.js +14 -2
- package/dist/quickhelp/index.cjs +102 -0
- package/dist/quickhelp/index.d.cts +97 -0
- package/dist/quickhelp/index.d.ts +97 -0
- package/dist/quickhelp/index.js +76 -0
- package/dist/webhook/index.d.cts +390 -102
- package/dist/webhook/index.d.ts +390 -102
- package/package.json +13 -8
package/dist/chat/index.js
CHANGED
|
@@ -9,8 +9,8 @@ var RealtimeManager = class {
|
|
|
9
9
|
this.options = options;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Initialize the
|
|
13
|
-
* Returns true if successful, false if
|
|
12
|
+
* Initialize the realtime client and authenticate with a token.
|
|
13
|
+
* Returns true if successful, false if the realtime runtime is unavailable.
|
|
14
14
|
*/
|
|
15
15
|
init() {
|
|
16
16
|
if (this.initPromise) return this.initPromise;
|
|
@@ -19,15 +19,15 @@ var RealtimeManager = class {
|
|
|
19
19
|
}
|
|
20
20
|
async doInit() {
|
|
21
21
|
try {
|
|
22
|
+
const tokenData = await this.options.fetchToken();
|
|
22
23
|
const { createClient } = await import(
|
|
23
24
|
/* webpackIgnore: true */
|
|
24
25
|
"@supabase/supabase-js"
|
|
25
26
|
);
|
|
26
|
-
this.client = createClient(
|
|
27
|
+
this.client = createClient(tokenData.realtimeUrl, tokenData.realtimeKey, {
|
|
27
28
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
28
29
|
realtime: { params: { eventsPerSecond: 10 } }
|
|
29
30
|
});
|
|
30
|
-
const tokenData = await this.options.fetchToken();
|
|
31
31
|
this.setToken(tokenData);
|
|
32
32
|
return true;
|
|
33
33
|
} catch {
|
|
@@ -85,7 +85,8 @@ var RealtimeManager = class {
|
|
|
85
85
|
sender_name: row.sender_name ?? "",
|
|
86
86
|
created_at: row.created_at,
|
|
87
87
|
read_at: row.read_at ?? null,
|
|
88
|
-
attachments: row.attachments ?? []
|
|
88
|
+
attachments: row.attachments ?? [],
|
|
89
|
+
payload: row.payload ?? null
|
|
89
90
|
};
|
|
90
91
|
callback(message);
|
|
91
92
|
}
|
|
@@ -93,10 +94,18 @@ var RealtimeManager = class {
|
|
|
93
94
|
this.channels.set(channelName, channel);
|
|
94
95
|
return () => this.removeChannel(channelName);
|
|
95
96
|
}
|
|
96
|
-
/** Get the underlying
|
|
97
|
+
/** Get the underlying realtime client (for sharing with PresenceManager). */
|
|
97
98
|
getClient() {
|
|
98
99
|
return this.client;
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The current Supabase JWT (set by init/refresh). Returned so callers can
|
|
103
|
+
* decode the `sub` claim to learn the customerId — required after a
|
|
104
|
+
* session-mode login where the SDK doesn't have customerId at hand.
|
|
105
|
+
*/
|
|
106
|
+
getCurrentToken() {
|
|
107
|
+
return this.currentToken;
|
|
108
|
+
}
|
|
100
109
|
/** Whether the realtime manager is initialized and has a token. */
|
|
101
110
|
get isReady() {
|
|
102
111
|
return this.client !== null && this.currentToken !== null;
|
|
@@ -135,7 +144,7 @@ var PresenceManager = class {
|
|
|
135
144
|
this.typingClearTimers = /* @__PURE__ */ new Map();
|
|
136
145
|
this.options = options;
|
|
137
146
|
}
|
|
138
|
-
/** Set the
|
|
147
|
+
/** Set the realtime client (shared from RealtimeManager). */
|
|
139
148
|
setClient(client) {
|
|
140
149
|
this.client = client;
|
|
141
150
|
}
|
|
@@ -238,6 +247,43 @@ var PresenceManager = class {
|
|
|
238
247
|
}
|
|
239
248
|
};
|
|
240
249
|
|
|
250
|
+
// src/chat/errors.ts
|
|
251
|
+
var ChatClientError = class extends Error {
|
|
252
|
+
constructor(message, opts) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = "ChatClientError";
|
|
255
|
+
this.code = opts.code ?? "UNKNOWN";
|
|
256
|
+
this.statusCode = opts.statusCode;
|
|
257
|
+
this.requestId = opts.requestId ?? null;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var ChatClientAuthError = class extends ChatClientError {
|
|
261
|
+
constructor(opts) {
|
|
262
|
+
super(opts.message, opts);
|
|
263
|
+
this.name = "ChatClientAuthError";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var ChatClientRateLimitError = class extends ChatClientError {
|
|
267
|
+
constructor(opts) {
|
|
268
|
+
super(opts.message, opts);
|
|
269
|
+
this.name = "ChatClientRateLimitError";
|
|
270
|
+
this.retryAfter = opts.retryAfter ?? null;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var ChatClientServerError = class extends ChatClientError {
|
|
274
|
+
constructor(opts) {
|
|
275
|
+
super(opts.message, opts);
|
|
276
|
+
this.name = "ChatClientServerError";
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
var ChatClientNetworkError = class extends ChatClientError {
|
|
280
|
+
constructor(opts) {
|
|
281
|
+
super(opts.message, { code: "NETWORK_ERROR", statusCode: 0, requestId: null });
|
|
282
|
+
this.name = "ChatClientNetworkError";
|
|
283
|
+
this.originalError = opts.originalError;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
241
287
|
// src/chat/index.ts
|
|
242
288
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
243
289
|
var DEFAULT_POLLING_INTERVAL = 3e3;
|
|
@@ -246,6 +292,7 @@ var ChatClient = class {
|
|
|
246
292
|
this.sessionToken = null;
|
|
247
293
|
this.teamId = null;
|
|
248
294
|
this.customerId = null;
|
|
295
|
+
this.authMode = null;
|
|
249
296
|
this.pollingTimers = /* @__PURE__ */ new Map();
|
|
250
297
|
this.realtimeManager = null;
|
|
251
298
|
this.presenceManager = null;
|
|
@@ -254,18 +301,17 @@ var ChatClient = class {
|
|
|
254
301
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
255
302
|
this.teamSlug = config.teamSlug;
|
|
256
303
|
this.pollingInterval = config.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
|
|
257
|
-
this.
|
|
258
|
-
this.
|
|
259
|
-
this.conversations = new ConversationsApi(this);
|
|
304
|
+
this.realtimeEnabled = config.realtime !== false;
|
|
305
|
+
this.tickets = new TicketsApi(this);
|
|
260
306
|
this.messages = new MessagesApi(this);
|
|
261
307
|
this.community = new ChatCommunityApi(this);
|
|
262
308
|
}
|
|
263
309
|
/**
|
|
264
310
|
* Identify a customer using HMAC signature verification.
|
|
265
|
-
* Must be called before accessing
|
|
311
|
+
* Must be called before accessing tickets or messages.
|
|
266
312
|
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
313
|
+
* Real-time subscriptions are initialized automatically (non-blocking)
|
|
314
|
+
* unless `realtime: false` was passed in config.
|
|
269
315
|
*
|
|
270
316
|
* @param customer - Customer identity fields
|
|
271
317
|
* @param signature - HMAC-SHA256 hex signature of the signed fields
|
|
@@ -287,7 +333,7 @@ var ChatClient = class {
|
|
|
287
333
|
this.sessionToken = response.data.sessionToken;
|
|
288
334
|
this.teamId = response.data.teamId;
|
|
289
335
|
this.customerId = response.data.customer.id;
|
|
290
|
-
if (this.
|
|
336
|
+
if (this.realtimeEnabled) {
|
|
291
337
|
this.realtimeInitPromise = this.initRealtime(
|
|
292
338
|
response.data.customer.id,
|
|
293
339
|
response.data.customer.name || customer.name || customer.email
|
|
@@ -296,8 +342,72 @@ var ChatClient = class {
|
|
|
296
342
|
return response.data;
|
|
297
343
|
}
|
|
298
344
|
/**
|
|
299
|
-
*
|
|
300
|
-
*
|
|
345
|
+
* Sign in with cStar's hosted password auth. The SDK keeps the resulting
|
|
346
|
+
* Supabase access token and uses it as Bearer for every subsequent call.
|
|
347
|
+
* Real-time bootstraps automatically (same flow as `identify()`).
|
|
348
|
+
*
|
|
349
|
+
* Use this when your customers don't have a session in your app and you
|
|
350
|
+
* want cStar to handle login. For the case where your app already has a
|
|
351
|
+
* Supabase session, use `loginWithSession({ accessToken })` instead.
|
|
352
|
+
*/
|
|
353
|
+
async loginWithCredentials(params) {
|
|
354
|
+
const teamId = await this.resolveTeamIdFromSlug();
|
|
355
|
+
const response = await this.fetch("/api/widget/auth/login", {
|
|
356
|
+
method: "POST",
|
|
357
|
+
body: { email: params.email, password: params.password, teamId }
|
|
358
|
+
});
|
|
359
|
+
if (!response.accessToken) {
|
|
360
|
+
throw new ChatClientAuthError({
|
|
361
|
+
message: "Login succeeded but no session was issued",
|
|
362
|
+
code: "NO_SESSION",
|
|
363
|
+
statusCode: 200,
|
|
364
|
+
requestId: null
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
await this.loginWithSession({
|
|
368
|
+
accessToken: response.accessToken,
|
|
369
|
+
teamId,
|
|
370
|
+
customerName: response.customer.name ?? response.customer.email
|
|
371
|
+
});
|
|
372
|
+
return {
|
|
373
|
+
customer: {
|
|
374
|
+
id: this.customerId ?? response.customer.id,
|
|
375
|
+
email: response.customer.email,
|
|
376
|
+
name: response.customer.name
|
|
377
|
+
},
|
|
378
|
+
teamId
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Use an existing Supabase access token for widget calls. Skip the cStar
|
|
383
|
+
* login flow entirely; the SDK uses the supplied token as Bearer and
|
|
384
|
+
* bootstraps real-time the same way `identify()` does.
|
|
385
|
+
*
|
|
386
|
+
* The widget API verifies the token via the `widget-session` strategy on
|
|
387
|
+
* every request — there is no client-side validation here.
|
|
388
|
+
*/
|
|
389
|
+
async loginWithSession(params) {
|
|
390
|
+
this.sessionToken = params.accessToken;
|
|
391
|
+
if (params.teamId) {
|
|
392
|
+
this.teamId = params.teamId;
|
|
393
|
+
}
|
|
394
|
+
this.authMode = "session";
|
|
395
|
+
if (this.realtimeEnabled) {
|
|
396
|
+
const customerName = params.customerName ?? "";
|
|
397
|
+
this.realtimeInitPromise = this.initRealtimeFromSession(customerName);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Returns the auth mode the client is currently using, or `null` before
|
|
402
|
+
* any authenticated call has resolved one. Useful for branching UI
|
|
403
|
+
* (e.g. to show a "Sign out" button only in session mode).
|
|
404
|
+
*/
|
|
405
|
+
getAuthMode() {
|
|
406
|
+
return this.authMode;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Subscribe to new messages on a ticket.
|
|
410
|
+
* Uses real-time delivery when available, falls back to polling.
|
|
301
411
|
*
|
|
302
412
|
* @returns Unsubscribe function
|
|
303
413
|
*/
|
|
@@ -309,7 +419,7 @@ var ChatClient = class {
|
|
|
309
419
|
}
|
|
310
420
|
/**
|
|
311
421
|
* Subscribe to typing indicator events.
|
|
312
|
-
* Requires
|
|
422
|
+
* Requires real-time — returns a no-op when unavailable.
|
|
313
423
|
*
|
|
314
424
|
* @returns Unsubscribe function
|
|
315
425
|
*/
|
|
@@ -321,8 +431,8 @@ var ChatClient = class {
|
|
|
321
431
|
};
|
|
322
432
|
}
|
|
323
433
|
/**
|
|
324
|
-
* Send a customer typing indicator (requires
|
|
325
|
-
* No-op
|
|
434
|
+
* Send a customer typing indicator (requires real-time).
|
|
435
|
+
* No-op when real-time is unavailable.
|
|
326
436
|
*/
|
|
327
437
|
async sendTyping(ticketId, isTyping) {
|
|
328
438
|
if (this.presenceManager) {
|
|
@@ -348,8 +458,20 @@ var ChatClient = class {
|
|
|
348
458
|
this.sessionToken = null;
|
|
349
459
|
this.teamId = null;
|
|
350
460
|
this.customerId = null;
|
|
461
|
+
this.authMode = null;
|
|
351
462
|
}
|
|
352
|
-
/**
|
|
463
|
+
/**
|
|
464
|
+
* @internal Make an authenticated request to the widget API.
|
|
465
|
+
*
|
|
466
|
+
* On any non-2xx response, throws a typed subclass of `ChatClientError`
|
|
467
|
+
* (`ChatClientAuthError`, `ChatClientRateLimitError`, etc.) so consumers
|
|
468
|
+
* can branch on error class instead of parsing message strings.
|
|
469
|
+
*
|
|
470
|
+
* On the way out, reads the `X-Cstar-Auth-Mode` response header (set by
|
|
471
|
+
* widgetHandler on every response) and caches it on the client. After
|
|
472
|
+
* the first authenticated round-trip, `getAuthMode()` returns the
|
|
473
|
+
* server-confirmed mode.
|
|
474
|
+
*/
|
|
353
475
|
async fetch(path, options) {
|
|
354
476
|
const url = this.buildUrl(path, options.query);
|
|
355
477
|
const headers = {
|
|
@@ -358,17 +480,104 @@ var ChatClient = class {
|
|
|
358
480
|
if (this.sessionToken) {
|
|
359
481
|
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
360
482
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
483
|
+
let response;
|
|
484
|
+
try {
|
|
485
|
+
response = await globalThis.fetch(url, {
|
|
486
|
+
method: options.method,
|
|
487
|
+
headers,
|
|
488
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
489
|
+
});
|
|
490
|
+
} catch (networkErr) {
|
|
491
|
+
throw new ChatClientNetworkError({
|
|
492
|
+
message: networkErr instanceof Error ? networkErr.message : "Network request failed",
|
|
493
|
+
originalError: networkErr
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
const responseHeaders = response.headers && typeof response.headers.get === "function" ? response.headers : null;
|
|
497
|
+
const headerMode = responseHeaders?.get("X-Cstar-Auth-Mode") ?? null;
|
|
498
|
+
if (headerMode === "hmac" || headerMode === "session") {
|
|
499
|
+
this.authMode = headerMode;
|
|
500
|
+
}
|
|
501
|
+
const requestId = responseHeaders?.get("X-Request-Id") ?? null;
|
|
366
502
|
if (!response.ok) {
|
|
367
|
-
const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
|
|
368
|
-
|
|
503
|
+
const errorBody = await response.json().catch(() => ({ error: { code: "UNKNOWN", message: "Request failed" } }));
|
|
504
|
+
const code = errorBody.error?.code ?? "UNKNOWN";
|
|
505
|
+
const message = errorBody.error?.message ?? `Widget API error: ${response.status}`;
|
|
506
|
+
if (response.status === 401 || response.status === 403) {
|
|
507
|
+
throw new ChatClientAuthError({
|
|
508
|
+
message,
|
|
509
|
+
code,
|
|
510
|
+
statusCode: response.status,
|
|
511
|
+
requestId
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (response.status === 429) {
|
|
515
|
+
const retryAfterHeader = responseHeaders?.get("Retry-After") ?? null;
|
|
516
|
+
const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : null;
|
|
517
|
+
throw new ChatClientRateLimitError({
|
|
518
|
+
message,
|
|
519
|
+
code,
|
|
520
|
+
statusCode: response.status,
|
|
521
|
+
requestId,
|
|
522
|
+
retryAfter: Number.isFinite(retryAfter ?? NaN) ? retryAfter : null
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (response.status >= 500) {
|
|
526
|
+
throw new ChatClientServerError({
|
|
527
|
+
message,
|
|
528
|
+
code,
|
|
529
|
+
statusCode: response.status,
|
|
530
|
+
requestId
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
throw new ChatClientError(message, {
|
|
534
|
+
code,
|
|
535
|
+
statusCode: response.status,
|
|
536
|
+
requestId
|
|
537
|
+
});
|
|
369
538
|
}
|
|
370
539
|
return response.json();
|
|
371
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Look up the team UUID from the configured `teamSlug`. Used by
|
|
543
|
+
* `loginWithCredentials`. HMAC integrators (`identify()`) never hit
|
|
544
|
+
* this path — verify-identity accepts the slug directly.
|
|
545
|
+
*
|
|
546
|
+
* @internal
|
|
547
|
+
*/
|
|
548
|
+
async resolveTeamIdFromSlug() {
|
|
549
|
+
const response = await this.fetch(
|
|
550
|
+
"/api/widget/team-by-slug",
|
|
551
|
+
{
|
|
552
|
+
method: "GET",
|
|
553
|
+
query: { teamSlug: this.teamSlug }
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
return response.data.teamId;
|
|
557
|
+
}
|
|
558
|
+
/** @internal Bootstrap realtime from a session login (no precomputed customerId). */
|
|
559
|
+
async initRealtimeFromSession(customerName) {
|
|
560
|
+
try {
|
|
561
|
+
this.realtimeManager = new RealtimeManager({
|
|
562
|
+
fetchToken: () => this.fetchRealtimeToken()
|
|
563
|
+
});
|
|
564
|
+
const ready = await this.realtimeManager.init();
|
|
565
|
+
if (!ready) return;
|
|
566
|
+
this.realtimeReady = true;
|
|
567
|
+
const token = this.realtimeManager.getCurrentToken();
|
|
568
|
+
const customerId = token ? decodeJwtSub(token) : null;
|
|
569
|
+
if (customerId) {
|
|
570
|
+
this.customerId = customerId;
|
|
571
|
+
this.presenceManager = new PresenceManager({ customerId, customerName });
|
|
572
|
+
const client = this.realtimeManager.getClient();
|
|
573
|
+
if (client) {
|
|
574
|
+
this.presenceManager.setClient(client);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
} catch {
|
|
578
|
+
this.realtimeReady = false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
372
581
|
/** @internal Get the stored team ID for requests that need it. */
|
|
373
582
|
getTeamId() {
|
|
374
583
|
return this.teamId;
|
|
@@ -386,8 +595,6 @@ var ChatClient = class {
|
|
|
386
595
|
async initRealtime(customerId, customerName) {
|
|
387
596
|
try {
|
|
388
597
|
this.realtimeManager = new RealtimeManager({
|
|
389
|
-
supabaseUrl: this.supabaseUrl,
|
|
390
|
-
supabaseAnonKey: this.supabaseAnonKey,
|
|
391
598
|
fetchToken: () => this.fetchRealtimeToken()
|
|
392
599
|
});
|
|
393
600
|
const ready = await this.realtimeManager.init();
|
|
@@ -454,28 +661,29 @@ var ChatClient = class {
|
|
|
454
661
|
return qs ? `${base}?${qs}` : base;
|
|
455
662
|
}
|
|
456
663
|
};
|
|
457
|
-
var
|
|
664
|
+
var TicketsApi = class {
|
|
458
665
|
constructor(chat) {
|
|
459
666
|
this.chat = chat;
|
|
460
667
|
}
|
|
461
|
-
/** List
|
|
668
|
+
/** List tickets for the identified customer. */
|
|
462
669
|
async list(params) {
|
|
463
670
|
const teamId = this.chat.getTeamId();
|
|
464
671
|
if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
|
|
465
|
-
const response = await this.chat.fetch(
|
|
466
|
-
"
|
|
467
|
-
{
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
limit: params?.limit?.toString(),
|
|
472
|
-
cursor: params?.cursor
|
|
473
|
-
}
|
|
672
|
+
const response = await this.chat.fetch("/api/widget/conversations", {
|
|
673
|
+
method: "GET",
|
|
674
|
+
query: {
|
|
675
|
+
teamId,
|
|
676
|
+
limit: params?.limit?.toString(),
|
|
677
|
+
cursor: params?.cursor
|
|
474
678
|
}
|
|
475
|
-
);
|
|
476
|
-
return
|
|
679
|
+
});
|
|
680
|
+
return {
|
|
681
|
+
tickets: response.data.tickets ?? response.data.conversations ?? [],
|
|
682
|
+
cursor: response.data.cursor,
|
|
683
|
+
hasMore: response.data.hasMore
|
|
684
|
+
};
|
|
477
685
|
}
|
|
478
|
-
/** Create a new
|
|
686
|
+
/** Create a new ticket. */
|
|
479
687
|
async create(params) {
|
|
480
688
|
const teamId = this.chat.getTeamId();
|
|
481
689
|
if (!teamId) throw new Error("Not identified. Call chat.identify() first.");
|
|
@@ -633,7 +841,7 @@ var MessagesApi = class {
|
|
|
633
841
|
constructor(chat) {
|
|
634
842
|
this.chat = chat;
|
|
635
843
|
}
|
|
636
|
-
/** List messages for a
|
|
844
|
+
/** List messages for a ticket. */
|
|
637
845
|
async list(ticketId) {
|
|
638
846
|
const teamId = this.chat.getTeamId();
|
|
639
847
|
const response = await this.chat.fetch(
|
|
@@ -648,7 +856,7 @@ var MessagesApi = class {
|
|
|
648
856
|
);
|
|
649
857
|
return response.data.messages;
|
|
650
858
|
}
|
|
651
|
-
/** Send a message to a
|
|
859
|
+
/** Send a message to a ticket. */
|
|
652
860
|
async send(ticketId, content) {
|
|
653
861
|
const teamId = this.chat.getTeamId();
|
|
654
862
|
const response = await this.chat.fetch(
|
|
@@ -665,6 +873,25 @@ var MessagesApi = class {
|
|
|
665
873
|
return response.data.message;
|
|
666
874
|
}
|
|
667
875
|
};
|
|
876
|
+
function decodeJwtSub(jwt) {
|
|
877
|
+
try {
|
|
878
|
+
const parts = jwt.split(".");
|
|
879
|
+
if (parts.length !== 3) return null;
|
|
880
|
+
const payloadB64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
881
|
+
const padded = payloadB64 + "=".repeat((4 - payloadB64.length % 4) % 4);
|
|
882
|
+
const payload = JSON.parse(
|
|
883
|
+
typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf-8")
|
|
884
|
+
);
|
|
885
|
+
return typeof payload.sub === "string" ? payload.sub : null;
|
|
886
|
+
} catch {
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
668
890
|
export {
|
|
669
|
-
ChatClient
|
|
891
|
+
ChatClient,
|
|
892
|
+
ChatClientAuthError,
|
|
893
|
+
ChatClientError,
|
|
894
|
+
ChatClientNetworkError,
|
|
895
|
+
ChatClientRateLimitError,
|
|
896
|
+
ChatClientServerError
|
|
670
897
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -140,6 +140,16 @@ var DEFAULT_MAX_RETRIES = 3;
|
|
|
140
140
|
var DEFAULT_TIMEOUT = 3e4;
|
|
141
141
|
var HttpClient = class {
|
|
142
142
|
constructor(config) {
|
|
143
|
+
if (!config || typeof config.apiKey !== "string" || config.apiKey.length === 0) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
"[cStar] CStarClient: apiKey is required. Pass a secret key (sk_live_... / sk_test_...) or a publishable key (pk_live_... / pk_test_...). See https://cstar.help/developers/quickstart"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (typeof config.teamId !== "string" || config.teamId.length === 0) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
"[cStar] CStarClient: teamId is required. Find your team ID in Settings \u2192 General or via `cstar status`."
|
|
151
|
+
);
|
|
152
|
+
}
|
|
143
153
|
this.config = {
|
|
144
154
|
apiKey: config.apiKey,
|
|
145
155
|
teamId: config.teamId,
|
|
@@ -1956,10 +1966,12 @@ var GameResource = class {
|
|
|
1956
1966
|
subscribeLeaderboard(realtime, params) {
|
|
1957
1967
|
const fetchFn = async () => {
|
|
1958
1968
|
const res = await this.leaderboard(params);
|
|
1969
|
+
const envelope = res.data;
|
|
1970
|
+
const entries = Array.isArray(envelope) ? envelope : envelope?.entries ?? [];
|
|
1959
1971
|
return {
|
|
1960
1972
|
success: true,
|
|
1961
|
-
data:
|
|
1962
|
-
pagination: { total:
|
|
1973
|
+
data: entries,
|
|
1974
|
+
pagination: { total: entries.length, page: 1, pageSize: 100, hasMore: false },
|
|
1963
1975
|
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1964
1976
|
};
|
|
1965
1977
|
};
|