@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.cjs
CHANGED
|
@@ -22,21 +22,30 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
CStarAuthenticationError: () => CStarAuthenticationError,
|
|
24
24
|
CStarClient: () => CStarClient,
|
|
25
|
+
CStarConflictError: () => CStarConflictError,
|
|
25
26
|
CStarError: () => CStarError,
|
|
26
27
|
CStarNotFoundError: () => CStarNotFoundError,
|
|
28
|
+
CStarPermissionError: () => CStarPermissionError,
|
|
27
29
|
CStarRateLimitError: () => CStarRateLimitError,
|
|
30
|
+
CStarRealtime: () => CStarRealtime,
|
|
31
|
+
CStarServerError: () => CStarServerError,
|
|
28
32
|
CStarValidationError: () => CStarValidationError,
|
|
29
|
-
ErrorType: () => ErrorType
|
|
33
|
+
ErrorType: () => ErrorType,
|
|
34
|
+
OfflineQueue: () => OfflineQueue,
|
|
35
|
+
Subscription: () => Subscription
|
|
30
36
|
});
|
|
31
37
|
module.exports = __toCommonJS(src_exports);
|
|
32
38
|
|
|
33
39
|
// src/errors.ts
|
|
34
40
|
var ErrorType = {
|
|
35
41
|
AUTHENTICATION_ERROR: "authentication_error",
|
|
42
|
+
PERMISSION_ERROR: "permission_error",
|
|
36
43
|
INVALID_REQUEST_ERROR: "invalid_request_error",
|
|
37
44
|
NOT_FOUND_ERROR: "not_found_error",
|
|
45
|
+
CONFLICT_ERROR: "conflict_error",
|
|
38
46
|
RATE_LIMIT_ERROR: "rate_limit_error",
|
|
39
|
-
API_ERROR: "api_error"
|
|
47
|
+
API_ERROR: "api_error",
|
|
48
|
+
SERVER_ERROR: "server_error"
|
|
40
49
|
};
|
|
41
50
|
var CStarError = class extends Error {
|
|
42
51
|
constructor(statusCode, body) {
|
|
@@ -75,6 +84,24 @@ var CStarValidationError = class extends CStarError {
|
|
|
75
84
|
this.name = "CStarValidationError";
|
|
76
85
|
}
|
|
77
86
|
};
|
|
87
|
+
var CStarPermissionError = class extends CStarError {
|
|
88
|
+
constructor(statusCode, body) {
|
|
89
|
+
super(statusCode, body);
|
|
90
|
+
this.name = "CStarPermissionError";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var CStarConflictError = class extends CStarError {
|
|
94
|
+
constructor(statusCode, body) {
|
|
95
|
+
super(statusCode, body);
|
|
96
|
+
this.name = "CStarConflictError";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var CStarServerError = class extends CStarError {
|
|
100
|
+
constructor(statusCode, body) {
|
|
101
|
+
super(statusCode, body);
|
|
102
|
+
this.name = "CStarServerError";
|
|
103
|
+
}
|
|
104
|
+
};
|
|
78
105
|
function parseError(statusCode, body, retryAfter) {
|
|
79
106
|
const errorBody = body?.error ?? {
|
|
80
107
|
type: ErrorType.API_ERROR,
|
|
@@ -85,19 +112,28 @@ function parseError(statusCode, body, retryAfter) {
|
|
|
85
112
|
switch (errorBody.type) {
|
|
86
113
|
case ErrorType.AUTHENTICATION_ERROR:
|
|
87
114
|
return new CStarAuthenticationError(statusCode, errorBody);
|
|
115
|
+
case ErrorType.PERMISSION_ERROR:
|
|
116
|
+
return new CStarPermissionError(statusCode, errorBody);
|
|
88
117
|
case ErrorType.RATE_LIMIT_ERROR:
|
|
89
118
|
return new CStarRateLimitError(statusCode, errorBody, retryAfter ?? 60);
|
|
90
119
|
case ErrorType.NOT_FOUND_ERROR:
|
|
91
120
|
return new CStarNotFoundError(statusCode, errorBody);
|
|
92
121
|
case ErrorType.INVALID_REQUEST_ERROR:
|
|
93
122
|
return new CStarValidationError(statusCode, errorBody);
|
|
123
|
+
case ErrorType.CONFLICT_ERROR:
|
|
124
|
+
return new CStarConflictError(statusCode, errorBody);
|
|
125
|
+
case ErrorType.SERVER_ERROR:
|
|
126
|
+
return new CStarServerError(statusCode, errorBody);
|
|
94
127
|
default:
|
|
128
|
+
if (statusCode === 403) return new CStarPermissionError(statusCode, errorBody);
|
|
129
|
+
if (statusCode === 409) return new CStarConflictError(statusCode, errorBody);
|
|
130
|
+
if (statusCode >= 500) return new CStarServerError(statusCode, errorBody);
|
|
95
131
|
return new CStarError(statusCode, errorBody);
|
|
96
132
|
}
|
|
97
133
|
}
|
|
98
134
|
|
|
99
135
|
// src/client.ts
|
|
100
|
-
var SDK_VERSION = "0.5.
|
|
136
|
+
var SDK_VERSION = "0.5.2";
|
|
101
137
|
var DEFAULT_BASE_URL = "https://www.cstar.help";
|
|
102
138
|
var DEFAULT_API_VERSION = "2026-03-01";
|
|
103
139
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -154,10 +190,21 @@ var HttpClient = class {
|
|
|
154
190
|
}
|
|
155
191
|
if (response.ok) {
|
|
156
192
|
if (response.status === 204) return void 0;
|
|
157
|
-
|
|
193
|
+
const json = await response.json();
|
|
194
|
+
const requestId2 = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
|
|
195
|
+
if (requestId2 && json && typeof json === "object" && "meta" in json) {
|
|
196
|
+
const existing = json.meta;
|
|
197
|
+
const metaBase = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
198
|
+
json.meta = { ...metaBase, requestId: requestId2 };
|
|
199
|
+
}
|
|
200
|
+
return json;
|
|
158
201
|
}
|
|
159
202
|
const errorBody = await response.json().catch(() => null);
|
|
160
203
|
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "", 10) || void 0;
|
|
204
|
+
const requestId = response.headers.get("x-request-id") ?? response.headers.get("x-cstar-request-id");
|
|
205
|
+
if (requestId && errorBody?.error) {
|
|
206
|
+
errorBody.error.request_id = requestId;
|
|
207
|
+
}
|
|
161
208
|
if (response.status < 500 && response.status !== 429) {
|
|
162
209
|
throw parseError(response.status, errorBody, retryAfter);
|
|
163
210
|
}
|
|
@@ -201,6 +248,14 @@ var HttpClient = class {
|
|
|
201
248
|
}
|
|
202
249
|
return headers;
|
|
203
250
|
}
|
|
251
|
+
/** Get the base URL for building SSE or other endpoints. */
|
|
252
|
+
getBaseUrl() {
|
|
253
|
+
return `${this.config.baseUrl}/api/v1/teams/${this.config.teamId}`;
|
|
254
|
+
}
|
|
255
|
+
/** Get authorization headers for non-fetch requests (e.g., SSE). */
|
|
256
|
+
getAuthHeaders() {
|
|
257
|
+
return this.buildHeaders();
|
|
258
|
+
}
|
|
204
259
|
/** Exponential backoff: 500ms, 1s, 2s, 4s... capped at 8s, plus jitter. */
|
|
205
260
|
getBackoffDelay(attempt) {
|
|
206
261
|
const base = Math.min(500 * Math.pow(2, attempt - 1), 8e3);
|
|
@@ -212,6 +267,504 @@ var HttpClient = class {
|
|
|
212
267
|
}
|
|
213
268
|
};
|
|
214
269
|
|
|
270
|
+
// src/realtime.ts
|
|
271
|
+
var CStarRealtime = class {
|
|
272
|
+
constructor(httpClient, options) {
|
|
273
|
+
this.httpClient = httpClient;
|
|
274
|
+
this.eventSource = null;
|
|
275
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
276
|
+
this.anyHandlers = /* @__PURE__ */ new Set();
|
|
277
|
+
this.errorHandlers = /* @__PURE__ */ new Set();
|
|
278
|
+
this.connectionHandlers = /* @__PURE__ */ new Set();
|
|
279
|
+
this.lastEventId = null;
|
|
280
|
+
this.retryCount = 0;
|
|
281
|
+
this.retryTimer = null;
|
|
282
|
+
this._connected = false;
|
|
283
|
+
this.destroyed = false;
|
|
284
|
+
this.connecting = false;
|
|
285
|
+
/** Short-lived SSE token (exchanged from secret key). */
|
|
286
|
+
this.sseToken = null;
|
|
287
|
+
/** When the current SSE token expires (epoch ms). */
|
|
288
|
+
this.sseTokenExpiresAt = 0;
|
|
289
|
+
/** Timer that refreshes the SSE token before it expires. */
|
|
290
|
+
this.tokenRefreshTimer = null;
|
|
291
|
+
this.autoReconnect = options?.autoReconnect ?? true;
|
|
292
|
+
this.maxRetries = options?.maxRetries ?? 10;
|
|
293
|
+
this.types = options?.types;
|
|
294
|
+
}
|
|
295
|
+
/** Subscribe to a specific event type. Supports wildcards (e.g., 'ticket.*'). Returns an unsubscribe function. */
|
|
296
|
+
on(eventType, handler) {
|
|
297
|
+
let set = this.handlers.get(eventType);
|
|
298
|
+
if (!set) {
|
|
299
|
+
set = /* @__PURE__ */ new Set();
|
|
300
|
+
this.handlers.set(eventType, set);
|
|
301
|
+
}
|
|
302
|
+
set.add(handler);
|
|
303
|
+
this.ensureConnected();
|
|
304
|
+
return () => {
|
|
305
|
+
set.delete(handler);
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/** Subscribe to all events. Returns an unsubscribe function. */
|
|
309
|
+
onAny(handler) {
|
|
310
|
+
this.anyHandlers.add(handler);
|
|
311
|
+
this.ensureConnected();
|
|
312
|
+
return () => {
|
|
313
|
+
this.anyHandlers.delete(handler);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/** Handle connection errors. */
|
|
317
|
+
onError(handler) {
|
|
318
|
+
this.errorHandlers.add(handler);
|
|
319
|
+
return this;
|
|
320
|
+
}
|
|
321
|
+
/** Handle connection state changes. */
|
|
322
|
+
onConnectionChange(handler) {
|
|
323
|
+
this.connectionHandlers.add(handler);
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
/** Remove a connection state handler. */
|
|
327
|
+
offConnectionChange(handler) {
|
|
328
|
+
this.connectionHandlers.delete(handler);
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
/** Remove a handler for a specific event type. */
|
|
332
|
+
off(eventType, handler) {
|
|
333
|
+
const set = this.handlers.get(eventType);
|
|
334
|
+
if (set) {
|
|
335
|
+
set.delete(handler);
|
|
336
|
+
if (set.size === 0) this.handlers.delete(eventType);
|
|
337
|
+
}
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
/** Remove a handler from the "any" subscription. */
|
|
341
|
+
offAny(handler) {
|
|
342
|
+
this.anyHandlers.delete(handler);
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
/** Start the SSE connection. Called automatically on first handler registration. */
|
|
346
|
+
connect() {
|
|
347
|
+
if (this.eventSource || this.destroyed || this.connecting) return;
|
|
348
|
+
this.connecting = true;
|
|
349
|
+
const headers = this.httpClient.getAuthHeaders();
|
|
350
|
+
const rawKey = headers.Authorization?.replace("Bearer ", "") ?? "";
|
|
351
|
+
if (rawKey.startsWith("sk_") && !this.hasFreshSseToken()) {
|
|
352
|
+
this.exchangeToken().then(() => {
|
|
353
|
+
this.connecting = false;
|
|
354
|
+
this.connect();
|
|
355
|
+
}).catch(() => {
|
|
356
|
+
this.connecting = false;
|
|
357
|
+
this.connectWithToken(rawKey);
|
|
358
|
+
});
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const token = this.sseToken ?? rawKey;
|
|
362
|
+
this.connectWithToken(token);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Open the EventSource with the given auth token.
|
|
366
|
+
* Extracted so both the happy path and the fallback path share logic.
|
|
367
|
+
*/
|
|
368
|
+
connectWithToken(authToken) {
|
|
369
|
+
const baseUrl = this.httpClient.getBaseUrl();
|
|
370
|
+
const params = new URLSearchParams();
|
|
371
|
+
if (this.types && this.types.length > 0) {
|
|
372
|
+
params.set("types", this.types.join(","));
|
|
373
|
+
}
|
|
374
|
+
if (this.lastEventId) {
|
|
375
|
+
params.set("since", this.lastEventId);
|
|
376
|
+
}
|
|
377
|
+
const qs = params.toString();
|
|
378
|
+
const url = `${baseUrl}/events/stream${qs ? `?${qs}` : ""}`;
|
|
379
|
+
if (authToken.startsWith("sk_") && typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined") {
|
|
380
|
+
console.warn(
|
|
381
|
+
"[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."
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
const separator = qs ? "&" : "?";
|
|
385
|
+
const authenticatedUrl = authToken ? `${url}${separator}token=${authToken}` : url;
|
|
386
|
+
this.eventSource = new EventSource(authenticatedUrl);
|
|
387
|
+
this.eventSource.onopen = () => {
|
|
388
|
+
this.connecting = false;
|
|
389
|
+
this.retryCount = 0;
|
|
390
|
+
this.setConnected(true);
|
|
391
|
+
};
|
|
392
|
+
this.eventSource.onerror = () => {
|
|
393
|
+
this.connecting = false;
|
|
394
|
+
this.setConnected(false);
|
|
395
|
+
if (this.eventSource) {
|
|
396
|
+
this.eventSource.close();
|
|
397
|
+
this.eventSource = null;
|
|
398
|
+
}
|
|
399
|
+
if (this.autoReconnect && !this.destroyed && this.retryCount < this.maxRetries) {
|
|
400
|
+
this.scheduleReconnect();
|
|
401
|
+
} else if (this.retryCount >= this.maxRetries) {
|
|
402
|
+
this.emitError(new Error(`Realtime connection failed after ${this.maxRetries} retries`));
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
this.eventSource.onmessage = (event) => {
|
|
406
|
+
this.handleMessage(event);
|
|
407
|
+
};
|
|
408
|
+
const knownTypes = [
|
|
409
|
+
"ticket.created",
|
|
410
|
+
"ticket.updated",
|
|
411
|
+
"ticket.closed",
|
|
412
|
+
"ticket.assigned",
|
|
413
|
+
"message.created",
|
|
414
|
+
"message.updated",
|
|
415
|
+
"notification.created",
|
|
416
|
+
"mention.created",
|
|
417
|
+
// Gamification events (future)
|
|
418
|
+
"boss.spawned",
|
|
419
|
+
"boss.damaged",
|
|
420
|
+
"boss.defeated",
|
|
421
|
+
"achievement.unlocked",
|
|
422
|
+
"quest.completed",
|
|
423
|
+
"level.up"
|
|
424
|
+
];
|
|
425
|
+
for (const type of knownTypes) {
|
|
426
|
+
this.eventSource.addEventListener(type, (event) => {
|
|
427
|
+
const msgEvent = event;
|
|
428
|
+
if (msgEvent.lastEventId) {
|
|
429
|
+
this.lastEventId = msgEvent.lastEventId;
|
|
430
|
+
}
|
|
431
|
+
try {
|
|
432
|
+
const parsed = JSON.parse(msgEvent.data);
|
|
433
|
+
const realtimeEvent = {
|
|
434
|
+
type: parsed.type || type,
|
|
435
|
+
id: msgEvent.lastEventId || "",
|
|
436
|
+
data: parsed.data || parsed,
|
|
437
|
+
timestamp: parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
438
|
+
teamId: parsed.teamId || ""
|
|
439
|
+
};
|
|
440
|
+
this.dispatch(realtimeEvent);
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/** Stop listening and clean up all resources. */
|
|
447
|
+
disconnect() {
|
|
448
|
+
this.destroyed = true;
|
|
449
|
+
if (this.retryTimer) {
|
|
450
|
+
clearTimeout(this.retryTimer);
|
|
451
|
+
this.retryTimer = null;
|
|
452
|
+
}
|
|
453
|
+
if (this.tokenRefreshTimer) {
|
|
454
|
+
clearTimeout(this.tokenRefreshTimer);
|
|
455
|
+
this.tokenRefreshTimer = null;
|
|
456
|
+
}
|
|
457
|
+
if (this.eventSource) {
|
|
458
|
+
this.eventSource.close();
|
|
459
|
+
this.eventSource = null;
|
|
460
|
+
}
|
|
461
|
+
this.sseToken = null;
|
|
462
|
+
this.sseTokenExpiresAt = 0;
|
|
463
|
+
this.setConnected(false);
|
|
464
|
+
this.handlers.clear();
|
|
465
|
+
this.anyHandlers.clear();
|
|
466
|
+
this.errorHandlers.clear();
|
|
467
|
+
this.connectionHandlers.clear();
|
|
468
|
+
}
|
|
469
|
+
/** Whether the SSE connection is currently open. */
|
|
470
|
+
get connected() {
|
|
471
|
+
return this._connected;
|
|
472
|
+
}
|
|
473
|
+
// ── Internal ──
|
|
474
|
+
ensureConnected() {
|
|
475
|
+
if (!this.eventSource && !this.destroyed) {
|
|
476
|
+
this.connect();
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
handleMessage(event) {
|
|
480
|
+
if (event.lastEventId) {
|
|
481
|
+
this.lastEventId = event.lastEventId;
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const parsed = JSON.parse(event.data);
|
|
485
|
+
if (parsed.type === "connected") return;
|
|
486
|
+
const realtimeEvent = {
|
|
487
|
+
type: parsed.type,
|
|
488
|
+
id: event.lastEventId || "",
|
|
489
|
+
data: parsed.data || parsed,
|
|
490
|
+
timestamp: parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
491
|
+
teamId: parsed.teamId || ""
|
|
492
|
+
};
|
|
493
|
+
this.dispatch(realtimeEvent);
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
dispatch(event) {
|
|
498
|
+
const exactSet = this.handlers.get(event.type);
|
|
499
|
+
if (exactSet) {
|
|
500
|
+
for (const handler of exactSet) handler(event);
|
|
501
|
+
}
|
|
502
|
+
for (const [pattern, handlerSet] of this.handlers) {
|
|
503
|
+
if (pattern.endsWith(".*")) {
|
|
504
|
+
const prefix = pattern.slice(0, -2);
|
|
505
|
+
if (event.type.startsWith(prefix + ".") && pattern !== event.type) {
|
|
506
|
+
for (const handler of handlerSet) handler(event);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
for (const handler of this.anyHandlers) handler(event);
|
|
511
|
+
}
|
|
512
|
+
scheduleReconnect() {
|
|
513
|
+
this.retryCount++;
|
|
514
|
+
const delay = Math.min(500 * Math.pow(2, this.retryCount - 1), 8e3) + Math.random() * 200;
|
|
515
|
+
this.retryTimer = setTimeout(() => {
|
|
516
|
+
this.retryTimer = null;
|
|
517
|
+
if (!this.destroyed) {
|
|
518
|
+
this.connect();
|
|
519
|
+
}
|
|
520
|
+
}, delay);
|
|
521
|
+
}
|
|
522
|
+
setConnected(value) {
|
|
523
|
+
if (this._connected !== value) {
|
|
524
|
+
this._connected = value;
|
|
525
|
+
for (const handler of this.connectionHandlers) handler(value);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
emitError(error) {
|
|
529
|
+
for (const handler of this.errorHandlers) handler(error);
|
|
530
|
+
}
|
|
531
|
+
// ── SSE Token Exchange ──
|
|
532
|
+
/** Whether we hold a token that won't expire for at least 30 seconds. */
|
|
533
|
+
hasFreshSseToken() {
|
|
534
|
+
return !!this.sseToken && this.sseTokenExpiresAt - Date.now() > 3e4;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Exchange the secret API key for a short-lived SSE token via
|
|
538
|
+
* POST /events/token. Schedules automatic refresh before expiry.
|
|
539
|
+
*/
|
|
540
|
+
async exchangeToken() {
|
|
541
|
+
const response = await this.httpClient.request("POST", "/events/token");
|
|
542
|
+
const { token, expiresAt, expiresIn } = response.data;
|
|
543
|
+
this.sseToken = token;
|
|
544
|
+
this.sseTokenExpiresAt = new Date(expiresAt).getTime();
|
|
545
|
+
const refreshIn = Math.max((expiresIn - 30) * 1e3, expiresIn / 2 * 1e3);
|
|
546
|
+
this.scheduleTokenRefresh(refreshIn);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Schedule a background token refresh. On success the existing connection
|
|
550
|
+
* keeps working (tokens are validated at connect time). On the next
|
|
551
|
+
* reconnect the fresh token will be used automatically.
|
|
552
|
+
*/
|
|
553
|
+
scheduleTokenRefresh(delayMs) {
|
|
554
|
+
if (this.tokenRefreshTimer) clearTimeout(this.tokenRefreshTimer);
|
|
555
|
+
this.tokenRefreshTimer = setTimeout(() => {
|
|
556
|
+
this.tokenRefreshTimer = null;
|
|
557
|
+
if (this.destroyed) return;
|
|
558
|
+
this.exchangeToken().catch(() => {
|
|
559
|
+
});
|
|
560
|
+
}, delayMs);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/subscription.ts
|
|
565
|
+
var RESOURCE_EVENT_MAP = {
|
|
566
|
+
"/tickets": ["ticket.created", "ticket.updated", "ticket.closed", "ticket.assigned"],
|
|
567
|
+
"/customers": ["customer.created", "customer.updated"],
|
|
568
|
+
"/articles": ["article.created", "article.updated", "article.published"],
|
|
569
|
+
"/messages": ["message.created", "message.updated"],
|
|
570
|
+
"/notifications": ["notification.created"],
|
|
571
|
+
"/mentions": ["mention.created"],
|
|
572
|
+
"/game/boss": ["boss.spawned", "boss.damaged", "boss.defeated"],
|
|
573
|
+
"/game/player": ["achievement.unlocked", "quest.completed", "level.up", "player.updated"],
|
|
574
|
+
"/game/leaderboard": [
|
|
575
|
+
"leaderboard.updated",
|
|
576
|
+
"achievement.unlocked",
|
|
577
|
+
"quest.completed",
|
|
578
|
+
"level.up"
|
|
579
|
+
],
|
|
580
|
+
"/game/activity": [
|
|
581
|
+
"boss.spawned",
|
|
582
|
+
"boss.defeated",
|
|
583
|
+
"achievement.unlocked",
|
|
584
|
+
"level.up",
|
|
585
|
+
"quest.completed"
|
|
586
|
+
]
|
|
587
|
+
};
|
|
588
|
+
function getEntityId(item) {
|
|
589
|
+
if (!item || typeof item !== "object") return void 0;
|
|
590
|
+
const obj = item;
|
|
591
|
+
return obj.id ?? obj.userId ?? void 0;
|
|
592
|
+
}
|
|
593
|
+
var Subscription = class {
|
|
594
|
+
constructor(realtime, fetchFn, resourcePath, filterFn) {
|
|
595
|
+
this.realtime = realtime;
|
|
596
|
+
this.fetchFn = fetchFn;
|
|
597
|
+
this.resourcePath = resourcePath;
|
|
598
|
+
this.filterFn = filterFn;
|
|
599
|
+
this._data = [];
|
|
600
|
+
this._loading = true;
|
|
601
|
+
this._error = null;
|
|
602
|
+
this.changeHandlers = /* @__PURE__ */ new Set();
|
|
603
|
+
this.errorEventHandlers = /* @__PURE__ */ new Set();
|
|
604
|
+
this.boundEventHandler = null;
|
|
605
|
+
this.destroyed = false;
|
|
606
|
+
/** Generation counter to prevent stale refresh() from overwriting SSE updates. */
|
|
607
|
+
this.generation = 0;
|
|
608
|
+
this.init();
|
|
609
|
+
}
|
|
610
|
+
/** Current data array (updated in real-time). */
|
|
611
|
+
get data() {
|
|
612
|
+
return this._data;
|
|
613
|
+
}
|
|
614
|
+
/** True during initial data fetch. */
|
|
615
|
+
get loading() {
|
|
616
|
+
return this._loading;
|
|
617
|
+
}
|
|
618
|
+
/** Last error, if any. */
|
|
619
|
+
get error() {
|
|
620
|
+
return this._error;
|
|
621
|
+
}
|
|
622
|
+
on(event, handler) {
|
|
623
|
+
if (event === "change") {
|
|
624
|
+
this.changeHandlers.add(handler);
|
|
625
|
+
} else if (event === "error") {
|
|
626
|
+
this.errorEventHandlers.add(handler);
|
|
627
|
+
}
|
|
628
|
+
return this;
|
|
629
|
+
}
|
|
630
|
+
/** Manually refresh data from the server. */
|
|
631
|
+
async refresh() {
|
|
632
|
+
const gen = ++this.generation;
|
|
633
|
+
try {
|
|
634
|
+
const response = await this.fetchFn();
|
|
635
|
+
if (gen !== this.generation) return;
|
|
636
|
+
this._data = response.data;
|
|
637
|
+
this._error = null;
|
|
638
|
+
this.emitChange();
|
|
639
|
+
} catch (err) {
|
|
640
|
+
if (gen !== this.generation) return;
|
|
641
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
642
|
+
this.emitError(this._error);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Apply an optimistic update to the local data.
|
|
647
|
+
* Returns a rollback function that reverts the change.
|
|
648
|
+
*/
|
|
649
|
+
optimisticUpdate(id, changes) {
|
|
650
|
+
const snapshot = [...this._data];
|
|
651
|
+
const index = this._data.findIndex((item) => getEntityId(item) === id);
|
|
652
|
+
if (index !== -1) {
|
|
653
|
+
this._data[index] = { ...this._data[index], ...changes };
|
|
654
|
+
if (this.filterFn && !this.filterFn(this._data[index])) {
|
|
655
|
+
this._data.splice(index, 1);
|
|
656
|
+
}
|
|
657
|
+
this.emitChange();
|
|
658
|
+
}
|
|
659
|
+
return () => {
|
|
660
|
+
this._data = snapshot;
|
|
661
|
+
this.emitChange();
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/** Stop the subscription and clean up all listeners. */
|
|
665
|
+
unsubscribe() {
|
|
666
|
+
this.destroyed = true;
|
|
667
|
+
if (this.boundEventHandler) {
|
|
668
|
+
this.realtime.offAny(this.boundEventHandler);
|
|
669
|
+
this.boundEventHandler = null;
|
|
670
|
+
}
|
|
671
|
+
this.changeHandlers.clear();
|
|
672
|
+
this.errorEventHandlers.clear();
|
|
673
|
+
}
|
|
674
|
+
// ── Internal ──
|
|
675
|
+
async init() {
|
|
676
|
+
this.boundEventHandler = (event) => this.handleEvent(event);
|
|
677
|
+
this.realtime.onAny(this.boundEventHandler);
|
|
678
|
+
const gen = ++this.generation;
|
|
679
|
+
try {
|
|
680
|
+
const response = await this.fetchFn();
|
|
681
|
+
if (gen !== this.generation) return;
|
|
682
|
+
this._data = response.data;
|
|
683
|
+
this._loading = false;
|
|
684
|
+
this._error = null;
|
|
685
|
+
this.emitChange();
|
|
686
|
+
} catch (err) {
|
|
687
|
+
if (gen !== this.generation) return;
|
|
688
|
+
this._loading = false;
|
|
689
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
690
|
+
this.emitError(this._error);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
handleEvent(event) {
|
|
694
|
+
if (this.destroyed) return;
|
|
695
|
+
const eventTypes = RESOURCE_EVENT_MAP[this.resourcePath];
|
|
696
|
+
if (eventTypes) {
|
|
697
|
+
const isRelevant = eventTypes.some((t) => {
|
|
698
|
+
const prefix = t.split(".")[0];
|
|
699
|
+
return event.type.startsWith(prefix + ".");
|
|
700
|
+
});
|
|
701
|
+
if (!isRelevant) return;
|
|
702
|
+
} else {
|
|
703
|
+
const eventPrefix = event.type.split(".")[0];
|
|
704
|
+
const segments = this.resourcePath.replace(/^\//, "").split("/");
|
|
705
|
+
const resourceName = segments[segments.length - 1];
|
|
706
|
+
if (resourceName !== eventPrefix && resourceName !== eventPrefix + "s") return;
|
|
707
|
+
}
|
|
708
|
+
const entityData = this.extractEntity(event);
|
|
709
|
+
if (!entityData || !getEntityId(entityData)) return;
|
|
710
|
+
this.generation++;
|
|
711
|
+
if (event.type.endsWith(".created")) {
|
|
712
|
+
this.handleCreated(entityData);
|
|
713
|
+
} else if (event.type.endsWith(".deleted")) {
|
|
714
|
+
this.handleDeleted(entityData);
|
|
715
|
+
} else if (event.type.endsWith(".updated") || event.type.endsWith(".assigned") || event.type.endsWith(".closed")) {
|
|
716
|
+
this.handleUpdated(entityData);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
handleCreated(entity) {
|
|
720
|
+
if (this.filterFn && !this.filterFn(entity)) return;
|
|
721
|
+
const entityId = getEntityId(entity);
|
|
722
|
+
if (entityId && this._data.some((item) => getEntityId(item) === entityId)) return;
|
|
723
|
+
this._data = [entity, ...this._data];
|
|
724
|
+
this.emitChange();
|
|
725
|
+
}
|
|
726
|
+
handleDeleted(entity) {
|
|
727
|
+
const entityId = getEntityId(entity);
|
|
728
|
+
if (!entityId) return;
|
|
729
|
+
const index = this._data.findIndex((item) => getEntityId(item) === entityId);
|
|
730
|
+
if (index !== -1) {
|
|
731
|
+
this._data = [...this._data.slice(0, index), ...this._data.slice(index + 1)];
|
|
732
|
+
this.emitChange();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
handleUpdated(entity) {
|
|
736
|
+
const entityId = getEntityId(entity);
|
|
737
|
+
const index = entityId ? this._data.findIndex((item) => getEntityId(item) === entityId) : -1;
|
|
738
|
+
if (index !== -1) {
|
|
739
|
+
const updated = { ...this._data[index], ...entity };
|
|
740
|
+
if (this.filterFn && !this.filterFn(updated)) {
|
|
741
|
+
this._data = [...this._data.slice(0, index), ...this._data.slice(index + 1)];
|
|
742
|
+
} else {
|
|
743
|
+
this._data = [...this._data.slice(0, index), updated, ...this._data.slice(index + 1)];
|
|
744
|
+
}
|
|
745
|
+
this.emitChange();
|
|
746
|
+
} else if (this.filterFn && this.filterFn(entity)) {
|
|
747
|
+
this._data = [entity, ...this._data];
|
|
748
|
+
this.emitChange();
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
extractEntity(event) {
|
|
752
|
+
const data = event.data;
|
|
753
|
+
const resourceKey = this.resourcePath.replace(/^\//, "").replace(/s$/, "");
|
|
754
|
+
if (data[resourceKey] && typeof data[resourceKey] === "object") {
|
|
755
|
+
return data[resourceKey];
|
|
756
|
+
}
|
|
757
|
+
if (data.id) return data;
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
emitChange() {
|
|
761
|
+
for (const handler of this.changeHandlers) handler(this._data);
|
|
762
|
+
}
|
|
763
|
+
emitError(error) {
|
|
764
|
+
for (const handler of this.errorEventHandlers) handler(error);
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
215
768
|
// src/resources/base.ts
|
|
216
769
|
var BaseResource = class {
|
|
217
770
|
constructor(client, basePath) {
|
|
@@ -270,6 +823,22 @@ var BaseResource = class {
|
|
|
270
823
|
page++;
|
|
271
824
|
}
|
|
272
825
|
}
|
|
826
|
+
/**
|
|
827
|
+
* Subscribe to real-time changes for this resource type.
|
|
828
|
+
*
|
|
829
|
+
* Fetches initial data, then auto-updates via SSE events.
|
|
830
|
+
* Requires a `CStarRealtime` instance (available from `CStarClient.realtime`).
|
|
831
|
+
*
|
|
832
|
+
* ```typescript
|
|
833
|
+
* const sub = cstar.tickets.subscribe(cstar.realtime, { status: 'open' });
|
|
834
|
+
* sub.on('change', (tickets) => console.log(tickets));
|
|
835
|
+
* // Later: sub.unsubscribe();
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
838
|
+
subscribe(realtime, params, filterFn) {
|
|
839
|
+
const fetchFn = () => this.list(params);
|
|
840
|
+
return new Subscription(realtime, fetchFn, this.basePath, filterFn);
|
|
841
|
+
}
|
|
273
842
|
/** Convert params to string values for query string serialization. */
|
|
274
843
|
serializeQuery(params) {
|
|
275
844
|
const result = {};
|
|
@@ -354,7 +923,7 @@ var WebhooksResource = class extends BaseResource {
|
|
|
354
923
|
);
|
|
355
924
|
}
|
|
356
925
|
/** Subscribe to an event (REST Hooks / Zapier protocol). */
|
|
357
|
-
async
|
|
926
|
+
async restSubscribe(params, options) {
|
|
358
927
|
return this.client.request("POST", "/webhooks/subscribe", {
|
|
359
928
|
body: params,
|
|
360
929
|
idempotencyKey: options?.idempotencyKey
|
|
@@ -1347,6 +1916,72 @@ var GameResource = class {
|
|
|
1347
1916
|
async config() {
|
|
1348
1917
|
return this.client.request("GET", "/game/config");
|
|
1349
1918
|
}
|
|
1919
|
+
// ── Real-time Subscriptions ──
|
|
1920
|
+
/**
|
|
1921
|
+
* Subscribe to live boss battle state.
|
|
1922
|
+
* Updates on boss.spawned, boss.damaged, boss.defeated events.
|
|
1923
|
+
*/
|
|
1924
|
+
subscribeBoss(realtime) {
|
|
1925
|
+
const fetchFn = async () => {
|
|
1926
|
+
const res = await this.boss.current();
|
|
1927
|
+
return {
|
|
1928
|
+
success: true,
|
|
1929
|
+
data: res.data.active ? [res.data] : [],
|
|
1930
|
+
pagination: { total: res.data.active ? 1 : 0, page: 1, pageSize: 1, hasMore: false },
|
|
1931
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1932
|
+
};
|
|
1933
|
+
};
|
|
1934
|
+
return new Subscription(realtime, fetchFn, "/game/boss");
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Subscribe to player stats (XP, gold, level, streaks).
|
|
1938
|
+
* Updates on achievement.unlocked, quest.completed, level.up events.
|
|
1939
|
+
*/
|
|
1940
|
+
subscribePlayer(realtime, userId) {
|
|
1941
|
+
const fetchFn = async () => {
|
|
1942
|
+
const res = await this.player({ userId });
|
|
1943
|
+
return {
|
|
1944
|
+
success: true,
|
|
1945
|
+
data: [res.data],
|
|
1946
|
+
pagination: { total: 1, page: 1, pageSize: 1, hasMore: false },
|
|
1947
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1948
|
+
};
|
|
1949
|
+
};
|
|
1950
|
+
return new Subscription(realtime, fetchFn, "/game/player");
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Subscribe to the team leaderboard.
|
|
1954
|
+
* Updates when scores change.
|
|
1955
|
+
*/
|
|
1956
|
+
subscribeLeaderboard(realtime, params) {
|
|
1957
|
+
const fetchFn = async () => {
|
|
1958
|
+
const res = await this.leaderboard(params);
|
|
1959
|
+
return {
|
|
1960
|
+
success: true,
|
|
1961
|
+
data: res.data,
|
|
1962
|
+
pagination: { total: res.data.length, page: 1, pageSize: 100, hasMore: false },
|
|
1963
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1964
|
+
};
|
|
1965
|
+
};
|
|
1966
|
+
return new Subscription(realtime, fetchFn, "/game/leaderboard");
|
|
1967
|
+
}
|
|
1968
|
+
/**
|
|
1969
|
+
* Subscribe to team activity feed (achievements, boss defeats, level ups).
|
|
1970
|
+
* Rolling feed of recent game events.
|
|
1971
|
+
*/
|
|
1972
|
+
subscribeActivity(realtime) {
|
|
1973
|
+
const fetchFn = async () => {
|
|
1974
|
+
const res = await this.activity.list({ limit: 50 });
|
|
1975
|
+
const data = res.data.data || [];
|
|
1976
|
+
return {
|
|
1977
|
+
success: true,
|
|
1978
|
+
data,
|
|
1979
|
+
pagination: { total: data.length, page: 1, pageSize: 50, hasMore: false },
|
|
1980
|
+
meta: { requestId: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1981
|
+
};
|
|
1982
|
+
};
|
|
1983
|
+
return new Subscription(realtime, fetchFn, "/game/activity");
|
|
1984
|
+
}
|
|
1350
1985
|
};
|
|
1351
1986
|
|
|
1352
1987
|
// src/resources/ai.ts
|
|
@@ -1409,53 +2044,191 @@ var BillingResource = class {
|
|
|
1409
2044
|
}
|
|
1410
2045
|
};
|
|
1411
2046
|
|
|
2047
|
+
// src/offline.ts
|
|
2048
|
+
var STORAGE_KEY = "cstar_offline_queue";
|
|
2049
|
+
var OfflineQueue = class {
|
|
2050
|
+
constructor(httpClient, options) {
|
|
2051
|
+
this.httpClient = httpClient;
|
|
2052
|
+
this.items = [];
|
|
2053
|
+
this.autoFlushEnabled = false;
|
|
2054
|
+
this.onlineHandler = null;
|
|
2055
|
+
this.useLocalStorage = options?.storage === "localStorage" && typeof globalThis !== "undefined" && typeof globalThis.localStorage !== "undefined";
|
|
2056
|
+
if (this.useLocalStorage) {
|
|
2057
|
+
this.loadFromStorage();
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
/** Queue a mutation for later execution. Returns a queue item ID. */
|
|
2061
|
+
enqueue(method, path, body) {
|
|
2062
|
+
const id = `q_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
2063
|
+
const item = {
|
|
2064
|
+
id,
|
|
2065
|
+
method,
|
|
2066
|
+
path,
|
|
2067
|
+
body,
|
|
2068
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2069
|
+
};
|
|
2070
|
+
this.items.push(item);
|
|
2071
|
+
this.saveToStorage();
|
|
2072
|
+
return id;
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Process all queued mutations in order. Returns results for each item.
|
|
2076
|
+
* Failed items are re-queued at the front to preserve ordering.
|
|
2077
|
+
* Items enqueued during flush are appended after re-queued failures.
|
|
2078
|
+
*/
|
|
2079
|
+
async flush() {
|
|
2080
|
+
const results = [];
|
|
2081
|
+
const itemsToProcess = [...this.items];
|
|
2082
|
+
this.items = [];
|
|
2083
|
+
this.saveToStorage();
|
|
2084
|
+
const failedItems = [];
|
|
2085
|
+
for (const item of itemsToProcess) {
|
|
2086
|
+
try {
|
|
2087
|
+
const data = await this.httpClient.request(item.method, item.path, {
|
|
2088
|
+
body: item.body
|
|
2089
|
+
});
|
|
2090
|
+
results.push({ id: item.id, success: true, data });
|
|
2091
|
+
} catch (err) {
|
|
2092
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2093
|
+
results.push({ id: item.id, success: false, error: message });
|
|
2094
|
+
const statusCode = err.statusCode;
|
|
2095
|
+
const isRetryable = !statusCode || statusCode >= 500;
|
|
2096
|
+
if (isRetryable) {
|
|
2097
|
+
failedItems.push(item);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
this.items = [...failedItems, ...this.items];
|
|
2102
|
+
this.saveToStorage();
|
|
2103
|
+
return results;
|
|
2104
|
+
}
|
|
2105
|
+
/** Get pending queue items. */
|
|
2106
|
+
get pending() {
|
|
2107
|
+
return [...this.items];
|
|
2108
|
+
}
|
|
2109
|
+
/** Number of items in the queue. */
|
|
2110
|
+
get size() {
|
|
2111
|
+
return this.items.length;
|
|
2112
|
+
}
|
|
2113
|
+
/** Clear all pending items. */
|
|
2114
|
+
clear() {
|
|
2115
|
+
this.items = [];
|
|
2116
|
+
this.saveToStorage();
|
|
2117
|
+
}
|
|
2118
|
+
/**
|
|
2119
|
+
* Enable auto-flush when the browser goes online.
|
|
2120
|
+
* Only works in browser environments with the `online` event.
|
|
2121
|
+
*/
|
|
2122
|
+
enableAutoFlush() {
|
|
2123
|
+
if (this.autoFlushEnabled) return;
|
|
2124
|
+
if (typeof globalThis === "undefined" || typeof globalThis.addEventListener !== "function") {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
this.autoFlushEnabled = true;
|
|
2128
|
+
this.onlineHandler = () => {
|
|
2129
|
+
if (this.items.length > 0) {
|
|
2130
|
+
this.flush().catch(() => {
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
globalThis.addEventListener("online", this.onlineHandler);
|
|
2135
|
+
}
|
|
2136
|
+
/** Disable auto-flush and clean up event listeners. */
|
|
2137
|
+
disableAutoFlush() {
|
|
2138
|
+
if (!this.autoFlushEnabled || !this.onlineHandler) return;
|
|
2139
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.removeEventListener === "function") {
|
|
2140
|
+
globalThis.removeEventListener("online", this.onlineHandler);
|
|
2141
|
+
}
|
|
2142
|
+
this.onlineHandler = null;
|
|
2143
|
+
this.autoFlushEnabled = false;
|
|
2144
|
+
}
|
|
2145
|
+
saveToStorage() {
|
|
2146
|
+
if (!this.useLocalStorage) return;
|
|
2147
|
+
try {
|
|
2148
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.items));
|
|
2149
|
+
} catch {
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
loadFromStorage() {
|
|
2153
|
+
try {
|
|
2154
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
2155
|
+
if (raw) {
|
|
2156
|
+
this.items = JSON.parse(raw);
|
|
2157
|
+
}
|
|
2158
|
+
} catch {
|
|
2159
|
+
this.items = [];
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
};
|
|
2163
|
+
|
|
1412
2164
|
// src/index.ts
|
|
1413
2165
|
var CStarClient = class {
|
|
1414
2166
|
constructor(config) {
|
|
1415
|
-
this.
|
|
1416
|
-
this.tickets = new TicketsResource(this.
|
|
1417
|
-
this.customers = new CustomersResource(this.
|
|
1418
|
-
this.articles = new ArticlesResource(this.
|
|
1419
|
-
this.webhooks = new WebhooksResource(this.
|
|
1420
|
-
this.automations = new AutomationsResource(this.
|
|
1421
|
-
this.categories = new CategoriesResource(this.
|
|
2167
|
+
this._httpClient = new HttpClient(config);
|
|
2168
|
+
this.tickets = new TicketsResource(this._httpClient);
|
|
2169
|
+
this.customers = new CustomersResource(this._httpClient);
|
|
2170
|
+
this.articles = new ArticlesResource(this._httpClient);
|
|
2171
|
+
this.webhooks = new WebhooksResource(this._httpClient);
|
|
2172
|
+
this.automations = new AutomationsResource(this._httpClient);
|
|
2173
|
+
this.categories = new CategoriesResource(this._httpClient);
|
|
1422
2174
|
this.community = {
|
|
1423
|
-
posts: new CommunityPostsResource(this.
|
|
1424
|
-
topics: new CommunityTopicsResource(this.
|
|
2175
|
+
posts: new CommunityPostsResource(this._httpClient),
|
|
2176
|
+
topics: new CommunityTopicsResource(this._httpClient)
|
|
1425
2177
|
};
|
|
1426
|
-
this.members = new MembersResource(this.
|
|
1427
|
-
this.settings = new SettingsResource(this.
|
|
1428
|
-
this.notifications = new NotificationsResource(this.
|
|
1429
|
-
this.mentions = new MentionsResource(this.
|
|
1430
|
-
this.attachments = new AttachmentsResource(this.
|
|
1431
|
-
this.customFields = new CustomFieldsResource(this.
|
|
1432
|
-
this.sla = new SlaResource(this.
|
|
1433
|
-
this.analytics = new AnalyticsResource(this.
|
|
1434
|
-
this.bulk = new BulkResource(this.
|
|
1435
|
-
this.export = new ExportResource(this.
|
|
1436
|
-
this.import = new ImportResource(this.
|
|
1437
|
-
this.auditLog = new AuditLogResource(this.
|
|
1438
|
-
this.search = new SearchResource(this.
|
|
1439
|
-
this.tags = new TagsResource(this.
|
|
1440
|
-
this.activity = new ActivityResource(this.
|
|
1441
|
-
this.customerGroups = new CustomerGroupsResource(this.
|
|
1442
|
-
this.game = new GameResource(this.
|
|
1443
|
-
this.ai = new AiResource(this.
|
|
1444
|
-
this.views = new ViewsResource(this.
|
|
1445
|
-
this.billing = new BillingResource(this.
|
|
2178
|
+
this.members = new MembersResource(this._httpClient);
|
|
2179
|
+
this.settings = new SettingsResource(this._httpClient);
|
|
2180
|
+
this.notifications = new NotificationsResource(this._httpClient);
|
|
2181
|
+
this.mentions = new MentionsResource(this._httpClient);
|
|
2182
|
+
this.attachments = new AttachmentsResource(this._httpClient);
|
|
2183
|
+
this.customFields = new CustomFieldsResource(this._httpClient);
|
|
2184
|
+
this.sla = new SlaResource(this._httpClient);
|
|
2185
|
+
this.analytics = new AnalyticsResource(this._httpClient);
|
|
2186
|
+
this.bulk = new BulkResource(this._httpClient);
|
|
2187
|
+
this.export = new ExportResource(this._httpClient);
|
|
2188
|
+
this.import = new ImportResource(this._httpClient);
|
|
2189
|
+
this.auditLog = new AuditLogResource(this._httpClient);
|
|
2190
|
+
this.search = new SearchResource(this._httpClient);
|
|
2191
|
+
this.tags = new TagsResource(this._httpClient);
|
|
2192
|
+
this.activity = new ActivityResource(this._httpClient);
|
|
2193
|
+
this.customerGroups = new CustomerGroupsResource(this._httpClient);
|
|
2194
|
+
this.game = new GameResource(this._httpClient);
|
|
2195
|
+
this.ai = new AiResource(this._httpClient);
|
|
2196
|
+
this.views = new ViewsResource(this._httpClient);
|
|
2197
|
+
this.billing = new BillingResource(this._httpClient);
|
|
2198
|
+
this.realtime = new CStarRealtime(this._httpClient);
|
|
2199
|
+
if (config.offline) {
|
|
2200
|
+
const queueOpts = typeof config.offline === "object" ? config.offline : void 0;
|
|
2201
|
+
this.offlineQueue = new OfflineQueue(this._httpClient, queueOpts);
|
|
2202
|
+
this.offlineQueue.enableAutoFlush();
|
|
2203
|
+
} else {
|
|
2204
|
+
this.offlineQueue = null;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
/** Disconnect the realtime stream and clean up. */
|
|
2208
|
+
destroy() {
|
|
2209
|
+
this.realtime.disconnect();
|
|
2210
|
+
if (this.offlineQueue) {
|
|
2211
|
+
this.offlineQueue.disableAutoFlush();
|
|
2212
|
+
}
|
|
1446
2213
|
}
|
|
1447
2214
|
/** Whether this client is using test mode keys (sk_test_* or pk_test_*). */
|
|
1448
2215
|
get isTestMode() {
|
|
1449
|
-
return this.
|
|
2216
|
+
return this._httpClient.environment === "test";
|
|
1450
2217
|
}
|
|
1451
2218
|
};
|
|
1452
2219
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1453
2220
|
0 && (module.exports = {
|
|
1454
2221
|
CStarAuthenticationError,
|
|
1455
2222
|
CStarClient,
|
|
2223
|
+
CStarConflictError,
|
|
1456
2224
|
CStarError,
|
|
1457
2225
|
CStarNotFoundError,
|
|
2226
|
+
CStarPermissionError,
|
|
1458
2227
|
CStarRateLimitError,
|
|
2228
|
+
CStarRealtime,
|
|
2229
|
+
CStarServerError,
|
|
1459
2230
|
CStarValidationError,
|
|
1460
|
-
ErrorType
|
|
2231
|
+
ErrorType,
|
|
2232
|
+
OfflineQueue,
|
|
2233
|
+
Subscription
|
|
1461
2234
|
});
|