@cstar.help/js 0.5.2 → 0.6.1
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.d.cts +11 -0
- package/dist/chat/index.d.ts +11 -0
- package/dist/community/index.d.cts +231 -0
- package/dist/community/index.d.ts +231 -0
- package/dist/index.cjs +809 -36
- package/dist/index.d.cts +297 -3
- package/dist/index.d.ts +297 -3
- package/dist/index.js +802 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var ErrorType = {
|
|
3
3
|
AUTHENTICATION_ERROR: "authentication_error",
|
|
4
|
+
PERMISSION_ERROR: "permission_error",
|
|
4
5
|
INVALID_REQUEST_ERROR: "invalid_request_error",
|
|
5
6
|
NOT_FOUND_ERROR: "not_found_error",
|
|
7
|
+
CONFLICT_ERROR: "conflict_error",
|
|
6
8
|
RATE_LIMIT_ERROR: "rate_limit_error",
|
|
7
|
-
API_ERROR: "api_error"
|
|
9
|
+
API_ERROR: "api_error",
|
|
10
|
+
SERVER_ERROR: "server_error"
|
|
8
11
|
};
|
|
9
12
|
var CStarError = class extends Error {
|
|
10
13
|
constructor(statusCode, body) {
|
|
@@ -43,6 +46,24 @@ var CStarValidationError = class extends CStarError {
|
|
|
43
46
|
this.name = "CStarValidationError";
|
|
44
47
|
}
|
|
45
48
|
};
|
|
49
|
+
var CStarPermissionError = class extends CStarError {
|
|
50
|
+
constructor(statusCode, body) {
|
|
51
|
+
super(statusCode, body);
|
|
52
|
+
this.name = "CStarPermissionError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var CStarConflictError = class extends CStarError {
|
|
56
|
+
constructor(statusCode, body) {
|
|
57
|
+
super(statusCode, body);
|
|
58
|
+
this.name = "CStarConflictError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var CStarServerError = class extends CStarError {
|
|
62
|
+
constructor(statusCode, body) {
|
|
63
|
+
super(statusCode, body);
|
|
64
|
+
this.name = "CStarServerError";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
46
67
|
function parseError(statusCode, body, retryAfter) {
|
|
47
68
|
const errorBody = body?.error ?? {
|
|
48
69
|
type: ErrorType.API_ERROR,
|
|
@@ -53,19 +74,28 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
53
74
|
switch (errorBody.type) {
|
|
54
75
|
case ErrorType.AUTHENTICATION_ERROR:
|
|
55
76
|
return new CStarAuthenticationError(statusCode, errorBody);
|
|
77
|
+
case ErrorType.PERMISSION_ERROR:
|
|
78
|
+
return new CStarPermissionError(statusCode, errorBody);
|
|
56
79
|
case ErrorType.RATE_LIMIT_ERROR:
|
|
57
80
|
return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
|
|
58
81
|
case ErrorType.NOT_FOUND_ERROR:
|
|
59
82
|
return new CStarNotFoundError(statusCode, errorBody);
|
|
60
83
|
case ErrorType.INVALID_REQUEST_ERROR:
|
|
61
84
|
return new CStarValidationError(statusCode, errorBody);
|
|
85
|
+
case ErrorType.CONFLICT_ERROR:
|
|
86
|
+
return new CStarConflictError(statusCode, errorBody);
|
|
87
|
+
case ErrorType.SERVER_ERROR:
|
|
88
|
+
return new CStarServerError(statusCode, errorBody);
|
|
62
89
|
default:
|
|
90
|
+
if (statusCode === 403) return new CStarPermissionError(statusCode, errorBody);
|
|
91
|
+
if (statusCode === 409) return new CStarConflictError(statusCode, errorBody);
|
|
92
|
+
if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
|
|
63
93
|
return new CStarError(statusCode, errorBody);
|
|
64
94
|
}
|
|
65
95
|
}
|
|
66
96
|
|
|
67
97
|
// src/client.ts
|
|
68
|
-
var SDK_VERSION = "0.5.
|
|
98
|
+
var SDK_VERSION = "0.5.2";
|
|
69
99
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
70
100
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
71
101
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -122,10 +152,21 @@ var HttpClient = class {
|
|
|
122
152
|
}
|
|
123
153
|
if (response.ok) {
|
|
124
154
|
if (response.status === 204) return void 0;
|
|
125
|
-
|
|
155
|
+
const json = await response.json();
|
|
156
|
+
const requestId2 = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
|
|
157
|
+
if (requestId2 && json && typeof json === "object" && "meta" in json) {
|
|
158
|
+
const existing = json.meta;
|
|
159
|
+
const metaBase = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
160
|
+
json.meta = { ...metaBase, requestId: requestId2 };
|
|
161
|
+
}
|
|
162
|
+
return json;
|
|
126
163
|
}
|
|
127
164
|
const errorBody = await response.json().catch(() => null);
|
|
128
165
|
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "", 10) || void 0;
|
|
166
|
+
const requestId = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
|
|
167
|
+
if (requestId && errorBody?.error) {
|
|
168
|
+
errorBody.error.request_id = requestId;
|
|
169
|
+
}
|
|
129
170
|
if (response.status < 500 && response.status !== 429) {
|
|
130
171
|
throw parseError(response.status, errorBody, retryAfter);
|
|
131
172
|
}
|
|
@@ -169,6 +210,14 @@ var HttpClient = class {
|
|
|
169
210
|
}
|
|
170
211
|
return headers;
|
|
171
212
|
}
|
|
213
|
+
/** Get the base URL for building SSE or other endpoints. */
|
|
214
|
+
getBaseUrl() {
|
|
215
|
+
return `${this.config.baseUrl}/api/v1/teams/${this.config.teamId}`;
|
|
216
|
+
}
|
|
217
|
+
/** Get authorization headers for non-fetch requests (e.g., SSE). */
|
|
218
|
+
getAuthHeaders() {
|
|
219
|
+
return this.buildHeaders();
|
|
220
|
+
}
|
|
172
221
|
/** Exponential backoff: 500ms, 1s, 2s, 4s... capped at 8s, plus jitter. */
|
|
173
222
|
getBackoffDelay(attempt) {
|
|
174
223
|
const base = Math.min(500 * Math.pow(2, attempt - 1), 8e3);
|
|
@@ -180,6 +229,504 @@ var HttpClient = class {
|
|
|
180
229
|
}
|
|
181
230
|
};
|
|
182
231
|
|
|
232
|
+
// src/realtime.ts
|
|
233
|
+
var CStarRealtime = class {
|
|
234
|
+
constructor(httpClient, options) {
|
|
235
|
+
this.httpClient = httpClient;
|
|
236
|
+
this.eventSource = null;
|
|
237
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
238
|
+
this.anyHandlers = /* @__PURE__ */ new Set();
|
|
239
|
+
this.errorHandlers = /* @__PURE__ */ new Set();
|
|
240
|
+
this.connectionHandlers = /* @__PURE__ */ new Set();
|
|
241
|
+
this.lastEventId = null;
|
|
242
|
+
this.retryCount = 0;
|
|
243
|
+
this.retryTimer = null;
|
|
244
|
+
this._connected = false;
|
|
245
|
+
this.destroyed = false;
|
|
246
|
+
this.connecting = false;
|
|
247
|
+
/** Short-lived SSE token (exchanged from secret key). */
|
|
248
|
+
this.sseToken = null;
|
|
249
|
+
/** When the current SSE token expires (epoch ms). */
|
|
250
|
+
this.sseTokenExpiresAt = 0;
|
|
251
|
+
/** Timer that refreshes the SSE token before it expires. */
|
|
252
|
+
this.tokenRefreshTimer = null;
|
|
253
|
+
this.autoReconnect = options?.autoReconnect ?? true;
|
|
254
|
+
this.maxRetries = options?.maxRetries ?? 10;
|
|
255
|
+
this.types = options?.types;
|
|
256
|
+
}
|
|
257
|
+
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
258
|
+
on(eventType, handler) {
|
|
259
|
+
let set = this.handlers.get(eventType);
|
|
260
|
+
if (!set) {
|
|
261
|
+
set = /* @__PURE__ */ new Set();
|
|
262
|
+
this.handlers.set(eventType, set);
|
|
263
|
+
}
|
|
264
|
+
set.add(handler);
|
|
265
|
+
this.ensureConnected();
|
|
266
|
+
return () => {
|
|
267
|
+
set.delete(handler);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/** Subscribe to all events. Returns an unsubscribe function. */
|
|
271
|
+
onAny(handler) {
|
|
272
|
+
this.anyHandlers.add(handler);
|
|
273
|
+
this.ensureConnected();
|
|
274
|
+
return () => {
|
|
275
|
+
this.anyHandlers.delete(handler);
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/** Handle connection errors. */
|
|
279
|
+
onError(handler) {
|
|
280
|
+
this.errorHandlers.add(handler);
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
/** Handle connection state changes. */
|
|
284
|
+
onConnectionChange(handler) {
|
|
285
|
+
this.connectionHandlers.add(handler);
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
/** Remove a connection state handler. */
|
|
289
|
+
offConnectionChange(handler) {
|
|
290
|
+
this.connectionHandlers.delete(handler);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
/** Remove a handler for a specific event type. */
|
|
294
|
+
off(eventType, handler) {
|
|
295
|
+
const set = this.handlers.get(eventType);
|
|
296
|
+
if (set) {
|
|
297
|
+
set.delete(handler);
|
|
298
|
+
if (set.size === 0) this.handlers.delete(eventType);
|
|
299
|
+
}
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
/** Remove a handler from the "any" subscription. */
|
|
303
|
+
offAny(handler) {
|
|
304
|
+
this.anyHandlers.delete(handler);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
/** Start the SSE connection. Called automatically on first handler registration. */
|
|
308
|
+
connect() {
|
|
309
|
+
if (this.eventSource || this.destroyed || this.connecting) return;
|
|
310
|
+
this.connecting = true;
|
|
311
|
+
const headers = this.httpClient.getAuthHeaders();
|
|
312
|
+
const rawKey = headers.Authorization?.replace("Bearer ", "") ?? "";
|
|
313
|
+
if (rawKey.startsWith("sk_") && !this.hasFreshSseToken()) {
|
|
314
|
+
this.exchangeToken().then(() => {
|
|
315
|
+
this.connecting = false;
|
|
316
|
+
this.connect();
|
|
317
|
+
}).catch(() => {
|
|
318
|
+
this.connecting = false;
|
|
319
|
+
this.connectWithToken(rawKey);
|
|
320
|
+
});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const token = this.sseToken ?? rawKey;
|
|
324
|
+
this.connectWithToken(token);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Open the EventSource with the given auth token.
|
|
328
|
+
* Extracted so both the happy path and the fallback path share logic.
|
|
329
|
+
*/
|
|
330
|
+
connectWithToken(authToken) {
|
|
331
|
+
const baseUrl = this.httpClient.getBaseUrl();
|
|
332
|
+
const params = new URLSearchParams();
|
|
333
|
+
if (this.types && this.types.length > 0) {
|
|
334
|
+
params.set("types", this.types.join(","));
|
|
335
|
+
}
|
|
336
|
+
if (this.lastEventId) {
|
|
337
|
+
params.set("since", this.lastEventId);
|
|
338
|
+
}
|
|
339
|
+
const qs = params.toString();
|
|
340
|
+
const url = `${baseUrl}/events/stream${qs ? `?${qs}` : ""}`;
|
|
341
|
+
if (authToken.startsWith("sk_") && typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
|
|
342
|
+
console.warn(
|
|
343
|
+
"[cStar] Secret API key is being passed via SSE query parameter. Query strings may be logged by proxies and CDNs. For production, use a server-side proxy to add the Authorization header."
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
const separator = qs ? "&" : "?";
|
|
347
|
+
const authenticatedUrl = authToken ? `${url}${separator}token=${authToken}` : url;
|
|
348
|
+
this.eventSource = new EventSource(authenticatedUrl);
|
|
349
|
+
this.eventSource.onopen = () => {
|
|
350
|
+
this.connecting = false;
|
|
351
|
+
this.retryCount = 0;
|
|
352
|
+
this.setConnected(true);
|
|
353
|
+
};
|
|
354
|
+
this.eventSource.onerror = () => {
|
|
355
|
+
this.connecting = false;
|
|
356
|
+
this.setConnected(false);
|
|
357
|
+
if (this.eventSource) {
|
|
358
|
+
this.eventSource.close();
|
|
359
|
+
this.eventSource = null;
|
|
360
|
+
}
|
|
361
|
+
if (this.autoReconnect && !this.destroyed && this.retryCount < this.maxRetries) {
|
|
362
|
+
this.scheduleReconnect();
|
|
363
|
+
} else if (this.retryCount >= this.maxRetries) {
|
|
364
|
+
this.emitError(new Error(`Realtime connection failed after ${this.maxRetries} retries`));
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
this.eventSource.onmessage = (event) => {
|
|
368
|
+
this.handleMessage(event);
|
|
369
|
+
};
|
|
370
|
+
const knownTypes = [
|
|
371
|
+
"ticket.created",
|
|
372
|
+
"ticket.updated",
|
|
373
|
+
"ticket.closed",
|
|
374
|
+
"ticket.assigned",
|
|
375
|
+
"message.created",
|
|
376
|
+
"message.updated",
|
|
377
|
+
"notification.created",
|
|
378
|
+
"mention.created",
|
|
379
|
+
// Gamification events (future)
|
|
380
|
+
"boss.spawned",
|
|
381
|
+
"boss.damaged",
|
|
382
|
+
"boss.defeated",
|
|
383
|
+
"achievement.unlocked",
|
|
384
|
+
"quest.completed",
|
|
385
|
+
"level.up"
|
|
386
|
+
];
|
|
387
|
+
for (const type of knownTypes) {
|
|
388
|
+
this.eventSource.addEventListener(type, (event) => {
|
|
389
|
+
const msgEvent = event;
|
|
390
|
+
if (msgEvent.lastEventId) {
|
|
391
|
+
this.lastEventId = msgEvent.lastEventId;
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const parsed = JSON.parse(msgEvent.data);
|
|
395
|
+
const realtimeEvent = {
|
|
396
|
+
type: parsed.type || type,
|
|
397
|
+
id: msgEvent.lastEventId || "",
|
|
398
|
+
data: parsed.data || parsed,
|
|
399
|
+
timestamp: parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
400
|
+
teamId: parsed.teamId || ""
|
|
401
|
+
};
|
|
402
|
+
this.dispatch(realtimeEvent);
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** Stop listening and clean up all resources. */
|
|
409
|
+
disconnect() {
|
|
410
|
+
this.destroyed = true;
|
|
411
|
+
if (this.retryTimer) {
|
|
412
|
+
clearTimeout(this.retryTimer);
|
|
413
|
+
this.retryTimer = null;
|
|
414
|
+
}
|
|
415
|
+
if (this.tokenRefreshTimer) {
|
|
416
|
+
clearTimeout(this.tokenRefreshTimer);
|
|
417
|
+
this.tokenRefreshTimer = null;
|
|
418
|
+
}
|
|
419
|
+
if (this.eventSource) {
|
|
420
|
+
this.eventSource.close();
|
|
421
|
+
this.eventSource = null;
|
|
422
|
+
}
|
|
423
|
+
this.sseToken = null;
|
|
424
|
+
this.sseTokenExpiresAt = 0;
|
|
425
|
+
this.setConnected(false);
|
|
426
|
+
this.handlers.clear();
|
|
427
|
+
this.anyHandlers.clear();
|
|
428
|
+
this.errorHandlers.clear();
|
|
429
|
+
this.connectionHandlers.clear();
|
|
430
|
+
}
|
|
431
|
+
/** Whether the SSE connection is currently open. */
|
|
432
|
+
get connected() {
|
|
433
|
+
return this._connected;
|
|
434
|
+
}
|
|
435
|
+
// ── Internal ──
|
|
436
|
+
ensureConnected() {
|
|
437
|
+
if (!this.eventSource && !this.destroyed) {
|
|
438
|
+
this.connect();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
handleMessage(event) {
|
|
442
|
+
if (event.lastEventId) {
|
|
443
|
+
this.lastEventId = event.lastEventId;
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const parsed = JSON.parse(event.data);
|
|
447
|
+
if (parsed.type === "connected") return;
|
|
448
|
+
const realtimeEvent = {
|
|
449
|
+
type: parsed.type,
|
|
450
|
+
id: event.lastEventId || "",
|
|
451
|
+
data: parsed.data || parsed,
|
|
452
|
+
timestamp: parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
453
|
+
teamId: parsed.teamId || ""
|
|
454
|
+
};
|
|
455
|
+
this.dispatch(realtimeEvent);
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
dispatch(event) {
|
|
460
|
+
const exactSet = this.handlers.get(event.type);
|
|
461
|
+
if (exactSet) {
|
|
462
|
+
for (const handler of exactSet) handler(event);
|
|
463
|
+
}
|
|
464
|
+
for (const [pattern, handlerSet] of this.handlers) {
|
|
465
|
+
if (pattern.endsWith(".*")) {
|
|
466
|
+
const prefix = pattern.slice(0, -2);
|
|
467
|
+
if (event.type.startsWith(prefix + ".") && pattern !== event.type) {
|
|
468
|
+
for (const handler of handlerSet) handler(event);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
for (const handler of this.anyHandlers) handler(event);
|
|
473
|
+
}
|
|
474
|
+
scheduleReconnect() {
|
|
475
|
+
this.retryCount++;
|
|
476
|
+
const delay = Math.min(500 * Math.pow(2, this.retryCount - 1), 8e3) + Math.random() * 200;
|
|
477
|
+
this.retryTimer = setTimeout(() => {
|
|
478
|
+
this.retryTimer = null;
|
|
479
|
+
if (!this.destroyed) {
|
|
480
|
+
this.connect();
|
|
481
|
+
}
|
|
482
|
+
}, delay);
|
|
483
|
+
}
|
|
484
|
+
setConnected(value) {
|
|
485
|
+
if (this._connected !== value) {
|
|
486
|
+
this._connected = value;
|
|
487
|
+
for (const handler of this.connectionHandlers) handler(value);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
emitError(error) {
|
|
491
|
+
for (const handler of this.errorHandlers) handler(error);
|
|
492
|
+
}
|
|
493
|
+
// ── SSE Token Exchange ──
|
|
494
|
+
/** Whether we hold a token that won't expire for at least 30 seconds. */
|
|
495
|
+
hasFreshSseToken() {
|
|
496
|
+
return !!this.sseToken && this.sseTokenExpiresAt - Date.now() > 3e4;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Exchange the secret API key for a short-lived SSE token via
|
|
500
|
+
* POST /events/token. Schedules automatic refresh before expiry.
|
|
501
|
+
*/
|
|
502
|
+
async exchangeToken() {
|
|
503
|
+
const response = await this.httpClient.request("POST", "/events/token");
|
|
504
|
+
const { token, expiresAt, expiresIn } = response.data;
|
|
505
|
+
this.sseToken = token;
|
|
506
|
+
this.sseTokenExpiresAt = new Date(expiresAt).getTime();
|
|
507
|
+
const refreshIn = Math.max((expiresIn - 30) * 1e3, expiresIn / 2 * 1e3);
|
|
508
|
+
this.scheduleTokenRefresh(refreshIn);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Schedule a background token refresh. On success the existing connection
|
|
512
|
+
* keeps working (tokens are validated at connect time). On the next
|
|
513
|
+
* reconnect the fresh token will be used automatically.
|
|
514
|
+
*/
|
|
515
|
+
scheduleTokenRefresh(delayMs) {
|
|
516
|
+
if (this.tokenRefreshTimer) clearTimeout(this.tokenRefreshTimer);
|
|
517
|
+
this.tokenRefreshTimer = setTimeout(() => {
|
|
518
|
+
this.tokenRefreshTimer = null;
|
|
519
|
+
if (this.destroyed) return;
|
|
520
|
+
this.exchangeToken().catch(() => {
|
|
521
|
+
});
|
|
522
|
+
}, delayMs);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// src/subscription.ts
|
|
527
|
+
var RESOURCE_EVENT_MAP = {
|
|
528
|
+
"/tickets": ["ticket.created", "ticket.updated", "ticket.closed", "ticket.assigned"],
|
|
529
|
+
"/customers": ["customer.created", "customer.updated"],
|
|
530
|
+
"/articles": ["article.created", "article.updated", "article.published"],
|
|
531
|
+
"/messages": ["message.created", "message.updated"],
|
|
532
|
+
"/notifications": ["notification.created"],
|
|
533
|
+
"/mentions": ["mention.created"],
|
|
534
|
+
"/game/boss": ["boss.spawned", "boss.damaged", "boss.defeated"],
|
|
535
|
+
"/game/player": ["achievement.unlocked", "quest.completed", "level.up", "player.updated"],
|
|
536
|
+
"/game/leaderboard": [
|
|
537
|
+
"leaderboard.updated",
|
|
538
|
+
"achievement.unlocked",
|
|
539
|
+
"quest.completed",
|
|
540
|
+
"level.up"
|
|
541
|
+
],
|
|
542
|
+
"/game/activity": [
|
|
543
|
+
"boss.spawned",
|
|
544
|
+
"boss.defeated",
|
|
545
|
+
"achievement.unlocked",
|
|
546
|
+
"level.up",
|
|
547
|
+
"quest.completed"
|
|
548
|
+
]
|
|
549
|
+
};
|
|
550
|
+
function getEntityId(item) {
|
|
551
|
+
if (!item || typeof item !== "object") return void 0;
|
|
552
|
+
const obj = item;
|
|
553
|
+
return obj.id ?? obj.userId ?? void 0;
|
|
554
|
+
}
|
|
555
|
+
var Subscription = class {
|
|
556
|
+
constructor(realtime, fetchFn, resourcePath, filterFn) {
|
|
557
|
+
this.realtime = realtime;
|
|
558
|
+
this.fetchFn = fetchFn;
|
|
559
|
+
this.resourcePath = resourcePath;
|
|
560
|
+
this.filterFn = filterFn;
|
|
561
|
+
this._data = [];
|
|
562
|
+
this._loading = true;
|
|
563
|
+
this._error = null;
|
|
564
|
+
this.changeHandlers = /* @__PURE__ */ new Set();
|
|
565
|
+
this.errorEventHandlers = /* @__PURE__ */ new Set();
|
|
566
|
+
this.boundEventHandler = null;
|
|
567
|
+
this.destroyed = false;
|
|
568
|
+
/** Generation counter to prevent stale refresh() from overwriting SSE updates. */
|
|
569
|
+
this.generation = 0;
|
|
570
|
+
this.init();
|
|
571
|
+
}
|
|
572
|
+
/** Current data array (updated in real-time). */
|
|
573
|
+
get data() {
|
|
574
|
+
return this._data;
|
|
575
|
+
}
|
|
576
|
+
/** True during initial data fetch. */
|
|
577
|
+
get loading() {
|
|
578
|
+
return this._loading;
|
|
579
|
+
}
|
|
580
|
+
/** Last error, if any. */
|
|
581
|
+
get error() {
|
|
582
|
+
return this._error;
|
|
583
|
+
}
|
|
584
|
+
on(event, handler) {
|
|
585
|
+
if (event === "change") {
|
|
586
|
+
this.changeHandlers.add(handler);
|
|
587
|
+
} else if (event === "error") {
|
|
588
|
+
this.errorEventHandlers.add(handler);
|
|
589
|
+
}
|
|
590
|
+
return this;
|
|
591
|
+
}
|
|
592
|
+
/** Manually refresh data from the server. */
|
|
593
|
+
async refresh() {
|
|
594
|
+
const gen = ++this.generation;
|
|
595
|
+
try {
|
|
596
|
+
const response = await this.fetchFn();
|
|
597
|
+
if (gen !== this.generation) return;
|
|
598
|
+
this._data = response.data;
|
|
599
|
+
this._error = null;
|
|
600
|
+
this.emitChange();
|
|
601
|
+
} catch (err) {
|
|
602
|
+
if (gen !== this.generation) return;
|
|
603
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
604
|
+
this.emitError(this._error);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Apply an optimistic update to the local data.
|
|
609
|
+
* Returns a rollback function that reverts the change.
|
|
610
|
+
*/
|
|
611
|
+
optimisticUpdate(id, changes) {
|
|
612
|
+
const snapshot = [...this._data];
|
|
613
|
+
const index = this._data.findIndex((item) => getEntityId(item) === id);
|
|
614
|
+
if (index !== -1) {
|
|
615
|
+
this._data[index] = { ...this._data[index], ...changes };
|
|
616
|
+
if (this.filterFn && !this.filterFn(this._data[index])) {
|
|
617
|
+
this._data.splice(index, 1);
|
|
618
|
+
}
|
|
619
|
+
this.emitChange();
|
|
620
|
+
}
|
|
621
|
+
return () => {
|
|
622
|
+
this._data = snapshot;
|
|
623
|
+
this.emitChange();
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/** Stop the subscription and clean up all listeners. */
|
|
627
|
+
unsubscribe() {
|
|
628
|
+
this.destroyed = true;
|
|
629
|
+
if (this.boundEventHandler) {
|
|
630
|
+
this.realtime.offAny(this.boundEventHandler);
|
|
631
|
+
this.boundEventHandler = null;
|
|
632
|
+
}
|
|
633
|
+
this.changeHandlers.clear();
|
|
634
|
+
this.errorEventHandlers.clear();
|
|
635
|
+
}
|
|
636
|
+
// ── Internal ──
|
|
637
|
+
async init() {
|
|
638
|
+
this.boundEventHandler = (event) => this.handleEvent(event);
|
|
639
|
+
this.realtime.onAny(this.boundEventHandler);
|
|
640
|
+
const gen = ++this.generation;
|
|
641
|
+
try {
|
|
642
|
+
const response = await this.fetchFn();
|
|
643
|
+
if (gen !== this.generation) return;
|
|
644
|
+
this._data = response.data;
|
|
645
|
+
this._loading = false;
|
|
646
|
+
this._error = null;
|
|
647
|
+
this.emitChange();
|
|
648
|
+
} catch (err) {
|
|
649
|
+
if (gen !== this.generation) return;
|
|
650
|
+
this._loading = false;
|
|
651
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
652
|
+
this.emitError(this._error);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
handleEvent(event) {
|
|
656
|
+
if (this.destroyed) return;
|
|
657
|
+
const eventTypes = RESOURCE_EVENT_MAP[this.resourcePath];
|
|
658
|
+
if (eventTypes) {
|
|
659
|
+
const isRelevant = eventTypes.some((t) => {
|
|
660
|
+
const prefix = t.split(".")[0];
|
|
661
|
+
return event.type.startsWith(prefix + ".");
|
|
662
|
+
});
|
|
663
|
+
if (!isRelevant) return;
|
|
664
|
+
} else {
|
|
665
|
+
const eventPrefix = event.type.split(".")[0];
|
|
666
|
+
const segments = this.resourcePath.replace(/^\//, "").split("/");
|
|
667
|
+
const resourceName = segments[segments.length - 1];
|
|
668
|
+
if (resourceName !== eventPrefix && resourceName !== eventPrefix + "s") return;
|
|
669
|
+
}
|
|
670
|
+
const entityData = this.extractEntity(event);
|
|
671
|
+
if (!entityData || !getEntityId(entityData)) return;
|
|
672
|
+
this.generation++;
|
|
673
|
+
if (event.type.endsWith(".created")) {
|
|
674
|
+
this.handleCreated(entityData);
|
|
675
|
+
} else if (event.type.endsWith(".deleted")) {
|
|
676
|
+
this.handleDeleted(entityData);
|
|
677
|
+
} else if (event.type.endsWith(".updated") || event.type.endsWith(".assigned") || event.type.endsWith(".closed")) {
|
|
678
|
+
this.handleUpdated(entityData);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
handleCreated(entity) {
|
|
682
|
+
if (this.filterFn && !this.filterFn(entity)) return;
|
|
683
|
+
const entityId = getEntityId(entity);
|
|
684
|
+
if (entityId && this._data.some((item) => getEntityId(item) === entityId)) return;
|
|
685
|
+
this._data = [entity, ...this._data];
|
|
686
|
+
this.emitChange();
|
|
687
|
+
}
|
|
688
|
+
handleDeleted(entity) {
|
|
689
|
+
const entityId = getEntityId(entity);
|
|
690
|
+
if (!entityId) return;
|
|
691
|
+
const index = this._data.findIndex((item) => getEntityId(item) === entityId);
|
|
692
|
+
if (index !== -1) {
|
|
693
|
+
this._data = [...this._data.slice(0, index), ...this._data.slice(index + 1)];
|
|
694
|
+
this.emitChange();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
handleUpdated(entity) {
|
|
698
|
+
const entityId = getEntityId(entity);
|
|
699
|
+
const index = entityId ? this._data.findIndex((item) => getEntityId(item) === entityId) : -1;
|
|
700
|
+
if (index !== -1) {
|
|
701
|
+
const updated = { ...this._data[index], ...entity };
|
|
702
|
+
if (this.filterFn && !this.filterFn(updated)) {
|
|
703
|
+
this._data = [...this._data.slice(0, index), ...this._data.slice(index + 1)];
|
|
704
|
+
} else {
|
|
705
|
+
this._data = [...this._data.slice(0, index), updated, ...this._data.slice(index + 1)];
|
|
706
|
+
}
|
|
707
|
+
this.emitChange();
|
|
708
|
+
} else if (this.filterFn && this.filterFn(entity)) {
|
|
709
|
+
this._data = [entity, ...this._data];
|
|
710
|
+
this.emitChange();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
extractEntity(event) {
|
|
714
|
+
const data = event.data;
|
|
715
|
+
const resourceKey = this.resourcePath.replace(/^\//, "").replace(/s$/, "");
|
|
716
|
+
if (data[resourceKey] && typeof data[resourceKey] === "object") {
|
|
717
|
+
return data[resourceKey];
|
|
718
|
+
}
|
|
719
|
+
if (data.id) return data;
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
emitChange() {
|
|
723
|
+
for (const handler of this.changeHandlers) handler(this._data);
|
|
724
|
+
}
|
|
725
|
+
emitError(error) {
|
|
726
|
+
for (const handler of this.errorEventHandlers) handler(error);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
|
|
183
730
|
// src/resources/base.ts
|
|
184
731
|
var BaseResource = class {
|
|
185
732
|
constructor(client, basePath) {
|
|
@@ -238,6 +785,22 @@ var BaseResource = class {
|
|
|
238
785
|
page++;
|
|
239
786
|
}
|
|
240
787
|
}
|
|
788
|
+
/**
|
|
789
|
+
* Subscribe to real-time changes for this resource type.
|
|
790
|
+
*
|
|
791
|
+
* Fetches initial data, then auto-updates via SSE events.
|
|
792
|
+
* Requires a `CStarRealtime` instance (available from `CStarClient.realtime`).
|
|
793
|
+
*
|
|
794
|
+
* ```typescript
|
|
795
|
+
* const sub = cstar.tickets.subscribe(cstar.realtime, { status: 'open' });
|
|
796
|
+
* sub.on('change', (tickets) => console.log(tickets));
|
|
797
|
+
* // Later: sub.unsubscribe();
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
subscribe(realtime, params, filterFn) {
|
|
801
|
+
const fetchFn = () => this.list(params);
|
|
802
|
+
return new Subscription(realtime, fetchFn, this.basePath, filterFn);
|
|
803
|
+
}
|
|
241
804
|
/** Convert params to string values for query string serialization. */
|
|
242
805
|
serializeQuery(params) {
|
|
243
806
|
const result = {};
|
|
@@ -322,7 +885,7 @@ var WebhooksResource = class extends BaseResource {
|
|
|
322
885
|
);
|
|
323
886
|
}
|
|
324
887
|
/** Subscribe to an event (REST Hooks / Zapier protocol). */
|
|
325
|
-
async
|
|
888
|
+
async restSubscribe(params, options) {
|
|
326
889
|
return this.client.request("POST", "/webhooks/subscribe", {
|
|
327
890
|
body: params,
|
|
328
891
|
idempotencyKey: options?.idempotencyKey
|
|
@@ -1315,6 +1878,72 @@ var GameResource = class {
|
|
|
1315
1878
|
async config() {
|
|
1316
1879
|
return this.client.request("GET", "/game/config");
|
|
1317
1880
|
}
|
|
1881
|
+
// ── Real-time Subscriptions ──
|
|
1882
|
+
/**
|
|
1883
|
+
* Subscribe to live boss battle state.
|
|
1884
|
+
* Updates on boss.spawned, boss.damaged, boss.defeated events.
|
|
1885
|
+
*/
|
|
1886
|
+
subscribeBoss(realtime) {
|
|
1887
|
+
const fetchFn = async () => {
|
|
1888
|
+
const res = await this.boss.current();
|
|
1889
|
+
return {
|
|
1890
|
+
success: true,
|
|
1891
|
+
data: res.data.active ? [res.data] : [],
|
|
1892
|
+
pagination: { total: res.data.active ? 1 : 0, page: 1, pageSize: 1, hasMore: false },
|
|
1893
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1894
|
+
};
|
|
1895
|
+
};
|
|
1896
|
+
return new Subscription(realtime, fetchFn, "/game/boss");
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Subscribe to player stats (XP, gold, level, streaks).
|
|
1900
|
+
* Updates on achievement.unlocked, quest.completed, level.up events.
|
|
1901
|
+
*/
|
|
1902
|
+
subscribePlayer(realtime, userId) {
|
|
1903
|
+
const fetchFn = async () => {
|
|
1904
|
+
const res = await this.player({ userId });
|
|
1905
|
+
return {
|
|
1906
|
+
success: true,
|
|
1907
|
+
data: [res.data],
|
|
1908
|
+
pagination: { total: 1, page: 1, pageSize: 1, hasMore: false },
|
|
1909
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1910
|
+
};
|
|
1911
|
+
};
|
|
1912
|
+
return new Subscription(realtime, fetchFn, "/game/player");
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Subscribe to the team leaderboard.
|
|
1916
|
+
* Updates when scores change.
|
|
1917
|
+
*/
|
|
1918
|
+
subscribeLeaderboard(realtime, params) {
|
|
1919
|
+
const fetchFn = async () => {
|
|
1920
|
+
const res = await this.leaderboard(params);
|
|
1921
|
+
return {
|
|
1922
|
+
success: true,
|
|
1923
|
+
data: res.data,
|
|
1924
|
+
pagination: { total: res.data.length, page: 1, pageSize: 100, hasMore: false },
|
|
1925
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1926
|
+
};
|
|
1927
|
+
};
|
|
1928
|
+
return new Subscription(realtime, fetchFn, "/game/leaderboard");
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Subscribe to team activity feed (achievements, boss defeats, level ups).
|
|
1932
|
+
* Rolling feed of recent game events.
|
|
1933
|
+
*/
|
|
1934
|
+
subscribeActivity(realtime) {
|
|
1935
|
+
const fetchFn = async () => {
|
|
1936
|
+
const res = await this.activity.list({ limit: 50 });
|
|
1937
|
+
const data = res.data.data || [];
|
|
1938
|
+
return {
|
|
1939
|
+
success: true,
|
|
1940
|
+
data,
|
|
1941
|
+
pagination: { total: data.length, page: 1, pageSize: 50, hasMore: false },
|
|
1942
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1943
|
+
};
|
|
1944
|
+
};
|
|
1945
|
+
return new Subscription(realtime, fetchFn, "/game/activity");
|
|
1946
|
+
}
|
|
1318
1947
|
};
|
|
1319
1948
|
|
|
1320
1949
|
// src/resources/ai.ts
|
|
@@ -1377,52 +2006,190 @@ var BillingResource = class {
|
|
|
1377
2006
|
}
|
|
1378
2007
|
};
|
|
1379
2008
|
|
|
2009
|
+
// src/offline.ts
|
|
2010
|
+
var STORAGE_KEY = "cstar_offline_queue";
|
|
2011
|
+
var OfflineQueue = class {
|
|
2012
|
+
constructor(httpClient, options) {
|
|
2013
|
+
this.httpClient = httpClient;
|
|
2014
|
+
this.items = [];
|
|
2015
|
+
this.autoFlushEnabled = false;
|
|
2016
|
+
this.onlineHandler = null;
|
|
2017
|
+
this.useLocalStorage = options?.storage === "localStorage" && typeof globalThis !== "undefined" && typeof globalThis.localStorage !== "undefined";
|
|
2018
|
+
if (this.useLocalStorage) {
|
|
2019
|
+
this.loadFromStorage();
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
/** Queue a mutation for later execution. Returns a queue item ID. */
|
|
2023
|
+
enqueue(method, path, body) {
|
|
2024
|
+
const id = `q_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
2025
|
+
const item = {
|
|
2026
|
+
id,
|
|
2027
|
+
method,
|
|
2028
|
+
path,
|
|
2029
|
+
body,
|
|
2030
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2031
|
+
};
|
|
2032
|
+
this.items.push(item);
|
|
2033
|
+
this.saveToStorage();
|
|
2034
|
+
return id;
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Process all queued mutations in order. Returns results for each item.
|
|
2038
|
+
* Failed items are re-queued at the front to preserve ordering.
|
|
2039
|
+
* Items enqueued during flush are appended after re-queued failures.
|
|
2040
|
+
*/
|
|
2041
|
+
async flush() {
|
|
2042
|
+
const results = [];
|
|
2043
|
+
const itemsToProcess = [...this.items];
|
|
2044
|
+
this.items = [];
|
|
2045
|
+
this.saveToStorage();
|
|
2046
|
+
const failedItems = [];
|
|
2047
|
+
for (const item of itemsToProcess) {
|
|
2048
|
+
try {
|
|
2049
|
+
const data = await this.httpClient.request(item.method, item.path, {
|
|
2050
|
+
body: item.body
|
|
2051
|
+
});
|
|
2052
|
+
results.push({ id: item.id, success: true, data });
|
|
2053
|
+
} catch (err) {
|
|
2054
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2055
|
+
results.push({ id: item.id, success: false, error: message });
|
|
2056
|
+
const statusCode = err.statusCode;
|
|
2057
|
+
const isRetryable = !statusCode || statusCode >= 500;
|
|
2058
|
+
if (isRetryable) {
|
|
2059
|
+
failedItems.push(item);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
this.items = [...failedItems, ...this.items];
|
|
2064
|
+
this.saveToStorage();
|
|
2065
|
+
return results;
|
|
2066
|
+
}
|
|
2067
|
+
/** Get pending queue items. */
|
|
2068
|
+
get pending() {
|
|
2069
|
+
return [...this.items];
|
|
2070
|
+
}
|
|
2071
|
+
/** Number of items in the queue. */
|
|
2072
|
+
get size() {
|
|
2073
|
+
return this.items.length;
|
|
2074
|
+
}
|
|
2075
|
+
/** Clear all pending items. */
|
|
2076
|
+
clear() {
|
|
2077
|
+
this.items = [];
|
|
2078
|
+
this.saveToStorage();
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Enable auto-flush when the browser goes online.
|
|
2082
|
+
* Only works in browser environments with the `online` event.
|
|
2083
|
+
*/
|
|
2084
|
+
enableAutoFlush() {
|
|
2085
|
+
if (this.autoFlushEnabled) return;
|
|
2086
|
+
if (typeof globalThis === "undefined" || typeof globalThis.addEventListener !== "function") {
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
this.autoFlushEnabled = true;
|
|
2090
|
+
this.onlineHandler = () => {
|
|
2091
|
+
if (this.items.length > 0) {
|
|
2092
|
+
this.flush().catch(() => {
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
globalThis.addEventListener("online", this.onlineHandler);
|
|
2097
|
+
}
|
|
2098
|
+
/** Disable auto-flush and clean up event listeners. */
|
|
2099
|
+
disableAutoFlush() {
|
|
2100
|
+
if (!this.autoFlushEnabled || !this.onlineHandler) return;
|
|
2101
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.removeEventListener === "function") {
|
|
2102
|
+
globalThis.removeEventListener("online", this.onlineHandler);
|
|
2103
|
+
}
|
|
2104
|
+
this.onlineHandler = null;
|
|
2105
|
+
this.autoFlushEnabled = false;
|
|
2106
|
+
}
|
|
2107
|
+
saveToStorage() {
|
|
2108
|
+
if (!this.useLocalStorage) return;
|
|
2109
|
+
try {
|
|
2110
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.items));
|
|
2111
|
+
} catch {
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
loadFromStorage() {
|
|
2115
|
+
try {
|
|
2116
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
2117
|
+
if (raw) {
|
|
2118
|
+
this.items = JSON.parse(raw);
|
|
2119
|
+
}
|
|
2120
|
+
} catch {
|
|
2121
|
+
this.items = [];
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
|
|
1380
2126
|
// src/index.ts
|
|
1381
2127
|
var CStarClient = class {
|
|
1382
2128
|
constructor(config) {
|
|
1383
|
-
this.
|
|
1384
|
-
this.tickets = new TicketsResource(this.
|
|
1385
|
-
this.customers = new CustomersResource(this.
|
|
1386
|
-
this.articles = new ArticlesResource(this.
|
|
1387
|
-
this.webhooks = new WebhooksResource(this.
|
|
1388
|
-
this.automations = new AutomationsResource(this.
|
|
1389
|
-
this.categories = new CategoriesResource(this.
|
|
2129
|
+
this._httpClient = new HttpClient(config);
|
|
2130
|
+
this.tickets = new TicketsResource(this._httpClient);
|
|
2131
|
+
this.customers = new CustomersResource(this._httpClient);
|
|
2132
|
+
this.articles = new ArticlesResource(this._httpClient);
|
|
2133
|
+
this.webhooks = new WebhooksResource(this._httpClient);
|
|
2134
|
+
this.automations = new AutomationsResource(this._httpClient);
|
|
2135
|
+
this.categories = new CategoriesResource(this._httpClient);
|
|
1390
2136
|
this.community = {
|
|
1391
|
-
posts: new CommunityPostsResource(this.
|
|
1392
|
-
topics: new CommunityTopicsResource(this.
|
|
2137
|
+
posts: new CommunityPostsResource(this._httpClient),
|
|
2138
|
+
topics: new CommunityTopicsResource(this._httpClient)
|
|
1393
2139
|
};
|
|
1394
|
-
this.members = new MembersResource(this.
|
|
1395
|
-
this.settings = new SettingsResource(this.
|
|
1396
|
-
this.notifications = new NotificationsResource(this.
|
|
1397
|
-
this.mentions = new MentionsResource(this.
|
|
1398
|
-
this.attachments = new AttachmentsResource(this.
|
|
1399
|
-
this.customFields = new CustomFieldsResource(this.
|
|
1400
|
-
this.sla = new SlaResource(this.
|
|
1401
|
-
this.analytics = new AnalyticsResource(this.
|
|
1402
|
-
this.bulk = new BulkResource(this.
|
|
1403
|
-
this.export = new ExportResource(this.
|
|
1404
|
-
this.import = new ImportResource(this.
|
|
1405
|
-
this.auditLog = new AuditLogResource(this.
|
|
1406
|
-
this.search = new SearchResource(this.
|
|
1407
|
-
this.tags = new TagsResource(this.
|
|
1408
|
-
this.activity = new ActivityResource(this.
|
|
1409
|
-
this.customerGroups = new CustomerGroupsResource(this.
|
|
1410
|
-
this.game = new GameResource(this.
|
|
1411
|
-
this.ai = new AiResource(this.
|
|
1412
|
-
this.views = new ViewsResource(this.
|
|
1413
|
-
this.billing = new BillingResource(this.
|
|
2140
|
+
this.members = new MembersResource(this._httpClient);
|
|
2141
|
+
this.settings = new SettingsResource(this._httpClient);
|
|
2142
|
+
this.notifications = new NotificationsResource(this._httpClient);
|
|
2143
|
+
this.mentions = new MentionsResource(this._httpClient);
|
|
2144
|
+
this.attachments = new AttachmentsResource(this._httpClient);
|
|
2145
|
+
this.customFields = new CustomFieldsResource(this._httpClient);
|
|
2146
|
+
this.sla = new SlaResource(this._httpClient);
|
|
2147
|
+
this.analytics = new AnalyticsResource(this._httpClient);
|
|
2148
|
+
this.bulk = new BulkResource(this._httpClient);
|
|
2149
|
+
this.export = new ExportResource(this._httpClient);
|
|
2150
|
+
this.import = new ImportResource(this._httpClient);
|
|
2151
|
+
this.auditLog = new AuditLogResource(this._httpClient);
|
|
2152
|
+
this.search = new SearchResource(this._httpClient);
|
|
2153
|
+
this.tags = new TagsResource(this._httpClient);
|
|
2154
|
+
this.activity = new ActivityResource(this._httpClient);
|
|
2155
|
+
this.customerGroups = new CustomerGroupsResource(this._httpClient);
|
|
2156
|
+
this.game = new GameResource(this._httpClient);
|
|
2157
|
+
this.ai = new AiResource(this._httpClient);
|
|
2158
|
+
this.views = new ViewsResource(this._httpClient);
|
|
2159
|
+
this.billing = new BillingResource(this._httpClient);
|
|
2160
|
+
this.realtime = new CStarRealtime(this._httpClient);
|
|
2161
|
+
if (config.offline) {
|
|
2162
|
+
const queueOpts = typeof config.offline === "object" ? config.offline : void 0;
|
|
2163
|
+
this.offlineQueue = new OfflineQueue(this._httpClient, queueOpts);
|
|
2164
|
+
this.offlineQueue.enableAutoFlush();
|
|
2165
|
+
} else {
|
|
2166
|
+
this.offlineQueue = null;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
/** Disconnect the realtime stream and clean up. */
|
|
2170
|
+
destroy() {
|
|
2171
|
+
this.realtime.disconnect();
|
|
2172
|
+
if (this.offlineQueue) {
|
|
2173
|
+
this.offlineQueue.disableAutoFlush();
|
|
2174
|
+
}
|
|
1414
2175
|
}
|
|
1415
2176
|
/** Whether this client is using test mode keys (sk_test_* or pk_test_*). */
|
|
1416
2177
|
get isTestMode() {
|
|
1417
|
-
return this.
|
|
2178
|
+
return this._httpClient.environment === "test";
|
|
1418
2179
|
}
|
|
1419
2180
|
};
|
|
1420
2181
|
export {
|
|
1421
2182
|
CStarAuthenticationError,
|
|
1422
2183
|
CStarClient,
|
|
2184
|
+
CStarConflictError,
|
|
1423
2185
|
CStarError,
|
|
1424
2186
|
CStarNotFoundError,
|
|
2187
|
+
CStarPermissionError,
|
|
1425
2188
|
CStarRateLimitError,
|
|
2189
|
+
CStarRealtime,
|
|
2190
|
+
CStarServerError,
|
|
1426
2191
|
CStarValidationError,
|
|
1427
|
-
ErrorType
|
|
2192
|
+
ErrorType,
|
|
2193
|
+
OfflineQueue,
|
|
2194
|
+
Subscription
|
|
1428
2195
|
};
|