@channlize/core 0.1.0-alpha.1 → 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/CHANGELOG.md +14 -0
- package/README.md +6 -0
- package/dist/index.d.mts +48 -7
- package/dist/index.d.ts +48 -7
- package/dist/index.js +119 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -11
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @channlize/core
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9ddf630: Errors thrown by the client now carry the API's human-readable `message`
|
|
8
|
+
(field messages joined for validation failures) instead of the error code,
|
|
9
|
+
and `ChannlizeError.code` is set from the API's `error` code.
|
|
10
|
+
|
|
11
|
+
`CreateContactResponse` gains the fields the API actually returns:
|
|
12
|
+
`success`, `conversationId` and `message`. The old `id`, `status` and
|
|
13
|
+
`slackThreadUrl` fields were never returned and are deprecated. The MCP
|
|
14
|
+
`submit_contact_form` tool now reports `conversationId`.
|
package/README.md
CHANGED
|
@@ -68,6 +68,12 @@ const messages = await client.getChatMessages(session.id, 50, 0);
|
|
|
68
68
|
|
|
69
69
|
// Close session
|
|
70
70
|
await client.closeChatSession(session.id);
|
|
71
|
+
|
|
72
|
+
// Event tracking (browser)
|
|
73
|
+
await client.trackEvent('page.viewed', { properties: { path: '/pricing' } });
|
|
74
|
+
|
|
75
|
+
// Event tracking (server; set serverApiKey on the client)
|
|
76
|
+
await client.trackServerEvent('webhook.received', { severity: 'warning' });
|
|
71
77
|
```
|
|
72
78
|
|
|
73
79
|
### Error Handling
|
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core types for Channlize SDK
|
|
3
3
|
*/
|
|
4
|
+
/** Severity for tracked events (Slack routing uses project settings). */
|
|
5
|
+
type EventSeverity = 'info' | 'warning' | 'error';
|
|
6
|
+
interface TrackEventOptions {
|
|
7
|
+
properties?: Record<string, unknown>;
|
|
8
|
+
severity?: EventSeverity;
|
|
9
|
+
distinctId?: string;
|
|
10
|
+
pageUrl?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
}
|
|
4
13
|
interface ChannlizeConfig {
|
|
5
|
-
/**
|
|
14
|
+
/** Widget key used for public SDK endpoints (contact form, chat, browser event tracking) */
|
|
6
15
|
apiKey: string;
|
|
7
16
|
/** Base URL for the Channlize API */
|
|
8
17
|
baseUrl?: string;
|
|
9
|
-
/** Project ID */
|
|
18
|
+
/** Project ID (required for server-side `trackServerEvent`) */
|
|
10
19
|
projectId: string;
|
|
11
20
|
/** Request timeout in milliseconds */
|
|
12
21
|
timeout?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Organization API key for server-side `trackServerEvent` (`POST /v1/events`).
|
|
24
|
+
* Do not expose in browser bundles.
|
|
25
|
+
*/
|
|
26
|
+
serverApiKey?: string;
|
|
13
27
|
}
|
|
14
28
|
interface ContactFormData {
|
|
15
29
|
/** Contact's name */
|
|
@@ -81,12 +95,19 @@ interface ApiResponse<T = unknown> {
|
|
|
81
95
|
/** Additional metadata */
|
|
82
96
|
meta?: Record<string, unknown>;
|
|
83
97
|
}
|
|
98
|
+
/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */
|
|
84
99
|
interface CreateContactResponse {
|
|
85
|
-
/**
|
|
100
|
+
/** Whether the submission was accepted */
|
|
101
|
+
success: boolean;
|
|
102
|
+
/** Conversation the submission was filed under */
|
|
103
|
+
conversationId: string;
|
|
104
|
+
/** Human-readable confirmation to show the visitor */
|
|
105
|
+
message: string;
|
|
106
|
+
/** @deprecated Never returned by the API (always undefined). Use `conversationId`. */
|
|
86
107
|
id: string;
|
|
87
|
-
/**
|
|
108
|
+
/** @deprecated Never returned by the API (always undefined). */
|
|
88
109
|
status: 'received' | 'processing' | 'delivered';
|
|
89
|
-
/**
|
|
110
|
+
/** @deprecated Never returned by the API (always undefined). */
|
|
90
111
|
slackThreadUrl?: string;
|
|
91
112
|
}
|
|
92
113
|
interface CreateChatSessionResponse {
|
|
@@ -139,7 +160,9 @@ declare class ChannlizeClient {
|
|
|
139
160
|
*/
|
|
140
161
|
createChatSession(metadata?: Record<string, unknown>): Promise<CreateChatSessionResponse>;
|
|
141
162
|
/**
|
|
142
|
-
* Send a message in a chat session
|
|
163
|
+
* Send a message in a chat session.
|
|
164
|
+
* When `attachments` are provided the request is sent as multipart/form-data
|
|
165
|
+
* so the files are uploaded alongside the message content.
|
|
143
166
|
*/
|
|
144
167
|
sendMessage(sessionId: string, content: string, attachments?: File[]): Promise<SendMessageResponse>;
|
|
145
168
|
/**
|
|
@@ -154,6 +177,24 @@ declare class ChannlizeClient {
|
|
|
154
177
|
* Close a chat session
|
|
155
178
|
*/
|
|
156
179
|
closeChatSession(sessionId: string): Promise<ChatSession>;
|
|
180
|
+
/**
|
|
181
|
+
* Track a product or UX event from the browser (uses widget key → POST /public/events).
|
|
182
|
+
*/
|
|
183
|
+
trackEvent(name: string, options?: TrackEventOptions): Promise<{
|
|
184
|
+
success: boolean;
|
|
185
|
+
id: string;
|
|
186
|
+
}>;
|
|
187
|
+
/**
|
|
188
|
+
* Track an event from a trusted server using the organization API key (POST /v1/events).
|
|
189
|
+
*/
|
|
190
|
+
trackServerEvent(name: string, options?: {
|
|
191
|
+
properties?: Record<string, unknown>;
|
|
192
|
+
severity?: EventSeverity;
|
|
193
|
+
}): Promise<{
|
|
194
|
+
success: boolean;
|
|
195
|
+
id: string;
|
|
196
|
+
}>;
|
|
157
197
|
}
|
|
158
198
|
|
|
159
|
-
export {
|
|
199
|
+
export { AuthenticationError, ChannlizeClient, ChannlizeError, RateLimitError, ValidationError, ChannlizeClient as default };
|
|
200
|
+
export type { ApiResponse, ChannlizeConfig, ChatMessage, ChatSession, ContactFormData, CreateChatSessionResponse, CreateContactResponse, EventSeverity, FileAttachment, SendMessageResponse, TrackEventOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core types for Channlize SDK
|
|
3
3
|
*/
|
|
4
|
+
/** Severity for tracked events (Slack routing uses project settings). */
|
|
5
|
+
type EventSeverity = 'info' | 'warning' | 'error';
|
|
6
|
+
interface TrackEventOptions {
|
|
7
|
+
properties?: Record<string, unknown>;
|
|
8
|
+
severity?: EventSeverity;
|
|
9
|
+
distinctId?: string;
|
|
10
|
+
pageUrl?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
}
|
|
4
13
|
interface ChannlizeConfig {
|
|
5
|
-
/**
|
|
14
|
+
/** Widget key used for public SDK endpoints (contact form, chat, browser event tracking) */
|
|
6
15
|
apiKey: string;
|
|
7
16
|
/** Base URL for the Channlize API */
|
|
8
17
|
baseUrl?: string;
|
|
9
|
-
/** Project ID */
|
|
18
|
+
/** Project ID (required for server-side `trackServerEvent`) */
|
|
10
19
|
projectId: string;
|
|
11
20
|
/** Request timeout in milliseconds */
|
|
12
21
|
timeout?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Organization API key for server-side `trackServerEvent` (`POST /v1/events`).
|
|
24
|
+
* Do not expose in browser bundles.
|
|
25
|
+
*/
|
|
26
|
+
serverApiKey?: string;
|
|
13
27
|
}
|
|
14
28
|
interface ContactFormData {
|
|
15
29
|
/** Contact's name */
|
|
@@ -81,12 +95,19 @@ interface ApiResponse<T = unknown> {
|
|
|
81
95
|
/** Additional metadata */
|
|
82
96
|
meta?: Record<string, unknown>;
|
|
83
97
|
}
|
|
98
|
+
/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */
|
|
84
99
|
interface CreateContactResponse {
|
|
85
|
-
/**
|
|
100
|
+
/** Whether the submission was accepted */
|
|
101
|
+
success: boolean;
|
|
102
|
+
/** Conversation the submission was filed under */
|
|
103
|
+
conversationId: string;
|
|
104
|
+
/** Human-readable confirmation to show the visitor */
|
|
105
|
+
message: string;
|
|
106
|
+
/** @deprecated Never returned by the API (always undefined). Use `conversationId`. */
|
|
86
107
|
id: string;
|
|
87
|
-
/**
|
|
108
|
+
/** @deprecated Never returned by the API (always undefined). */
|
|
88
109
|
status: 'received' | 'processing' | 'delivered';
|
|
89
|
-
/**
|
|
110
|
+
/** @deprecated Never returned by the API (always undefined). */
|
|
90
111
|
slackThreadUrl?: string;
|
|
91
112
|
}
|
|
92
113
|
interface CreateChatSessionResponse {
|
|
@@ -139,7 +160,9 @@ declare class ChannlizeClient {
|
|
|
139
160
|
*/
|
|
140
161
|
createChatSession(metadata?: Record<string, unknown>): Promise<CreateChatSessionResponse>;
|
|
141
162
|
/**
|
|
142
|
-
* Send a message in a chat session
|
|
163
|
+
* Send a message in a chat session.
|
|
164
|
+
* When `attachments` are provided the request is sent as multipart/form-data
|
|
165
|
+
* so the files are uploaded alongside the message content.
|
|
143
166
|
*/
|
|
144
167
|
sendMessage(sessionId: string, content: string, attachments?: File[]): Promise<SendMessageResponse>;
|
|
145
168
|
/**
|
|
@@ -154,6 +177,24 @@ declare class ChannlizeClient {
|
|
|
154
177
|
* Close a chat session
|
|
155
178
|
*/
|
|
156
179
|
closeChatSession(sessionId: string): Promise<ChatSession>;
|
|
180
|
+
/**
|
|
181
|
+
* Track a product or UX event from the browser (uses widget key → POST /public/events).
|
|
182
|
+
*/
|
|
183
|
+
trackEvent(name: string, options?: TrackEventOptions): Promise<{
|
|
184
|
+
success: boolean;
|
|
185
|
+
id: string;
|
|
186
|
+
}>;
|
|
187
|
+
/**
|
|
188
|
+
* Track an event from a trusted server using the organization API key (POST /v1/events).
|
|
189
|
+
*/
|
|
190
|
+
trackServerEvent(name: string, options?: {
|
|
191
|
+
properties?: Record<string, unknown>;
|
|
192
|
+
severity?: EventSeverity;
|
|
193
|
+
}): Promise<{
|
|
194
|
+
success: boolean;
|
|
195
|
+
id: string;
|
|
196
|
+
}>;
|
|
157
197
|
}
|
|
158
198
|
|
|
159
|
-
export {
|
|
199
|
+
export { AuthenticationError, ChannlizeClient, ChannlizeError, RateLimitError, ValidationError, ChannlizeClient as default };
|
|
200
|
+
export type { ApiResponse, ChannlizeConfig, ChatMessage, ChatSession, ContactFormData, CreateChatSessionResponse, CreateContactResponse, EventSeverity, FileAttachment, SendMessageResponse, TrackEventOptions };
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,8 @@ var ChannlizeError = class extends Error {
|
|
|
50
50
|
this.code = code;
|
|
51
51
|
this.name = "ChannlizeError";
|
|
52
52
|
}
|
|
53
|
+
statusCode;
|
|
54
|
+
code;
|
|
53
55
|
};
|
|
54
56
|
var ValidationError = class extends ChannlizeError {
|
|
55
57
|
constructor(message, field) {
|
|
@@ -57,6 +59,7 @@ var ValidationError = class extends ChannlizeError {
|
|
|
57
59
|
this.field = field;
|
|
58
60
|
this.name = "ValidationError";
|
|
59
61
|
}
|
|
62
|
+
field;
|
|
60
63
|
};
|
|
61
64
|
var AuthenticationError = class extends ChannlizeError {
|
|
62
65
|
constructor(message = "Authentication failed") {
|
|
@@ -92,13 +95,17 @@ var ChannlizeClient = class {
|
|
|
92
95
|
const controller = new AbortController();
|
|
93
96
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
94
97
|
try {
|
|
98
|
+
const mergedHeaders = {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
...options.headers
|
|
101
|
+
};
|
|
102
|
+
if (mergedHeaders["Content-Type"] === "") {
|
|
103
|
+
delete mergedHeaders["Content-Type"];
|
|
104
|
+
}
|
|
95
105
|
const response = await (0, import_cross_fetch.default)(url, {
|
|
96
106
|
...options,
|
|
97
107
|
signal: controller.signal,
|
|
98
|
-
headers:
|
|
99
|
-
"Content-Type": "application/json",
|
|
100
|
-
...options.headers
|
|
101
|
-
}
|
|
108
|
+
headers: mergedHeaders
|
|
102
109
|
});
|
|
103
110
|
clearTimeout(timeoutId);
|
|
104
111
|
if (!response.ok) {
|
|
@@ -128,7 +135,9 @@ var ChannlizeClient = class {
|
|
|
128
135
|
} catch {
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
|
-
const
|
|
138
|
+
const rawMessage = errorData.message;
|
|
139
|
+
const message = (Array.isArray(rawMessage) ? rawMessage.join(", ") : rawMessage) || `HTTP ${response.status}: ${response.statusText}`;
|
|
140
|
+
const code = errorData.code ?? errorData.error;
|
|
132
141
|
switch (response.status) {
|
|
133
142
|
case 401:
|
|
134
143
|
throw new AuthenticationError(message);
|
|
@@ -137,51 +146,77 @@ var ChannlizeClient = class {
|
|
|
137
146
|
case 429:
|
|
138
147
|
throw new RateLimitError(message);
|
|
139
148
|
default:
|
|
140
|
-
throw new ChannlizeError(message, response.status,
|
|
149
|
+
throw new ChannlizeError(message, response.status, code);
|
|
141
150
|
}
|
|
142
151
|
}
|
|
143
152
|
/**
|
|
144
153
|
* Submit a contact form
|
|
145
154
|
*/
|
|
146
155
|
async submitContactForm(data) {
|
|
147
|
-
const response = await this.makeRequest(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
const response = await this.makeRequest(
|
|
157
|
+
"/public/contact",
|
|
158
|
+
{
|
|
159
|
+
method: "POST",
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
widgetKey: this.config.apiKey,
|
|
162
|
+
// apiKey is actually the widgetKey
|
|
163
|
+
subject: data.subject,
|
|
164
|
+
message: data.message,
|
|
165
|
+
email: data.email,
|
|
166
|
+
name: data.name,
|
|
167
|
+
pageUrl: data.pageUrl || (typeof window !== "undefined" ? window.location.href : void 0),
|
|
168
|
+
userAgent: data.userAgent || (typeof navigator !== "undefined" ? navigator.userAgent : void 0)
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
);
|
|
160
172
|
return response.data || response;
|
|
161
173
|
}
|
|
162
174
|
/**
|
|
163
175
|
* Create a new chat session
|
|
164
176
|
*/
|
|
165
177
|
async createChatSession(metadata) {
|
|
166
|
-
const response = await this.makeRequest(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
const response = await this.makeRequest(
|
|
179
|
+
"/v1/chat/sessions",
|
|
180
|
+
{
|
|
181
|
+
method: "POST",
|
|
182
|
+
body: JSON.stringify({
|
|
183
|
+
widgetKey: this.config.apiKey,
|
|
184
|
+
// apiKey is actually the widgetKey
|
|
185
|
+
metadata: {
|
|
186
|
+
...metadata,
|
|
187
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : void 0,
|
|
188
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
);
|
|
178
193
|
return response.data;
|
|
179
194
|
}
|
|
180
195
|
/**
|
|
181
|
-
* Send a message in a chat session
|
|
196
|
+
* Send a message in a chat session.
|
|
197
|
+
* When `attachments` are provided the request is sent as multipart/form-data
|
|
198
|
+
* so the files are uploaded alongside the message content.
|
|
182
199
|
*/
|
|
183
200
|
async sendMessage(sessionId, content, attachments) {
|
|
184
201
|
const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
202
|
+
if (attachments && attachments.length > 0) {
|
|
203
|
+
const formData = new FormData();
|
|
204
|
+
formData.append("content", content);
|
|
205
|
+
formData.append("idempotencyKey", idempotencyKey);
|
|
206
|
+
for (const file of attachments) {
|
|
207
|
+
formData.append("attachments", file, file.name);
|
|
208
|
+
}
|
|
209
|
+
const response2 = await this.makeRequest(
|
|
210
|
+
`/v1/chat/sessions/${sessionId}/messages`,
|
|
211
|
+
{
|
|
212
|
+
method: "POST",
|
|
213
|
+
body: formData,
|
|
214
|
+
// Let the browser set Content-Type with boundary for multipart
|
|
215
|
+
headers: { "Content-Type": "" }
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
return response2.data;
|
|
219
|
+
}
|
|
185
220
|
const response = await this.makeRequest(
|
|
186
221
|
`/v1/chat/sessions/${sessionId}/messages`,
|
|
187
222
|
{
|
|
@@ -198,7 +233,9 @@ var ChannlizeClient = class {
|
|
|
198
233
|
* Get chat session details
|
|
199
234
|
*/
|
|
200
235
|
async getChatSession(sessionId) {
|
|
201
|
-
const response = await this.makeRequest(
|
|
236
|
+
const response = await this.makeRequest(
|
|
237
|
+
`/v1/chat/sessions/${sessionId}`
|
|
238
|
+
);
|
|
202
239
|
return response.data;
|
|
203
240
|
}
|
|
204
241
|
/**
|
|
@@ -220,6 +257,55 @@ var ChannlizeClient = class {
|
|
|
220
257
|
);
|
|
221
258
|
return response.data;
|
|
222
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Track a product or UX event from the browser (uses widget key → POST /public/events).
|
|
262
|
+
*/
|
|
263
|
+
async trackEvent(name, options) {
|
|
264
|
+
const res = await this.makeRequest(
|
|
265
|
+
"/public/events",
|
|
266
|
+
{
|
|
267
|
+
method: "POST",
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
widgetKey: this.config.apiKey,
|
|
270
|
+
name,
|
|
271
|
+
properties: options?.properties,
|
|
272
|
+
severity: options?.severity,
|
|
273
|
+
distinctId: options?.distinctId,
|
|
274
|
+
pageUrl: options?.pageUrl ?? (typeof window !== "undefined" ? window.location.href : void 0),
|
|
275
|
+
userAgent: options?.userAgent ?? (typeof navigator !== "undefined" ? navigator.userAgent : void 0)
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
const data = res.data ?? res;
|
|
280
|
+
return data;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Track an event from a trusted server using the organization API key (POST /v1/events).
|
|
284
|
+
*/
|
|
285
|
+
async trackServerEvent(name, options) {
|
|
286
|
+
if (!this.config.serverApiKey) {
|
|
287
|
+
throw new ValidationError(
|
|
288
|
+
"serverApiKey is required for trackServerEvent"
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const res = await this.makeRequest(
|
|
292
|
+
"/v1/events",
|
|
293
|
+
{
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
projectId: this.config.projectId,
|
|
297
|
+
name,
|
|
298
|
+
properties: options?.properties,
|
|
299
|
+
severity: options?.severity
|
|
300
|
+
}),
|
|
301
|
+
headers: {
|
|
302
|
+
Authorization: `Bearer ${this.config.serverApiKey}`
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
const data = res.data ?? res;
|
|
307
|
+
return data;
|
|
308
|
+
}
|
|
223
309
|
};
|
|
224
310
|
// Annotate the CommonJS export names for ESM import in node:
|
|
225
311
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["/**\n * @channlize/core\n * Core TypeScript SDK for Channlize platform\n */\n\nexport { ChannlizeClient } from './client';\nexport * from './types';\n\n// Re-export for convenience\nexport { ChannlizeClient as default } from './client';\n","import fetch from 'cross-fetch';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n} from './types';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform\n */\nexport class ChannlizeClient {\n private readonly config: Required<ChannlizeConfig>;\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n baseUrl: config.baseUrl || 'https://api.channlize.com',\n timeout: 10000,\n ...config,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private async makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n const url = `${this.config.baseUrl}${endpoint}`;\n \n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n \n if (error instanceof ChannlizeError) {\n throw error;\n }\n \n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n \n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type');\n let errorData: any = {};\n\n if (contentType?.includes('application/json')) {\n try {\n errorData = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n\n const message = errorData.error || `HTTP ${response.status}: ${response.statusText}`;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 400:\n throw new ValidationError(message, errorData.field);\n case 429:\n throw new RateLimitError(message);\n default:\n throw new ChannlizeError(message, response.status, errorData.code);\n }\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(data: ContactFormData): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>('/public/contact', {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n pageUrl: data.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent: data.userAgent || (typeof navigator !== 'undefined' ? navigator.userAgent : undefined),\n }),\n });\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(metadata?: Record<string, unknown>): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>('/v1/chat/sessions', {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n }),\n });\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n // Generate idempotency key to prevent duplicate message creation\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n \n // For now, we send as JSON. File attachments support can be added later.\n // TODO: Implement file upload support\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(`/v1/chat/sessions/${sessionId}`);\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(sessionId: string, limit = 50, offset = 0): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n}\n","/**\n * Core types for Channlize SDK\n */\n\nexport interface ChannlizeConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /** Session status */\n status: 'active' | 'closed' | 'waiting';\n /** Messages in the session */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp */\n createdAt: Date;\n /** Updated timestamp */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\nexport interface CreateContactResponse {\n /** Contact form submission ID */\n id: string;\n /** Status of the submission */\n status: 'received' | 'processing' | 'delivered';\n /** Slack thread URL if available */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** Updated session */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(message: string, public field?: string) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\nexport class RateLimitError extends ChannlizeError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429, 'RATE_LIMIT_ERROR');\n this.name = 'RateLimitError';\n }\n}\n"],"mappings":"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","names":["fetch"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["/**\n * @channlize/core\n * Core TypeScript SDK for Channlize platform\n */\n\nexport { ChannlizeClient } from './client';\nexport * from './types';\n\n// Re-export for convenience\nexport { ChannlizeClient as default } from './client';\n","import fetch from 'cross-fetch';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n TrackEventOptions,\n EventSeverity,\n} from './types';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform\n */\nexport class ChannlizeClient {\n private readonly config: ChannlizeConfig & {\n baseUrl: string;\n timeout: number;\n };\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n baseUrl: config.baseUrl || 'https://api.channlize.com',\n timeout: 10000,\n ...config,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private async makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n const url = `${this.config.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string>),\n };\n\n // When Content-Type is explicitly empty (e.g. FormData), remove it so the\n // browser / runtime can set the correct multipart boundary automatically.\n if (mergedHeaders['Content-Type'] === '') {\n delete mergedHeaders['Content-Type'];\n }\n\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: mergedHeaders,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ChannlizeError) {\n throw error;\n }\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n\n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type');\n let errorData: any = {};\n\n if (contentType?.includes('application/json')) {\n try {\n errorData = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n\n // API error shape (api/lib/api/error.ts):\n // { statusCode, error: \"BAD_REQUEST\", message: string | string[] }\n // `error` is the machine-readable code; `message` is the human text\n // (an array of field messages for validation failures).\n const rawMessage = errorData.message;\n const message =\n (Array.isArray(rawMessage) ? rawMessage.join(', ') : rawMessage) ||\n `HTTP ${response.status}: ${response.statusText}`;\n const code: string | undefined = errorData.code ?? errorData.error;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 400:\n throw new ValidationError(message, errorData.field);\n case 429:\n throw new RateLimitError(message);\n default:\n throw new ChannlizeError(message, response.status, code);\n }\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(\n data: ContactFormData\n ): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>(\n '/public/contact',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n pageUrl:\n data.pageUrl ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n data.userAgent ||\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(\n metadata?: Record<string, unknown>\n ): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>(\n '/v1/chat/sessions',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl:\n typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent:\n typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined,\n },\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session.\n * When `attachments` are provided the request is sent as multipart/form-data\n * so the files are uploaded alongside the message content.\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\n if (attachments && attachments.length > 0) {\n const formData = new FormData();\n formData.append('content', content);\n formData.append('idempotencyKey', idempotencyKey);\n for (const file of attachments) {\n formData.append('attachments', file, file.name);\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: formData,\n // Let the browser set Content-Type with boundary for multipart\n headers: { 'Content-Type': '' },\n }\n );\n\n return response.data;\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}`\n );\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(\n sessionId: string,\n limit = 50,\n offset = 0\n ): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n\n /**\n * Track a product or UX event from the browser (uses widget key → POST /public/events).\n */\n async trackEvent(\n name: string,\n options?: TrackEventOptions\n ): Promise<{ success: boolean; id: string }> {\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/public/events',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey,\n name,\n properties: options?.properties,\n severity: options?.severity,\n distinctId: options?.distinctId,\n pageUrl:\n options?.pageUrl ??\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n options?.userAgent ??\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Track an event from a trusted server using the organization API key (POST /v1/events).\n */\n async trackServerEvent(\n name: string,\n options?: { properties?: Record<string, unknown>; severity?: EventSeverity }\n ): Promise<{ success: boolean; id: string }> {\n if (!this.config.serverApiKey) {\n throw new ValidationError(\n 'serverApiKey is required for trackServerEvent'\n );\n }\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/v1/events',\n {\n method: 'POST',\n body: JSON.stringify({\n projectId: this.config.projectId,\n name,\n properties: options?.properties,\n severity: options?.severity,\n }),\n headers: {\n Authorization: `Bearer ${this.config.serverApiKey}`,\n },\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n}\n","/**\n * Core types for Channlize SDK\n */\n\n/** Severity for tracked events (Slack routing uses project settings). */\nexport type EventSeverity = 'info' | 'warning' | 'error';\n\nexport interface TrackEventOptions {\n properties?: Record<string, unknown>;\n severity?: EventSeverity;\n distinctId?: string;\n pageUrl?: string;\n userAgent?: string;\n}\n\nexport interface ChannlizeConfig {\n /** Widget key used for public SDK endpoints (contact form, chat, browser event tracking) */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID (required for server-side `trackServerEvent`) */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /**\n * Organization API key for server-side `trackServerEvent` (`POST /v1/events`).\n * Do not expose in browser bundles.\n */\n serverApiKey?: string;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /** Session status */\n status: 'active' | 'closed' | 'waiting';\n /** Messages in the session */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp */\n createdAt: Date;\n /** Updated timestamp */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\n/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */\nexport interface CreateContactResponse {\n /** Whether the submission was accepted */\n success: boolean;\n /** Conversation the submission was filed under */\n conversationId: string;\n /** Human-readable confirmation to show the visitor */\n message: string;\n /** @deprecated Never returned by the API (always undefined). Use `conversationId`. */\n id: string;\n /** @deprecated Never returned by the API (always undefined). */\n status: 'received' | 'processing' | 'delivered';\n /** @deprecated Never returned by the API (always undefined). */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** Updated session */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\nexport class RateLimitError extends ChannlizeError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429, 'RATE_LIMIT_ERROR');\n this.name = 'RateLimitError';\n }\n}\n"],"mappings":"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","names":["fetch","response"]}
|
package/dist/index.mjs
CHANGED
|
@@ -9,6 +9,8 @@ var ChannlizeError = class extends Error {
|
|
|
9
9
|
this.code = code;
|
|
10
10
|
this.name = "ChannlizeError";
|
|
11
11
|
}
|
|
12
|
+
statusCode;
|
|
13
|
+
code;
|
|
12
14
|
};
|
|
13
15
|
var ValidationError = class extends ChannlizeError {
|
|
14
16
|
constructor(message, field) {
|
|
@@ -16,6 +18,7 @@ var ValidationError = class extends ChannlizeError {
|
|
|
16
18
|
this.field = field;
|
|
17
19
|
this.name = "ValidationError";
|
|
18
20
|
}
|
|
21
|
+
field;
|
|
19
22
|
};
|
|
20
23
|
var AuthenticationError = class extends ChannlizeError {
|
|
21
24
|
constructor(message = "Authentication failed") {
|
|
@@ -51,13 +54,17 @@ var ChannlizeClient = class {
|
|
|
51
54
|
const controller = new AbortController();
|
|
52
55
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
53
56
|
try {
|
|
57
|
+
const mergedHeaders = {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
...options.headers
|
|
60
|
+
};
|
|
61
|
+
if (mergedHeaders["Content-Type"] === "") {
|
|
62
|
+
delete mergedHeaders["Content-Type"];
|
|
63
|
+
}
|
|
54
64
|
const response = await fetch(url, {
|
|
55
65
|
...options,
|
|
56
66
|
signal: controller.signal,
|
|
57
|
-
headers:
|
|
58
|
-
"Content-Type": "application/json",
|
|
59
|
-
...options.headers
|
|
60
|
-
}
|
|
67
|
+
headers: mergedHeaders
|
|
61
68
|
});
|
|
62
69
|
clearTimeout(timeoutId);
|
|
63
70
|
if (!response.ok) {
|
|
@@ -87,7 +94,9 @@ var ChannlizeClient = class {
|
|
|
87
94
|
} catch {
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
|
-
const
|
|
97
|
+
const rawMessage = errorData.message;
|
|
98
|
+
const message = (Array.isArray(rawMessage) ? rawMessage.join(", ") : rawMessage) || `HTTP ${response.status}: ${response.statusText}`;
|
|
99
|
+
const code = errorData.code ?? errorData.error;
|
|
91
100
|
switch (response.status) {
|
|
92
101
|
case 401:
|
|
93
102
|
throw new AuthenticationError(message);
|
|
@@ -96,51 +105,77 @@ var ChannlizeClient = class {
|
|
|
96
105
|
case 429:
|
|
97
106
|
throw new RateLimitError(message);
|
|
98
107
|
default:
|
|
99
|
-
throw new ChannlizeError(message, response.status,
|
|
108
|
+
throw new ChannlizeError(message, response.status, code);
|
|
100
109
|
}
|
|
101
110
|
}
|
|
102
111
|
/**
|
|
103
112
|
* Submit a contact form
|
|
104
113
|
*/
|
|
105
114
|
async submitContactForm(data) {
|
|
106
|
-
const response = await this.makeRequest(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const response = await this.makeRequest(
|
|
116
|
+
"/public/contact",
|
|
117
|
+
{
|
|
118
|
+
method: "POST",
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
widgetKey: this.config.apiKey,
|
|
121
|
+
// apiKey is actually the widgetKey
|
|
122
|
+
subject: data.subject,
|
|
123
|
+
message: data.message,
|
|
124
|
+
email: data.email,
|
|
125
|
+
name: data.name,
|
|
126
|
+
pageUrl: data.pageUrl || (typeof window !== "undefined" ? window.location.href : void 0),
|
|
127
|
+
userAgent: data.userAgent || (typeof navigator !== "undefined" ? navigator.userAgent : void 0)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
);
|
|
119
131
|
return response.data || response;
|
|
120
132
|
}
|
|
121
133
|
/**
|
|
122
134
|
* Create a new chat session
|
|
123
135
|
*/
|
|
124
136
|
async createChatSession(metadata) {
|
|
125
|
-
const response = await this.makeRequest(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
const response = await this.makeRequest(
|
|
138
|
+
"/v1/chat/sessions",
|
|
139
|
+
{
|
|
140
|
+
method: "POST",
|
|
141
|
+
body: JSON.stringify({
|
|
142
|
+
widgetKey: this.config.apiKey,
|
|
143
|
+
// apiKey is actually the widgetKey
|
|
144
|
+
metadata: {
|
|
145
|
+
...metadata,
|
|
146
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : void 0,
|
|
147
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
);
|
|
137
152
|
return response.data;
|
|
138
153
|
}
|
|
139
154
|
/**
|
|
140
|
-
* Send a message in a chat session
|
|
155
|
+
* Send a message in a chat session.
|
|
156
|
+
* When `attachments` are provided the request is sent as multipart/form-data
|
|
157
|
+
* so the files are uploaded alongside the message content.
|
|
141
158
|
*/
|
|
142
159
|
async sendMessage(sessionId, content, attachments) {
|
|
143
160
|
const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
161
|
+
if (attachments && attachments.length > 0) {
|
|
162
|
+
const formData = new FormData();
|
|
163
|
+
formData.append("content", content);
|
|
164
|
+
formData.append("idempotencyKey", idempotencyKey);
|
|
165
|
+
for (const file of attachments) {
|
|
166
|
+
formData.append("attachments", file, file.name);
|
|
167
|
+
}
|
|
168
|
+
const response2 = await this.makeRequest(
|
|
169
|
+
`/v1/chat/sessions/${sessionId}/messages`,
|
|
170
|
+
{
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: formData,
|
|
173
|
+
// Let the browser set Content-Type with boundary for multipart
|
|
174
|
+
headers: { "Content-Type": "" }
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
return response2.data;
|
|
178
|
+
}
|
|
144
179
|
const response = await this.makeRequest(
|
|
145
180
|
`/v1/chat/sessions/${sessionId}/messages`,
|
|
146
181
|
{
|
|
@@ -157,7 +192,9 @@ var ChannlizeClient = class {
|
|
|
157
192
|
* Get chat session details
|
|
158
193
|
*/
|
|
159
194
|
async getChatSession(sessionId) {
|
|
160
|
-
const response = await this.makeRequest(
|
|
195
|
+
const response = await this.makeRequest(
|
|
196
|
+
`/v1/chat/sessions/${sessionId}`
|
|
197
|
+
);
|
|
161
198
|
return response.data;
|
|
162
199
|
}
|
|
163
200
|
/**
|
|
@@ -179,6 +216,55 @@ var ChannlizeClient = class {
|
|
|
179
216
|
);
|
|
180
217
|
return response.data;
|
|
181
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Track a product or UX event from the browser (uses widget key → POST /public/events).
|
|
221
|
+
*/
|
|
222
|
+
async trackEvent(name, options) {
|
|
223
|
+
const res = await this.makeRequest(
|
|
224
|
+
"/public/events",
|
|
225
|
+
{
|
|
226
|
+
method: "POST",
|
|
227
|
+
body: JSON.stringify({
|
|
228
|
+
widgetKey: this.config.apiKey,
|
|
229
|
+
name,
|
|
230
|
+
properties: options?.properties,
|
|
231
|
+
severity: options?.severity,
|
|
232
|
+
distinctId: options?.distinctId,
|
|
233
|
+
pageUrl: options?.pageUrl ?? (typeof window !== "undefined" ? window.location.href : void 0),
|
|
234
|
+
userAgent: options?.userAgent ?? (typeof navigator !== "undefined" ? navigator.userAgent : void 0)
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
const data = res.data ?? res;
|
|
239
|
+
return data;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Track an event from a trusted server using the organization API key (POST /v1/events).
|
|
243
|
+
*/
|
|
244
|
+
async trackServerEvent(name, options) {
|
|
245
|
+
if (!this.config.serverApiKey) {
|
|
246
|
+
throw new ValidationError(
|
|
247
|
+
"serverApiKey is required for trackServerEvent"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const res = await this.makeRequest(
|
|
251
|
+
"/v1/events",
|
|
252
|
+
{
|
|
253
|
+
method: "POST",
|
|
254
|
+
body: JSON.stringify({
|
|
255
|
+
projectId: this.config.projectId,
|
|
256
|
+
name,
|
|
257
|
+
properties: options?.properties,
|
|
258
|
+
severity: options?.severity
|
|
259
|
+
}),
|
|
260
|
+
headers: {
|
|
261
|
+
Authorization: `Bearer ${this.config.serverApiKey}`
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
const data = res.data ?? res;
|
|
266
|
+
return data;
|
|
267
|
+
}
|
|
182
268
|
};
|
|
183
269
|
export {
|
|
184
270
|
AuthenticationError,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["import fetch from 'cross-fetch';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n} from './types';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform\n */\nexport class ChannlizeClient {\n private readonly config: Required<ChannlizeConfig>;\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n baseUrl: config.baseUrl || 'https://api.channlize.com',\n timeout: 10000,\n ...config,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private async makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n const url = `${this.config.baseUrl}${endpoint}`;\n \n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n \n if (error instanceof ChannlizeError) {\n throw error;\n }\n \n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n \n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type');\n let errorData: any = {};\n\n if (contentType?.includes('application/json')) {\n try {\n errorData = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n\n const message = errorData.error || `HTTP ${response.status}: ${response.statusText}`;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 400:\n throw new ValidationError(message, errorData.field);\n case 429:\n throw new RateLimitError(message);\n default:\n throw new ChannlizeError(message, response.status, errorData.code);\n }\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(data: ContactFormData): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>('/public/contact', {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n pageUrl: data.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent: data.userAgent || (typeof navigator !== 'undefined' ? navigator.userAgent : undefined),\n }),\n });\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(metadata?: Record<string, unknown>): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>('/v1/chat/sessions', {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n }),\n });\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n // Generate idempotency key to prevent duplicate message creation\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n \n // For now, we send as JSON. File attachments support can be added later.\n // TODO: Implement file upload support\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(`/v1/chat/sessions/${sessionId}`);\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(sessionId: string, limit = 50, offset = 0): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n}\n","/**\n * Core types for Channlize SDK\n */\n\nexport interface ChannlizeConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /** Session status */\n status: 'active' | 'closed' | 'waiting';\n /** Messages in the session */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp */\n createdAt: Date;\n /** Updated timestamp */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\nexport interface CreateContactResponse {\n /** Contact form submission ID */\n id: string;\n /** Status of the submission */\n status: 'received' | 'processing' | 'delivered';\n /** Slack thread URL if available */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** Updated session */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(message: string, public field?: string) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\nexport class RateLimitError extends ChannlizeError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429, 'RATE_LIMIT_ERROR');\n this.name = 'RateLimitError';\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["import fetch from 'cross-fetch';\nimport type {\n ChannlizeConfig,\n ContactFormData,\n ChatMessage,\n ApiResponse,\n CreateContactResponse,\n CreateChatSessionResponse,\n SendMessageResponse,\n ChatSession,\n TrackEventOptions,\n EventSeverity,\n} from './types';\nimport {\n ChannlizeError,\n AuthenticationError,\n ValidationError,\n RateLimitError,\n} from './types';\n\n/**\n * Channlize API Client\n * Provides methods to interact with the Channlize platform\n */\nexport class ChannlizeClient {\n private readonly config: ChannlizeConfig & {\n baseUrl: string;\n timeout: number;\n };\n\n constructor(config: ChannlizeConfig) {\n this.config = {\n baseUrl: config.baseUrl || 'https://api.channlize.com',\n timeout: 10000,\n ...config,\n };\n\n this.validateConfig();\n }\n\n private validateConfig(): void {\n if (!this.config.apiKey) {\n throw new ValidationError('Widget key is required');\n }\n }\n\n private async makeRequest<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<ApiResponse<T>> {\n const url = `${this.config.baseUrl}${endpoint}`;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string>),\n };\n\n // When Content-Type is explicitly empty (e.g. FormData), remove it so the\n // browser / runtime can set the correct multipart boundary automatically.\n if (mergedHeaders['Content-Type'] === '') {\n delete mergedHeaders['Content-Type'];\n }\n\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: mergedHeaders,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const data = await response.json();\n return data as ApiResponse<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof ChannlizeError) {\n throw error;\n }\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new ChannlizeError('Request timeout', 408, 'TIMEOUT');\n }\n\n throw new ChannlizeError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n const contentType = response.headers.get('content-type');\n let errorData: any = {};\n\n if (contentType?.includes('application/json')) {\n try {\n errorData = await response.json();\n } catch {\n // Ignore JSON parsing errors\n }\n }\n\n // API error shape (api/lib/api/error.ts):\n // { statusCode, error: \"BAD_REQUEST\", message: string | string[] }\n // `error` is the machine-readable code; `message` is the human text\n // (an array of field messages for validation failures).\n const rawMessage = errorData.message;\n const message =\n (Array.isArray(rawMessage) ? rawMessage.join(', ') : rawMessage) ||\n `HTTP ${response.status}: ${response.statusText}`;\n const code: string | undefined = errorData.code ?? errorData.error;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 400:\n throw new ValidationError(message, errorData.field);\n case 429:\n throw new RateLimitError(message);\n default:\n throw new ChannlizeError(message, response.status, code);\n }\n }\n\n /**\n * Submit a contact form\n */\n async submitContactForm(\n data: ContactFormData\n ): Promise<CreateContactResponse> {\n const response = await this.makeRequest<CreateContactResponse>(\n '/public/contact',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n subject: data.subject,\n message: data.message,\n email: data.email,\n name: data.name,\n pageUrl:\n data.pageUrl ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n data.userAgent ||\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n\n return response.data || response;\n }\n\n /**\n * Create a new chat session\n */\n async createChatSession(\n metadata?: Record<string, unknown>\n ): Promise<CreateChatSessionResponse> {\n const response = await this.makeRequest<CreateChatSessionResponse>(\n '/v1/chat/sessions',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey, // apiKey is actually the widgetKey\n metadata: {\n ...metadata,\n pageUrl:\n typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent:\n typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined,\n },\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Send a message in a chat session.\n * When `attachments` are provided the request is sent as multipart/form-data\n * so the files are uploaded alongside the message content.\n */\n async sendMessage(\n sessionId: string,\n content: string,\n attachments?: File[]\n ): Promise<SendMessageResponse> {\n const idempotencyKey = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\n if (attachments && attachments.length > 0) {\n const formData = new FormData();\n formData.append('content', content);\n formData.append('idempotencyKey', idempotencyKey);\n for (const file of attachments) {\n formData.append('attachments', file, file.name);\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: formData,\n // Let the browser set Content-Type with boundary for multipart\n headers: { 'Content-Type': '' },\n }\n );\n\n return response.data;\n }\n\n const response = await this.makeRequest<SendMessageResponse>(\n `/v1/chat/sessions/${sessionId}/messages`,\n {\n method: 'POST',\n body: JSON.stringify({\n content,\n idempotencyKey,\n }),\n }\n );\n\n return response.data;\n }\n\n /**\n * Get chat session details\n */\n async getChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}`\n );\n return response.data;\n }\n\n /**\n * Get messages for a chat session\n */\n async getChatMessages(\n sessionId: string,\n limit = 50,\n offset = 0\n ): Promise<ChatMessage[]> {\n const response = await this.makeRequest<ChatMessage[]>(\n `/v1/chat/sessions/${sessionId}/messages?limit=${limit}&offset=${offset}`\n );\n return response.data;\n }\n\n /**\n * Close a chat session\n */\n async closeChatSession(sessionId: string): Promise<ChatSession> {\n const response = await this.makeRequest<ChatSession>(\n `/v1/chat/sessions/${sessionId}/close`,\n { method: 'POST' }\n );\n return response.data;\n }\n\n /**\n * Track a product or UX event from the browser (uses widget key → POST /public/events).\n */\n async trackEvent(\n name: string,\n options?: TrackEventOptions\n ): Promise<{ success: boolean; id: string }> {\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/public/events',\n {\n method: 'POST',\n body: JSON.stringify({\n widgetKey: this.config.apiKey,\n name,\n properties: options?.properties,\n severity: options?.severity,\n distinctId: options?.distinctId,\n pageUrl:\n options?.pageUrl ??\n (typeof window !== 'undefined' ? window.location.href : undefined),\n userAgent:\n options?.userAgent ??\n (typeof navigator !== 'undefined'\n ? navigator.userAgent\n : undefined),\n }),\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n\n /**\n * Track an event from a trusted server using the organization API key (POST /v1/events).\n */\n async trackServerEvent(\n name: string,\n options?: { properties?: Record<string, unknown>; severity?: EventSeverity }\n ): Promise<{ success: boolean; id: string }> {\n if (!this.config.serverApiKey) {\n throw new ValidationError(\n 'serverApiKey is required for trackServerEvent'\n );\n }\n const res = await this.makeRequest<{ success: boolean; id: string }>(\n '/v1/events',\n {\n method: 'POST',\n body: JSON.stringify({\n projectId: this.config.projectId,\n name,\n properties: options?.properties,\n severity: options?.severity,\n }),\n headers: {\n Authorization: `Bearer ${this.config.serverApiKey}`,\n },\n }\n );\n const data =\n (res as ApiResponse<{ success: boolean; id: string }>).data ??\n (res as unknown as { success: boolean; id: string });\n return data;\n }\n}\n","/**\n * Core types for Channlize SDK\n */\n\n/** Severity for tracked events (Slack routing uses project settings). */\nexport type EventSeverity = 'info' | 'warning' | 'error';\n\nexport interface TrackEventOptions {\n properties?: Record<string, unknown>;\n severity?: EventSeverity;\n distinctId?: string;\n pageUrl?: string;\n userAgent?: string;\n}\n\nexport interface ChannlizeConfig {\n /** Widget key used for public SDK endpoints (contact form, chat, browser event tracking) */\n apiKey: string;\n /** Base URL for the Channlize API */\n baseUrl?: string;\n /** Project ID (required for server-side `trackServerEvent`) */\n projectId: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /**\n * Organization API key for server-side `trackServerEvent` (`POST /v1/events`).\n * Do not expose in browser bundles.\n */\n serverApiKey?: string;\n}\n\nexport interface ContactFormData {\n /** Contact's name */\n name: string;\n /** Contact's email address */\n email: string;\n /** Subject of the inquiry */\n subject?: string;\n /** Message content */\n message: string;\n /** Custom fields */\n customFields?: Record<string, unknown>;\n /** Page URL where the form was submitted */\n pageUrl?: string;\n /** User agent string */\n userAgent?: string;\n}\n\nexport interface ChatMessage {\n /** Unique message ID */\n id: string;\n /** Message content */\n content: string;\n /** Message timestamp */\n timestamp: Date;\n /** Message author (user or agent) */\n author: 'user' | 'agent';\n /** Author metadata */\n authorData?: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n };\n /** Attached files */\n attachments?: FileAttachment[];\n}\n\nexport interface FileAttachment {\n /** File ID */\n id: string;\n /** Original filename */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type */\n type: string;\n /** Download URL */\n url: string;\n}\n\nexport interface ChatSession {\n /** Session ID */\n id: string;\n /** Session status */\n status: 'active' | 'closed' | 'waiting';\n /** Messages in the session */\n messages: ChatMessage[];\n /** Session metadata */\n metadata?: Record<string, unknown>;\n /** Created timestamp */\n createdAt: Date;\n /** Updated timestamp */\n updatedAt: Date;\n}\n\nexport interface ApiResponse<T = unknown> {\n /** Response data */\n data: T;\n /** Success status */\n success: boolean;\n /** Error message if any */\n error?: string;\n /** Additional metadata */\n meta?: Record<string, unknown>;\n}\n\n/** Response of `POST /public/contact` (api/app/api/public/contact/route.ts). */\nexport interface CreateContactResponse {\n /** Whether the submission was accepted */\n success: boolean;\n /** Conversation the submission was filed under */\n conversationId: string;\n /** Human-readable confirmation to show the visitor */\n message: string;\n /** @deprecated Never returned by the API (always undefined). Use `conversationId`. */\n id: string;\n /** @deprecated Never returned by the API (always undefined). */\n status: 'received' | 'processing' | 'delivered';\n /** @deprecated Never returned by the API (always undefined). */\n slackThreadUrl?: string;\n}\n\nexport interface CreateChatSessionResponse {\n /** Chat session data */\n session: ChatSession;\n /** SSE endpoint for real-time updates */\n sseEndpoint?: string;\n}\n\nexport interface SendMessageResponse {\n /** The sent message */\n message: ChatMessage;\n /** Updated session */\n session: ChatSession;\n}\n\n/**\n * Error types\n */\nexport class ChannlizeError extends Error {\n constructor(\n message: string,\n public statusCode?: number,\n public code?: string\n ) {\n super(message);\n this.name = 'ChannlizeError';\n }\n}\n\nexport class ValidationError extends ChannlizeError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class AuthenticationError extends ChannlizeError {\n constructor(message: string = 'Authentication failed') {\n super(message, 401, 'AUTHENTICATION_ERROR');\n this.name = 'AuthenticationError';\n }\n}\n\nexport class RateLimitError extends ChannlizeError {\n constructor(message: string = 'Rate limit exceeded') {\n super(message, 429, 'RATE_LIMIT_ERROR');\n this.name = 'RateLimitError';\n }\n}\n"],"mappings":";AAAA,OAAO,WAAW;;;AC2IX,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACE,SACO,YACA,MACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AAAA,EALS;AAAA,EACA;AAKX;AAEO,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAClD,YACE,SACO,OACP;AACA,UAAM,SAAS,KAAK,kBAAkB;AAF/B;AAGP,SAAK,OAAO;AAAA,EACd;AAAA,EAJS;AAKX;AAEO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EACtD,YAAY,UAAkB,yBAAyB;AACrD,UAAM,SAAS,KAAK,sBAAsB;AAC1C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,iBAAN,cAA6B,eAAe;AAAA,EACjD,YAAY,UAAkB,uBAAuB;AACnD,UAAM,SAAS,KAAK,kBAAkB;AACtC,SAAK,OAAO;AAAA,EACd;AACF;;;ADpJO,IAAM,kBAAN,MAAsB;AAAA,EACV;AAAA,EAKjB,YAAY,QAAyB;AACnC,SAAK,SAAS;AAAA,MACZ,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS;AAAA,MACT,GAAG;AAAA,IACL;AAEA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,CAAC,KAAK,OAAO,QAAQ;AACvB,YAAM,IAAI,gBAAgB,wBAAwB;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAc,YACZ,UACA,UAAuB,CAAC,GACC;AACzB,UAAM,MAAM,GAAG,KAAK,OAAO,OAAO,GAAG,QAAQ;AAE7C,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,OAAO;AAE1E,QAAI;AACF,YAAM,gBAAwC;AAAA,QAC5C,gBAAgB;AAAA,QAChB,GAAI,QAAQ;AAAA,MACd;AAIA,UAAI,cAAc,cAAc,MAAM,IAAI;AACxC,eAAO,cAAc,cAAc;AAAA,MACrC;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,GAAG;AAAA,QACH,QAAQ,WAAW;AAAA,QACnB,SAAS;AAAA,MACX,CAAC;AAED,mBAAa,SAAS;AAEtB,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,KAAK,oBAAoB,QAAQ;AAAA,MACzC;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,mBAAa,SAAS;AAEtB,UAAI,iBAAiB,gBAAgB;AACnC,cAAM;AAAA,MACR;AAEA,UAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD,cAAM,IAAI,eAAe,mBAAmB,KAAK,SAAS;AAAA,MAC5D;AAEA,YAAM,IAAI;AAAA,QACR,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,UAAoC;AACpE,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AACvD,QAAI,YAAiB,CAAC;AAEtB,QAAI,aAAa,SAAS,kBAAkB,GAAG;AAC7C,UAAI;AACF,oBAAY,MAAM,SAAS,KAAK;AAAA,MAClC,QAAQ;AAAA,MAER;AAAA,IACF;AAMA,UAAM,aAAa,UAAU;AAC7B,UAAM,WACH,MAAM,QAAQ,UAAU,IAAI,WAAW,KAAK,IAAI,IAAI,eACrD,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AACjD,UAAM,OAA2B,UAAU,QAAQ,UAAU;AAE7D,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,oBAAoB,OAAO;AAAA,MACvC,KAAK;AACH,cAAM,IAAI,gBAAgB,SAAS,UAAU,KAAK;AAAA,MACpD,KAAK;AACH,cAAM,IAAI,eAAe,OAAO;AAAA,MAClC;AACE,cAAM,IAAI,eAAe,SAAS,SAAS,QAAQ,IAAI;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBACJ,MACgC;AAChC,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,KAAK,OAAO;AAAA;AAAA,UACvB,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK;AAAA,UACX,SACE,KAAK,YACJ,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAAA,UAC1D,WACE,KAAK,cACJ,OAAO,cAAc,cAClB,UAAU,YACV;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,SAAS,QAAQ;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBACJ,UACoC;AACpC,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,KAAK,OAAO;AAAA;AAAA,UACvB,UAAU;AAAA,YACR,GAAG;AAAA,YACH,SACE,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAAA,YACzD,WACE,OAAO,cAAc,cACjB,UAAU,YACV;AAAA,UACR;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YACJ,WACA,SACA,aAC8B;AAC9B,UAAM,iBAAiB,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAEnF,QAAI,eAAe,YAAY,SAAS,GAAG;AACzC,YAAM,WAAW,IAAI,SAAS;AAC9B,eAAS,OAAO,WAAW,OAAO;AAClC,eAAS,OAAO,kBAAkB,cAAc;AAChD,iBAAW,QAAQ,aAAa;AAC9B,iBAAS,OAAO,eAAe,MAAM,KAAK,IAAI;AAAA,MAChD;AAEA,YAAMA,YAAW,MAAM,KAAK;AAAA,QAC1B,qBAAqB,SAAS;AAAA,QAC9B;AAAA,UACE,QAAQ;AAAA,UACR,MAAM;AAAA;AAAA,UAEN,SAAS,EAAE,gBAAgB,GAAG;AAAA,QAChC;AAAA,MACF;AAEA,aAAOA,UAAS;AAAA,IAClB;AAEA,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,qBAAqB,SAAS;AAAA,MAC9B;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,WAAyC;AAC5D,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,qBAAqB,SAAS;AAAA,IAChC;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,WACA,QAAQ,IACR,SAAS,GACe;AACxB,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,qBAAqB,SAAS,mBAAmB,KAAK,WAAW,MAAM;AAAA,IACzE;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,WAAyC;AAC9D,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,qBAAqB,SAAS;AAAA,MAC9B,EAAE,QAAQ,OAAO;AAAA,IACnB;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,MACA,SAC2C;AAC3C,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,KAAK,OAAO;AAAA,UACvB;AAAA,UACA,YAAY,SAAS;AAAA,UACrB,UAAU,SAAS;AAAA,UACnB,YAAY,SAAS;AAAA,UACrB,SACE,SAAS,YACR,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAAA,UAC1D,WACE,SAAS,cACR,OAAO,cAAc,cAClB,UAAU,YACV;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,OACH,IAAsD,QACtD;AACH,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,MACA,SAC2C;AAC3C,QAAI,CAAC,KAAK,OAAO,cAAc;AAC7B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,KAAK,OAAO;AAAA,UACvB;AAAA,UACA,YAAY,SAAS;AAAA,UACrB,UAAU,SAAS;AAAA,QACrB,CAAC;AAAA,QACD,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,OAAO,YAAY;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AACA,UAAM,OACH,IAAsD,QACtD;AACH,WAAO;AAAA,EACT;AACF;","names":["response"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@channlize/core",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Channlize Core SDK - TypeScript API wrapper for Channlize platform",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup",
|
|
20
20
|
"dev": "tsup --watch",
|
|
21
|
-
"lint": "eslint src
|
|
22
|
-
"lint:fix": "eslint src --
|
|
23
|
-
"type-check": "
|
|
21
|
+
"lint": "eslint src",
|
|
22
|
+
"lint:fix": "eslint src --fix",
|
|
23
|
+
"type-check": "tsc6 --noEmit",
|
|
24
24
|
"clean": "rm -rf dist"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
@@ -35,12 +35,10 @@
|
|
|
35
35
|
"cross-fetch": "^4.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"tsup": "^8.0.1",
|
|
43
|
-
"typescript": "^5.3.3"
|
|
38
|
+
"@types/node": "^26.0.0",
|
|
39
|
+
"eslint": "^10.0.0",
|
|
40
|
+
"tsup": "^9.0.0",
|
|
41
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
44
42
|
},
|
|
45
43
|
"publishConfig": {
|
|
46
44
|
"access": "public"
|
|
@@ -50,4 +48,4 @@
|
|
|
50
48
|
"url": "git+https://github.com/channlize/sdk.git",
|
|
51
49
|
"directory": "packages/core"
|
|
52
50
|
}
|
|
53
|
-
}
|
|
51
|
+
}
|