@cstar.help/js 0.1.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 +202 -0
- package/dist/chat/index.cjs +238 -0
- package/dist/chat/index.d.cts +172 -0
- package/dist/chat/index.d.ts +172 -0
- package/dist/chat/index.js +213 -0
- package/dist/index.cjs +364 -0
- package/dist/index.d.cts +519 -0
- package/dist/index.d.ts +519 -0
- package/dist/index.js +331 -0
- package/dist/library/index.cjs +97 -0
- package/dist/library/index.d.cts +82 -0
- package/dist/library/index.d.ts +82 -0
- package/dist/library/index.js +72 -0
- package/dist/webhook/index.cjs +84 -0
- package/dist/webhook/index.d.cts +163 -0
- package/dist/webhook/index.d.ts +163 -0
- package/dist/webhook/index.js +62 -0
- package/package.json +85 -0
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# @cstar.help/js
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the [cStar](https://cstar.help) customer support platform.
|
|
4
|
+
|
|
5
|
+
- **Zero dependencies** — works with Node.js 18+ native `fetch`
|
|
6
|
+
- **TypeScript-first** — full type definitions included
|
|
7
|
+
- **Four entry points** — tree-shake what you don't need
|
|
8
|
+
- **ESM + CJS** — works everywhere
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @cstar.help/js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### Server-side API Client
|
|
19
|
+
|
|
20
|
+
Manage tickets, customers, articles, and webhooks with your secret API key.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { CStarClient } from '@cstar.help/js';
|
|
24
|
+
|
|
25
|
+
const cstar = new CStarClient({
|
|
26
|
+
apiKey: 'sk_live_abc123...',
|
|
27
|
+
teamId: 'tm_your_team_id',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// List open tickets
|
|
31
|
+
const { data: tickets } = await cstar.tickets.list({ status: 'open' });
|
|
32
|
+
|
|
33
|
+
// Create a ticket
|
|
34
|
+
const { data: ticket } = await cstar.tickets.create({
|
|
35
|
+
title: 'Help with billing',
|
|
36
|
+
customerId: 'cus_abc123',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Get a customer with expanded fields
|
|
40
|
+
const { data: customer } = await cstar.customers.get('cus_abc123');
|
|
41
|
+
|
|
42
|
+
// Auto-paginate through all tickets
|
|
43
|
+
for await (const ticket of cstar.tickets.listAutoPaginating({ status: 'open' })) {
|
|
44
|
+
console.log(ticket.title);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Customer Chat (Widget SDK)
|
|
49
|
+
|
|
50
|
+
Build custom chat experiences with identity verification.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { ChatClient } from '@cstar.help/js/chat';
|
|
54
|
+
|
|
55
|
+
const chat = new ChatClient({ teamSlug: 'acme' });
|
|
56
|
+
|
|
57
|
+
// Identify the customer (HMAC signature from your server)
|
|
58
|
+
await chat.identify(
|
|
59
|
+
{ externalId: 'usr_123', email: 'jane@example.com', name: 'Jane', timestamp: Date.now() / 1000 },
|
|
60
|
+
hmacSignature
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// List conversations
|
|
64
|
+
const { conversations } = await chat.conversations.list();
|
|
65
|
+
|
|
66
|
+
// Send a message
|
|
67
|
+
await chat.messages.send('tkt_abc123', 'Thanks for the help!');
|
|
68
|
+
|
|
69
|
+
// Subscribe to new messages (polling)
|
|
70
|
+
const unsubscribe = chat.onMessage('tkt_abc123', (message) => {
|
|
71
|
+
console.log(`${message.sender_name}: ${message.content}`);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Clean up
|
|
75
|
+
chat.disconnect();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Public Knowledge Base
|
|
79
|
+
|
|
80
|
+
Search and browse your public help center — no authentication required.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { LibraryClient } from '@cstar.help/js/library';
|
|
84
|
+
|
|
85
|
+
const library = new LibraryClient({ teamSlug: 'acme' });
|
|
86
|
+
|
|
87
|
+
// List categories
|
|
88
|
+
const categories = await library.categories();
|
|
89
|
+
|
|
90
|
+
// Search articles
|
|
91
|
+
const results = await library.search('reset password');
|
|
92
|
+
|
|
93
|
+
// Get a specific article
|
|
94
|
+
const article = await library.article('how-to-reset-password');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Webhook Verification
|
|
98
|
+
|
|
99
|
+
Verify incoming webhook signatures on your server.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { constructEvent } from '@cstar.help/js/webhook';
|
|
103
|
+
|
|
104
|
+
// In your webhook handler (Express, Hono, etc.)
|
|
105
|
+
const event = constructEvent(rawBody, signature, 'whsec_your_secret');
|
|
106
|
+
|
|
107
|
+
switch (event.type) {
|
|
108
|
+
case 'ticket.created':
|
|
109
|
+
console.log('New ticket:', event.data.title);
|
|
110
|
+
break;
|
|
111
|
+
case 'customer.created':
|
|
112
|
+
console.log('New customer:', event.data.email);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For edge environments (Cloudflare Workers, Deno), use the async variant:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { constructEventAsync } from '@cstar.help/js/webhook';
|
|
121
|
+
|
|
122
|
+
const event = await constructEventAsync(rawBody, signature, secret);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## API Reference
|
|
126
|
+
|
|
127
|
+
### CStarClient
|
|
128
|
+
|
|
129
|
+
| Resource | Methods |
|
|
130
|
+
|----------|---------|
|
|
131
|
+
| `cstar.tickets` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
132
|
+
| `cstar.tickets.messages` | `.list(ticketId)` `.create(ticketId, params)` |
|
|
133
|
+
| `cstar.customers` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
134
|
+
| `cstar.articles` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.listAutoPaginating()` |
|
|
135
|
+
| `cstar.webhooks` | `.list()` `.get(id)` `.create(params)` `.update(id, params)` `.del(id)` `.test(id)` |
|
|
136
|
+
|
|
137
|
+
### Configuration
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const cstar = new CStarClient({
|
|
141
|
+
apiKey: 'sk_live_...', // Required — secret or publishable key
|
|
142
|
+
teamId: 'tm_...', // Required — your team ID
|
|
143
|
+
baseUrl: 'https://...', // Optional — defaults to https://app.cstar.help
|
|
144
|
+
version: '2026-03-01', // Optional — API version header
|
|
145
|
+
maxRetries: 3, // Optional — retry attempts for 5xx/429
|
|
146
|
+
timeout: 30000, // Optional — request timeout in ms
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Test Mode
|
|
151
|
+
|
|
152
|
+
Test mode is auto-detected from your API key prefix:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Uses test data — no real customers affected
|
|
156
|
+
const cstar = new CStarClient({
|
|
157
|
+
apiKey: 'sk_test_abc123...',
|
|
158
|
+
teamId: 'tm_your_team_id',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log(cstar.isTestMode); // true
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Error Handling
|
|
165
|
+
|
|
166
|
+
All API errors throw typed exceptions:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { CStarError, CStarNotFoundError, CStarValidationError } from '@cstar.help/js';
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await cstar.tickets.get('tkt_nonexistent');
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err instanceof CStarNotFoundError) {
|
|
175
|
+
console.log('Ticket not found:', err.message);
|
|
176
|
+
} else if (err instanceof CStarValidationError) {
|
|
177
|
+
console.log('Invalid param:', err.param);
|
|
178
|
+
} else if (err instanceof CStarError) {
|
|
179
|
+
console.log('API error:', err.code, err.statusCode);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Idempotency
|
|
185
|
+
|
|
186
|
+
Safely retry create operations:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const { data: ticket } = await cstar.tickets.create(
|
|
190
|
+
{ title: 'Billing issue', customerId: 'cus_abc' },
|
|
191
|
+
{ idempotencyKey: 'create-billing-issue-usr123' }
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Requirements
|
|
196
|
+
|
|
197
|
+
- Node.js 18+ (for native `fetch` and `AbortSignal.timeout`)
|
|
198
|
+
- TypeScript 5.0+ (for type definitions)
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/chat/index.ts
|
|
21
|
+
var chat_exports = {};
|
|
22
|
+
__export(chat_exports, {
|
|
23
|
+
ChatClient: () => ChatClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(chat_exports);
|
|
26
|
+
var DEFAULT_BASE_URL = "https://app.cstar.help";
|
|
27
|
+
var DEFAULT_POLLING_INTERVAL = 3e3;
|
|
28
|
+
var ChatClient = class {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.sessionToken = null;
|
|
31
|
+
this.teamId = null;
|
|
32
|
+
this.pollingTimers = /* @__PURE__ */ new Map();
|
|
33
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
34
|
+
this.teamSlug = config.teamSlug;
|
|
35
|
+
this.pollingInterval = config.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
|
|
36
|
+
this.conversations = new ConversationsApi(this);
|
|
37
|
+
this.messages = new MessagesApi(this);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Identify a customer using HMAC signature verification.
|
|
41
|
+
* Must be called before accessing conversations or messages.
|
|
42
|
+
*
|
|
43
|
+
* @param customer - Customer identity fields
|
|
44
|
+
* @param signature - HMAC-SHA256 hex signature of the signed fields
|
|
45
|
+
* @param testMode - Whether to use test mode (relaxed timestamp validation)
|
|
46
|
+
*/
|
|
47
|
+
async identify(customer, signature, testMode = false) {
|
|
48
|
+
const response = await this.fetch(
|
|
49
|
+
"/api/widget/verify-identity",
|
|
50
|
+
{
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: {
|
|
53
|
+
teamSlug: this.teamSlug,
|
|
54
|
+
testMode,
|
|
55
|
+
customer,
|
|
56
|
+
signature
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
this.sessionToken = response.data.sessionToken;
|
|
61
|
+
return response.data;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Subscribe to new messages on a conversation.
|
|
65
|
+
* Falls back to polling when Supabase Realtime is not available.
|
|
66
|
+
*
|
|
67
|
+
* @returns Unsubscribe function
|
|
68
|
+
*/
|
|
69
|
+
onMessage(ticketId, callback) {
|
|
70
|
+
let lastMessageId = null;
|
|
71
|
+
const poll = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const messages = await this.messages.list(ticketId);
|
|
74
|
+
if (messages.length > 0) {
|
|
75
|
+
const latest = messages[messages.length - 1];
|
|
76
|
+
if (lastMessageId && latest.id !== lastMessageId) {
|
|
77
|
+
const lastIdx = messages.findIndex((m) => m.id === lastMessageId);
|
|
78
|
+
const newMessages = lastIdx >= 0 ? messages.slice(lastIdx + 1) : [latest];
|
|
79
|
+
for (const msg of newMessages) {
|
|
80
|
+
callback(msg);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
lastMessageId = latest.id;
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
poll();
|
|
89
|
+
const timer = setInterval(poll, this.pollingInterval);
|
|
90
|
+
this.pollingTimers.set(ticketId, timer);
|
|
91
|
+
return () => {
|
|
92
|
+
clearInterval(timer);
|
|
93
|
+
this.pollingTimers.delete(ticketId);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to typing indicator events.
|
|
98
|
+
* Requires Supabase Realtime — returns a no-op if unavailable.
|
|
99
|
+
*
|
|
100
|
+
* @returns Unsubscribe function
|
|
101
|
+
*/
|
|
102
|
+
onTyping(_ticketId, _callback) {
|
|
103
|
+
return () => {
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** Disconnect all subscriptions and clean up. */
|
|
107
|
+
disconnect() {
|
|
108
|
+
for (const timer of this.pollingTimers.values()) {
|
|
109
|
+
clearInterval(timer);
|
|
110
|
+
}
|
|
111
|
+
this.pollingTimers.clear();
|
|
112
|
+
this.sessionToken = null;
|
|
113
|
+
}
|
|
114
|
+
/** @internal Make an authenticated request to the widget API. */
|
|
115
|
+
async fetch(path, options) {
|
|
116
|
+
const url = this.buildUrl(path, options.query);
|
|
117
|
+
const headers = {
|
|
118
|
+
"Content-Type": "application/json"
|
|
119
|
+
};
|
|
120
|
+
if (this.sessionToken) {
|
|
121
|
+
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
122
|
+
}
|
|
123
|
+
const response = await globalThis.fetch(url, {
|
|
124
|
+
method: options.method,
|
|
125
|
+
headers,
|
|
126
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
127
|
+
});
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
const errorBody = await response.json().catch(() => ({ error: { message: "Request failed" } }));
|
|
130
|
+
throw new Error(errorBody.error?.message ?? `Widget API error: ${response.status}`);
|
|
131
|
+
}
|
|
132
|
+
return response.json();
|
|
133
|
+
}
|
|
134
|
+
/** @internal Get the stored team ID for requests that need it. */
|
|
135
|
+
getTeamId() {
|
|
136
|
+
return this.teamId;
|
|
137
|
+
}
|
|
138
|
+
/** @internal Set team ID from identify response or other source. */
|
|
139
|
+
setTeamId(teamId) {
|
|
140
|
+
this.teamId = teamId;
|
|
141
|
+
}
|
|
142
|
+
buildUrl(path, query) {
|
|
143
|
+
const base = `${this.baseUrl}${path}`;
|
|
144
|
+
if (!query) return base;
|
|
145
|
+
const params = new URLSearchParams();
|
|
146
|
+
for (const [key, value] of Object.entries(query)) {
|
|
147
|
+
if (value !== void 0) params.set(key, value);
|
|
148
|
+
}
|
|
149
|
+
const qs = params.toString();
|
|
150
|
+
return qs ? `${base}?${qs}` : base;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var ConversationsApi = class {
|
|
154
|
+
constructor(chat) {
|
|
155
|
+
this.chat = chat;
|
|
156
|
+
}
|
|
157
|
+
/** List conversations for the identified customer. */
|
|
158
|
+
async list(params) {
|
|
159
|
+
const teamId = this.chat.getTeamId();
|
|
160
|
+
const response = await this.chat.fetch(
|
|
161
|
+
"/api/widget/conversations",
|
|
162
|
+
{
|
|
163
|
+
method: "GET",
|
|
164
|
+
query: {
|
|
165
|
+
teamId: teamId ?? void 0,
|
|
166
|
+
limit: params?.limit?.toString(),
|
|
167
|
+
cursor: params?.cursor
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
return response.data;
|
|
172
|
+
}
|
|
173
|
+
/** Create a new conversation (ticket). */
|
|
174
|
+
async create(params) {
|
|
175
|
+
const teamId = this.chat.getTeamId();
|
|
176
|
+
const response = await this.chat.fetch(
|
|
177
|
+
"/api/widget/tickets",
|
|
178
|
+
{
|
|
179
|
+
method: "POST",
|
|
180
|
+
body: {
|
|
181
|
+
teamId,
|
|
182
|
+
subject: params.subject,
|
|
183
|
+
message: params.message
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
return {
|
|
188
|
+
id: response.data.id,
|
|
189
|
+
ticketNumber: "",
|
|
190
|
+
subject: response.data.subject,
|
|
191
|
+
status: response.data.status,
|
|
192
|
+
messageCount: params.message ? 1 : 0,
|
|
193
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
194
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
195
|
+
closedAt: null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
var MessagesApi = class {
|
|
200
|
+
constructor(chat) {
|
|
201
|
+
this.chat = chat;
|
|
202
|
+
}
|
|
203
|
+
/** List messages for a conversation. */
|
|
204
|
+
async list(ticketId) {
|
|
205
|
+
const teamId = this.chat.getTeamId();
|
|
206
|
+
const response = await this.chat.fetch(
|
|
207
|
+
"/api/widget/messages",
|
|
208
|
+
{
|
|
209
|
+
method: "GET",
|
|
210
|
+
query: {
|
|
211
|
+
teamId: teamId ?? void 0,
|
|
212
|
+
ticketId
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
return response.data.messages;
|
|
217
|
+
}
|
|
218
|
+
/** Send a message to a conversation. */
|
|
219
|
+
async send(ticketId, content) {
|
|
220
|
+
const teamId = this.chat.getTeamId();
|
|
221
|
+
const response = await this.chat.fetch(
|
|
222
|
+
"/api/widget/messages",
|
|
223
|
+
{
|
|
224
|
+
method: "POST",
|
|
225
|
+
body: {
|
|
226
|
+
teamId,
|
|
227
|
+
ticketId,
|
|
228
|
+
content
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
return response.data.message;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
236
|
+
0 && (module.exports = {
|
|
237
|
+
ChatClient
|
|
238
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/** Configuration for the ChatClient. */
|
|
2
|
+
interface ChatConfig {
|
|
3
|
+
/** Team slug (e.g., 'acme'). Used to identify the team. */
|
|
4
|
+
teamSlug: string;
|
|
5
|
+
/** Override the base URL (defaults to https://app.cstar.help). */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
/** Supabase project URL for realtime subscriptions (optional). */
|
|
8
|
+
supabaseUrl?: string;
|
|
9
|
+
/** Supabase anon key for realtime subscriptions (optional). */
|
|
10
|
+
supabaseAnonKey?: string;
|
|
11
|
+
/** Polling interval in ms when Supabase Realtime is not available (defaults to 3000). */
|
|
12
|
+
pollingInterval?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Parameters for identifying a customer via HMAC signature verification. */
|
|
15
|
+
interface IdentifyParams {
|
|
16
|
+
/** Customer ID from your system. */
|
|
17
|
+
externalId: string;
|
|
18
|
+
/** Customer email address. */
|
|
19
|
+
email: string;
|
|
20
|
+
/** Customer display name. */
|
|
21
|
+
name?: string;
|
|
22
|
+
/** Avatar URL (not included in signature). */
|
|
23
|
+
avatarUrl?: string;
|
|
24
|
+
/** Custom metadata (not included in signature). */
|
|
25
|
+
metadata?: Record<string, {
|
|
26
|
+
value: string;
|
|
27
|
+
agentVisible: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
/** Unix timestamp (seconds). Used for replay protection. */
|
|
30
|
+
timestamp: number;
|
|
31
|
+
}
|
|
32
|
+
/** Identity verification response. */
|
|
33
|
+
interface IdentifyResult {
|
|
34
|
+
verified: true;
|
|
35
|
+
customer: {
|
|
36
|
+
id: string;
|
|
37
|
+
externalId: string;
|
|
38
|
+
email: string;
|
|
39
|
+
name: string;
|
|
40
|
+
};
|
|
41
|
+
sessionToken: string;
|
|
42
|
+
expiresAt: string;
|
|
43
|
+
}
|
|
44
|
+
/** A customer conversation (maps to a ticket). */
|
|
45
|
+
interface Conversation {
|
|
46
|
+
id: string;
|
|
47
|
+
ticketNumber: string;
|
|
48
|
+
subject: string;
|
|
49
|
+
status: string;
|
|
50
|
+
messageCount: number;
|
|
51
|
+
createdAt: string;
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
closedAt: string | null;
|
|
54
|
+
}
|
|
55
|
+
/** Paginated conversation list response. */
|
|
56
|
+
interface ConversationListResponse {
|
|
57
|
+
conversations: Conversation[];
|
|
58
|
+
cursor: string | null;
|
|
59
|
+
hasMore: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** A message in a conversation. */
|
|
62
|
+
interface WidgetMessage {
|
|
63
|
+
id: string;
|
|
64
|
+
content: string;
|
|
65
|
+
sender: 'customer' | 'agent';
|
|
66
|
+
sender_name: string;
|
|
67
|
+
created_at: string;
|
|
68
|
+
read_at: string | null;
|
|
69
|
+
attachments: unknown[];
|
|
70
|
+
}
|
|
71
|
+
/** Parameters for listing conversations. */
|
|
72
|
+
interface ConversationListParams {
|
|
73
|
+
limit?: number;
|
|
74
|
+
cursor?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Parameters for creating a new conversation. */
|
|
77
|
+
interface ConversationCreateParams {
|
|
78
|
+
subject: string;
|
|
79
|
+
message?: string;
|
|
80
|
+
}
|
|
81
|
+
/** Typing indicator payload from realtime subscription. */
|
|
82
|
+
interface TypingPayload {
|
|
83
|
+
agent_id: string;
|
|
84
|
+
agent_name: string;
|
|
85
|
+
is_typing: boolean;
|
|
86
|
+
}
|
|
87
|
+
/** Callback for message events. */
|
|
88
|
+
type MessageCallback = (message: WidgetMessage) => void;
|
|
89
|
+
/** Callback for typing indicator events. */
|
|
90
|
+
type TypingCallback = (payload: TypingPayload) => void;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Customer-facing chat client for the cStar widget API.
|
|
94
|
+
*
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const chat = new ChatClient({ teamSlug: 'acme' });
|
|
97
|
+
* await chat.identify(
|
|
98
|
+
* { externalId: 'usr_123', email: 'jane@co.com', name: 'Jane', timestamp: Date.now() / 1000 },
|
|
99
|
+
* 'hmac_signature_hex'
|
|
100
|
+
* );
|
|
101
|
+
* const { conversations } = await chat.conversations.list();
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare class ChatClient {
|
|
105
|
+
private readonly baseUrl;
|
|
106
|
+
private readonly teamSlug;
|
|
107
|
+
private readonly pollingInterval;
|
|
108
|
+
private sessionToken;
|
|
109
|
+
private teamId;
|
|
110
|
+
private pollingTimers;
|
|
111
|
+
/** Sub-resource for managing conversations. */
|
|
112
|
+
readonly conversations: ConversationsApi;
|
|
113
|
+
/** Sub-resource for managing messages. */
|
|
114
|
+
readonly messages: MessagesApi;
|
|
115
|
+
constructor(config: ChatConfig);
|
|
116
|
+
/**
|
|
117
|
+
* Identify a customer using HMAC signature verification.
|
|
118
|
+
* Must be called before accessing conversations or messages.
|
|
119
|
+
*
|
|
120
|
+
* @param customer - Customer identity fields
|
|
121
|
+
* @param signature - HMAC-SHA256 hex signature of the signed fields
|
|
122
|
+
* @param testMode - Whether to use test mode (relaxed timestamp validation)
|
|
123
|
+
*/
|
|
124
|
+
identify(customer: IdentifyParams, signature: string, testMode?: boolean): Promise<IdentifyResult>;
|
|
125
|
+
/**
|
|
126
|
+
* Subscribe to new messages on a conversation.
|
|
127
|
+
* Falls back to polling when Supabase Realtime is not available.
|
|
128
|
+
*
|
|
129
|
+
* @returns Unsubscribe function
|
|
130
|
+
*/
|
|
131
|
+
onMessage(ticketId: string, callback: MessageCallback): () => void;
|
|
132
|
+
/**
|
|
133
|
+
* Subscribe to typing indicator events.
|
|
134
|
+
* Requires Supabase Realtime — returns a no-op if unavailable.
|
|
135
|
+
*
|
|
136
|
+
* @returns Unsubscribe function
|
|
137
|
+
*/
|
|
138
|
+
onTyping(_ticketId: string, _callback: TypingCallback): () => void;
|
|
139
|
+
/** Disconnect all subscriptions and clean up. */
|
|
140
|
+
disconnect(): void;
|
|
141
|
+
/** @internal Make an authenticated request to the widget API. */
|
|
142
|
+
fetch<T>(path: string, options: {
|
|
143
|
+
method: string;
|
|
144
|
+
body?: unknown;
|
|
145
|
+
query?: Record<string, string | undefined>;
|
|
146
|
+
}): Promise<T>;
|
|
147
|
+
/** @internal Get the stored team ID for requests that need it. */
|
|
148
|
+
getTeamId(): string | null;
|
|
149
|
+
/** @internal Set team ID from identify response or other source. */
|
|
150
|
+
setTeamId(teamId: string): void;
|
|
151
|
+
private buildUrl;
|
|
152
|
+
}
|
|
153
|
+
/** Conversations sub-resource on ChatClient. */
|
|
154
|
+
declare class ConversationsApi {
|
|
155
|
+
private readonly chat;
|
|
156
|
+
constructor(chat: ChatClient);
|
|
157
|
+
/** List conversations for the identified customer. */
|
|
158
|
+
list(params?: ConversationListParams): Promise<ConversationListResponse>;
|
|
159
|
+
/** Create a new conversation (ticket). */
|
|
160
|
+
create(params: ConversationCreateParams): Promise<Conversation>;
|
|
161
|
+
}
|
|
162
|
+
/** Messages sub-resource on ChatClient. */
|
|
163
|
+
declare class MessagesApi {
|
|
164
|
+
private readonly chat;
|
|
165
|
+
constructor(chat: ChatClient);
|
|
166
|
+
/** List messages for a conversation. */
|
|
167
|
+
list(ticketId: string): Promise<WidgetMessage[]>;
|
|
168
|
+
/** Send a message to a conversation. */
|
|
169
|
+
send(ticketId: string, content: string): Promise<WidgetMessage>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export { ChatClient, type ChatConfig, type Conversation, type ConversationCreateParams, type ConversationListParams, type ConversationListResponse, type IdentifyParams, type IdentifyResult, type MessageCallback, type TypingCallback, type WidgetMessage };
|