@xalantis/sdk 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 +120 -0
- package/dist/index.d.mts +338 -0
- package/dist/index.d.ts +338 -0
- package/dist/index.js +267 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +239 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/** Configuration for the Xalantis SDK client. */
|
|
2
|
+
interface XalantisConfig {
|
|
3
|
+
/** Your Xalantis API key (e.g. `sk_live_...`). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** @internal For testing only. Defaults to https://xalantis.com */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
/** Request timeout in milliseconds. Defaults to 30000 (30s). */
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}
|
|
10
|
+
/** Standard API response wrapping a single resource. */
|
|
11
|
+
interface ApiResponse<T> {
|
|
12
|
+
success: boolean;
|
|
13
|
+
data: T;
|
|
14
|
+
message: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** Paginated API response wrapping a list of resources. */
|
|
17
|
+
interface PaginatedResponse<T> {
|
|
18
|
+
success: boolean;
|
|
19
|
+
data: T[];
|
|
20
|
+
/** Pagination metadata. */
|
|
21
|
+
meta: {
|
|
22
|
+
current_page: number;
|
|
23
|
+
per_page: number;
|
|
24
|
+
total: number;
|
|
25
|
+
last_page: number;
|
|
26
|
+
from: number | null;
|
|
27
|
+
to: number | null;
|
|
28
|
+
};
|
|
29
|
+
/** Pagination navigation links. */
|
|
30
|
+
links: {
|
|
31
|
+
first: string | null;
|
|
32
|
+
last: string | null;
|
|
33
|
+
prev: string | null;
|
|
34
|
+
next: string | null;
|
|
35
|
+
};
|
|
36
|
+
message: string | null;
|
|
37
|
+
}
|
|
38
|
+
/** API error response structure. */
|
|
39
|
+
interface ApiError {
|
|
40
|
+
success: false;
|
|
41
|
+
error: {
|
|
42
|
+
/** Machine-readable error code (e.g. `VALIDATION_ERROR`, `NOT_FOUND`). */
|
|
43
|
+
code: string;
|
|
44
|
+
/** Human-readable error message. */
|
|
45
|
+
message: string;
|
|
46
|
+
/** Field-level validation errors, keyed by field name. */
|
|
47
|
+
details?: Record<string, string[]>;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Ticket priority levels. */
|
|
51
|
+
type TicketPriority = 'low' | 'medium' | 'high' | 'urgent';
|
|
52
|
+
/** Ticket lifecycle statuses. */
|
|
53
|
+
type TicketStatus = 'new' | 'open' | 'pending' | 'on_hold' | 'resolved' | 'closed';
|
|
54
|
+
/** Channel through which a ticket was created. */
|
|
55
|
+
type TicketChannel = 'api' | 'email' | 'phone' | 'chat' | 'web' | 'widget';
|
|
56
|
+
/** A support ticket. */
|
|
57
|
+
interface Ticket {
|
|
58
|
+
/** Unique identifier (UUID v4). */
|
|
59
|
+
uuid: string;
|
|
60
|
+
/** Human-readable reference (e.g. `TK-000042`). */
|
|
61
|
+
reference: string;
|
|
62
|
+
/** Ticket subject line. */
|
|
63
|
+
subject: string;
|
|
64
|
+
/** Ticket description / body. */
|
|
65
|
+
description: string;
|
|
66
|
+
/** Current status. */
|
|
67
|
+
status: TicketStatus;
|
|
68
|
+
/** Priority level. */
|
|
69
|
+
priority: TicketPriority;
|
|
70
|
+
/** Channel through which the ticket was created. */
|
|
71
|
+
channel: TicketChannel;
|
|
72
|
+
/** Ticket category, if assigned. */
|
|
73
|
+
category: {
|
|
74
|
+
name: string;
|
|
75
|
+
slug: string;
|
|
76
|
+
} | null;
|
|
77
|
+
/** Person who submitted the ticket. */
|
|
78
|
+
requester: {
|
|
79
|
+
type: string;
|
|
80
|
+
name: string | null;
|
|
81
|
+
email: string | null;
|
|
82
|
+
phone: string | null;
|
|
83
|
+
contact_id: string | null;
|
|
84
|
+
client_id: string | null;
|
|
85
|
+
};
|
|
86
|
+
/** Agent assigned to the ticket. */
|
|
87
|
+
assignee: {
|
|
88
|
+
name: string;
|
|
89
|
+
email: string;
|
|
90
|
+
} | null;
|
|
91
|
+
/** Custom field values. */
|
|
92
|
+
custom_fields: Record<string, unknown> | null;
|
|
93
|
+
/** Tags attached to the ticket. */
|
|
94
|
+
tags: string[];
|
|
95
|
+
/** SLA due date (ISO 8601). */
|
|
96
|
+
due_at: string | null;
|
|
97
|
+
/** Creation timestamp (ISO 8601). */
|
|
98
|
+
created_at: string;
|
|
99
|
+
/** Last update timestamp (ISO 8601). */
|
|
100
|
+
updated_at: string;
|
|
101
|
+
/** When the ticket was resolved (ISO 8601). */
|
|
102
|
+
resolved_at: string | null;
|
|
103
|
+
/** When the ticket was closed (ISO 8601). */
|
|
104
|
+
closed_at: string | null;
|
|
105
|
+
}
|
|
106
|
+
/** Input for creating a new ticket. */
|
|
107
|
+
interface CreateTicketInput {
|
|
108
|
+
/** Ticket subject (required, max 255 chars). */
|
|
109
|
+
subject: string;
|
|
110
|
+
/** Ticket description (required). */
|
|
111
|
+
description: string;
|
|
112
|
+
/** Email of the person reporting the issue (required). */
|
|
113
|
+
requester_email: string;
|
|
114
|
+
/** Name of the requester. */
|
|
115
|
+
requester_name?: string;
|
|
116
|
+
/** Priority level. Defaults to `medium`. */
|
|
117
|
+
priority?: TicketPriority;
|
|
118
|
+
/** Assign to a category by ID. */
|
|
119
|
+
category_id?: number;
|
|
120
|
+
/** Assign to a category by slug. */
|
|
121
|
+
category_slug?: string;
|
|
122
|
+
/** Channel source. Defaults to `api`. */
|
|
123
|
+
channel?: TicketChannel;
|
|
124
|
+
/** Custom field values. */
|
|
125
|
+
custom_fields?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
/** Input for updating an existing ticket. All fields are optional. */
|
|
128
|
+
interface UpdateTicketInput {
|
|
129
|
+
subject?: string;
|
|
130
|
+
description?: string;
|
|
131
|
+
status?: TicketStatus;
|
|
132
|
+
priority?: TicketPriority;
|
|
133
|
+
category_id?: number;
|
|
134
|
+
category_slug?: string;
|
|
135
|
+
/** Assign to a specific agent by ID. */
|
|
136
|
+
assignee_id?: number;
|
|
137
|
+
/** SLA due date (ISO 8601). */
|
|
138
|
+
due_at?: string;
|
|
139
|
+
custom_fields?: Record<string, unknown>;
|
|
140
|
+
}
|
|
141
|
+
/** Parameters for listing/filtering tickets. */
|
|
142
|
+
interface ListTicketsParams {
|
|
143
|
+
/** Page number (1-based). */
|
|
144
|
+
page?: number;
|
|
145
|
+
/** Results per page (default 15, max 100). */
|
|
146
|
+
per_page?: number;
|
|
147
|
+
/** Filter by status. */
|
|
148
|
+
status?: TicketStatus;
|
|
149
|
+
/** Filter by priority. */
|
|
150
|
+
priority?: TicketPriority;
|
|
151
|
+
/** Filter by assigned agent ID. */
|
|
152
|
+
assignee_id?: number;
|
|
153
|
+
/** Filter by requester email (exact match). */
|
|
154
|
+
requester_email?: string;
|
|
155
|
+
/** Full-text search on subject and description. */
|
|
156
|
+
search?: string;
|
|
157
|
+
/** Field to sort by. */
|
|
158
|
+
sort_by?: 'created_at' | 'updated_at' | 'priority' | 'status' | 'due_at' | 'reference';
|
|
159
|
+
/** Sort direction. */
|
|
160
|
+
sort_dir?: 'asc' | 'desc';
|
|
161
|
+
}
|
|
162
|
+
/** A reply or internal note on a ticket. */
|
|
163
|
+
interface TicketReply {
|
|
164
|
+
/** Unique identifier (UUID v4). */
|
|
165
|
+
uuid: string;
|
|
166
|
+
/** Reply content (HTML or plain text). */
|
|
167
|
+
content: string;
|
|
168
|
+
/** Whether this is an internal note (not visible to the requester). */
|
|
169
|
+
is_internal: boolean;
|
|
170
|
+
/** Whether this reply was auto-generated by the system. */
|
|
171
|
+
is_automated: boolean;
|
|
172
|
+
/** Agent who posted the reply, if any. */
|
|
173
|
+
user: {
|
|
174
|
+
id: number;
|
|
175
|
+
full_name: string;
|
|
176
|
+
email: string;
|
|
177
|
+
} | null;
|
|
178
|
+
/** Creation timestamp (ISO 8601). */
|
|
179
|
+
created_at: string;
|
|
180
|
+
/** Last update timestamp (ISO 8601). */
|
|
181
|
+
updated_at: string;
|
|
182
|
+
}
|
|
183
|
+
/** Input for creating a reply on a ticket. */
|
|
184
|
+
interface CreateTicketReplyInput {
|
|
185
|
+
/** Reply content (required). */
|
|
186
|
+
content: string;
|
|
187
|
+
/** Set to `true` for an internal note (not visible to the requester). */
|
|
188
|
+
is_internal?: boolean;
|
|
189
|
+
}
|
|
190
|
+
/** Parameters for listing replies. */
|
|
191
|
+
interface ListTicketRepliesParams {
|
|
192
|
+
/** Page number (1-based). */
|
|
193
|
+
page?: number;
|
|
194
|
+
/** Results per page (default 50). */
|
|
195
|
+
per_page?: number;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Error thrown when the Xalantis API returns a non-success response.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* try {
|
|
204
|
+
* await client.tickets.create({ ... })
|
|
205
|
+
* } catch (error) {
|
|
206
|
+
* if (error instanceof XalantisError) {
|
|
207
|
+
* console.error(error.code) // 'VALIDATION_ERROR'
|
|
208
|
+
* console.error(error.status) // 422
|
|
209
|
+
* console.error(error.details) // { subject: ['Required'] }
|
|
210
|
+
* }
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
declare class XalantisError extends Error {
|
|
215
|
+
/** Machine-readable error code (e.g. `VALIDATION_ERROR`, `NOT_FOUND`, `UNAUTHORIZED`). */
|
|
216
|
+
code: string;
|
|
217
|
+
/** HTTP status code. */
|
|
218
|
+
status: number;
|
|
219
|
+
/** Field-level validation errors, keyed by field name. Only present for 422 responses. */
|
|
220
|
+
details?: Record<string, string[]>;
|
|
221
|
+
/** Value of `Retry-After` header when rate-limited (429). */
|
|
222
|
+
retryAfter?: number;
|
|
223
|
+
constructor(message: string, code: string, status: number, details?: Record<string, string[]>, retryAfter?: number);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Low-level HTTP client for the Xalantis API.
|
|
227
|
+
* Handles authentication, request building, and error mapping.
|
|
228
|
+
* @internal Use the `Xalantis` class instead.
|
|
229
|
+
*/
|
|
230
|
+
declare class HttpClient {
|
|
231
|
+
private baseUrl;
|
|
232
|
+
private apiKey;
|
|
233
|
+
private timeoutMs;
|
|
234
|
+
constructor(config: XalantisConfig);
|
|
235
|
+
/** @internal Prevent accidental serialization of sensitive data. */
|
|
236
|
+
toJSON(): Record<string, unknown>;
|
|
237
|
+
private request;
|
|
238
|
+
/** Send a GET request and return a single resource. */
|
|
239
|
+
get<T>(path: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
|
|
240
|
+
/** Send a GET request and return a paginated list. */
|
|
241
|
+
getPaginated<T>(path: string, params?: Record<string, unknown>): Promise<PaginatedResponse<T>>;
|
|
242
|
+
/** Send a POST request. */
|
|
243
|
+
post<T>(path: string, body: unknown): Promise<ApiResponse<T>>;
|
|
244
|
+
/** Send a PATCH request. */
|
|
245
|
+
patch<T>(path: string, body: unknown): Promise<ApiResponse<T>>;
|
|
246
|
+
/** Send a DELETE request. */
|
|
247
|
+
delete(path: string): Promise<ApiResponse<null>>;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
declare class TicketsResource {
|
|
251
|
+
private http;
|
|
252
|
+
constructor(http: HttpClient);
|
|
253
|
+
/**
|
|
254
|
+
* List tickets with optional filters and pagination.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* const tickets = await client.tickets.list({ status: 'open', per_page: 10 })
|
|
258
|
+
*/
|
|
259
|
+
list(params?: ListTicketsParams): Promise<PaginatedResponse<Ticket>>;
|
|
260
|
+
/**
|
|
261
|
+
* Get a single ticket by UUID.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* const ticket = await client.tickets.get('uuid-here')
|
|
265
|
+
*/
|
|
266
|
+
get(uuid: string): Promise<ApiResponse<Ticket>>;
|
|
267
|
+
/**
|
|
268
|
+
* Create a new ticket.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* const ticket = await client.tickets.create({
|
|
272
|
+
* subject: 'Login issue',
|
|
273
|
+
* description: 'Cannot login since update',
|
|
274
|
+
* requester_email: 'user@example.com',
|
|
275
|
+
* priority: 'high',
|
|
276
|
+
* })
|
|
277
|
+
*/
|
|
278
|
+
create(input: CreateTicketInput): Promise<ApiResponse<Ticket>>;
|
|
279
|
+
/**
|
|
280
|
+
* Update an existing ticket.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* const ticket = await client.tickets.update('uuid-here', { status: 'resolved' })
|
|
284
|
+
*/
|
|
285
|
+
update(uuid: string, input: UpdateTicketInput): Promise<ApiResponse<Ticket>>;
|
|
286
|
+
/**
|
|
287
|
+
* List replies for a ticket.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* const replies = await client.tickets.listReplies('uuid-here')
|
|
291
|
+
*/
|
|
292
|
+
listReplies(ticketUuid: string, params?: ListTicketRepliesParams): Promise<PaginatedResponse<TicketReply>>;
|
|
293
|
+
/**
|
|
294
|
+
* Add a reply to a ticket.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* await client.tickets.reply('uuid-here', { content: 'Thanks for reporting!' })
|
|
298
|
+
*/
|
|
299
|
+
reply(ticketUuid: string, input: CreateTicketReplyInput): Promise<ApiResponse<TicketReply>>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Xalantis SDK Client.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* import { Xalantis } from '@xalantis/sdk'
|
|
308
|
+
*
|
|
309
|
+
* const client = new Xalantis({ apiKey: 'xal_live_...' })
|
|
310
|
+
*
|
|
311
|
+
* // Create a ticket
|
|
312
|
+
* const { data: ticket } = await client.tickets.create({
|
|
313
|
+
* subject: 'Bug report',
|
|
314
|
+
* description: 'Something is broken',
|
|
315
|
+
* requester_email: 'user@example.com',
|
|
316
|
+
* })
|
|
317
|
+
*
|
|
318
|
+
* // List tickets
|
|
319
|
+
* const { data: tickets } = await client.tickets.list({ status: 'open' })
|
|
320
|
+
*
|
|
321
|
+
* // Get a ticket
|
|
322
|
+
* const { data: ticket } = await client.tickets.get('uuid-here')
|
|
323
|
+
*
|
|
324
|
+
* // Update a ticket
|
|
325
|
+
* await client.tickets.update('uuid-here', { status: 'resolved' })
|
|
326
|
+
*
|
|
327
|
+
* // Reply to a ticket
|
|
328
|
+
* await client.tickets.reply('uuid-here', { content: 'Fixed!' })
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
declare class Xalantis {
|
|
332
|
+
private http;
|
|
333
|
+
/** Ticket management (create, list, get, update, reply). */
|
|
334
|
+
tickets: TicketsResource;
|
|
335
|
+
constructor(config: XalantisConfig);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export { type ApiError, type ApiResponse, type CreateTicketInput, type CreateTicketReplyInput, type ListTicketRepliesParams, type ListTicketsParams, type PaginatedResponse, type Ticket, type TicketChannel, type TicketPriority, type TicketReply, type TicketStatus, type UpdateTicketInput, Xalantis, type XalantisConfig, XalantisError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Xalantis: () => Xalantis,
|
|
24
|
+
XalantisError: () => XalantisError
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/http.ts
|
|
29
|
+
var XalantisError = class extends Error {
|
|
30
|
+
constructor(message, code, status, details, retryAfter) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "XalantisError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.details = details;
|
|
36
|
+
this.retryAfter = retryAfter;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
40
|
+
var HttpClient = class {
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.apiKey = config.apiKey;
|
|
43
|
+
this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
44
|
+
const raw = config.baseUrl || "https://xalantis.com";
|
|
45
|
+
const parsed = new URL(raw);
|
|
46
|
+
if (parsed.protocol !== "https:" && !config.baseUrl) {
|
|
47
|
+
throw new Error("Xalantis SDK: baseUrl must use HTTPS");
|
|
48
|
+
}
|
|
49
|
+
this.baseUrl = parsed.origin;
|
|
50
|
+
Object.defineProperty(this, "apiKey", { enumerable: false });
|
|
51
|
+
}
|
|
52
|
+
/** @internal Prevent accidental serialization of sensitive data. */
|
|
53
|
+
toJSON() {
|
|
54
|
+
return { baseUrl: this.baseUrl };
|
|
55
|
+
}
|
|
56
|
+
async request(method, path, body, params) {
|
|
57
|
+
const url = new URL(`${this.baseUrl}/api/v1${path}`);
|
|
58
|
+
if (params) {
|
|
59
|
+
for (const [key, value] of Object.entries(params)) {
|
|
60
|
+
if (value !== void 0 && value !== null) {
|
|
61
|
+
url.searchParams.set(key, String(value));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const headers = {
|
|
66
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
67
|
+
"Accept": "application/json",
|
|
68
|
+
"Content-Type": "application/json"
|
|
69
|
+
};
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await fetch(url.toString(), {
|
|
75
|
+
method,
|
|
76
|
+
headers,
|
|
77
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
78
|
+
signal: controller.signal
|
|
79
|
+
});
|
|
80
|
+
} catch (err) {
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
83
|
+
throw new XalantisError(`Request timed out after ${this.timeoutMs}ms`, "TIMEOUT", 0);
|
|
84
|
+
}
|
|
85
|
+
throw new XalantisError(
|
|
86
|
+
err instanceof Error ? err.message : "Network request failed",
|
|
87
|
+
"NETWORK_ERROR",
|
|
88
|
+
0
|
|
89
|
+
);
|
|
90
|
+
} finally {
|
|
91
|
+
clearTimeout(timeoutId);
|
|
92
|
+
}
|
|
93
|
+
let json;
|
|
94
|
+
try {
|
|
95
|
+
json = await response.json();
|
|
96
|
+
} catch {
|
|
97
|
+
throw new XalantisError(
|
|
98
|
+
`Server returned non-JSON response (HTTP ${response.status})`,
|
|
99
|
+
"INVALID_RESPONSE",
|
|
100
|
+
response.status
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (typeof json !== "object" || json === null || !("success" in json)) {
|
|
104
|
+
throw new XalantisError(
|
|
105
|
+
"Invalid API response format",
|
|
106
|
+
"INVALID_RESPONSE",
|
|
107
|
+
response.status
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
if (!response.ok || json.success === false) {
|
|
111
|
+
const error = json;
|
|
112
|
+
let retryAfter;
|
|
113
|
+
if (response.status === 429) {
|
|
114
|
+
const retryHeader = response.headers.get("Retry-After");
|
|
115
|
+
retryAfter = retryHeader ? parseInt(retryHeader, 10) : void 0;
|
|
116
|
+
}
|
|
117
|
+
throw new XalantisError(
|
|
118
|
+
error.error?.message || `Request failed with status ${response.status}`,
|
|
119
|
+
error.error?.code || "UNKNOWN_ERROR",
|
|
120
|
+
response.status,
|
|
121
|
+
error.error?.details,
|
|
122
|
+
retryAfter
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return json;
|
|
126
|
+
}
|
|
127
|
+
/** Send a GET request and return a single resource. */
|
|
128
|
+
async get(path, params) {
|
|
129
|
+
return this.request("GET", path, void 0, params);
|
|
130
|
+
}
|
|
131
|
+
/** Send a GET request and return a paginated list. */
|
|
132
|
+
async getPaginated(path, params) {
|
|
133
|
+
return this.request("GET", path, void 0, params);
|
|
134
|
+
}
|
|
135
|
+
/** Send a POST request. */
|
|
136
|
+
async post(path, body) {
|
|
137
|
+
return this.request("POST", path, body);
|
|
138
|
+
}
|
|
139
|
+
/** Send a PATCH request. */
|
|
140
|
+
async patch(path, body) {
|
|
141
|
+
return this.request("PATCH", path, body);
|
|
142
|
+
}
|
|
143
|
+
/** Send a DELETE request. */
|
|
144
|
+
async delete(path) {
|
|
145
|
+
return this.request("DELETE", path);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/resources/tickets.ts
|
|
150
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
151
|
+
var VALID_PRIORITIES = ["low", "medium", "high", "urgent"];
|
|
152
|
+
var VALID_STATUSES = ["new", "open", "pending", "on_hold", "resolved", "closed"];
|
|
153
|
+
var VALID_CHANNELS = ["api", "email", "phone", "chat", "web", "widget"];
|
|
154
|
+
function assertUuid(value, label) {
|
|
155
|
+
if (!UUID_RE.test(value)) {
|
|
156
|
+
throw new Error(`Xalantis SDK: ${label} must be a valid UUID (got "${value}")`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
var TicketsResource = class {
|
|
160
|
+
constructor(http) {
|
|
161
|
+
this.http = http;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* List tickets with optional filters and pagination.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const tickets = await client.tickets.list({ status: 'open', per_page: 10 })
|
|
168
|
+
*/
|
|
169
|
+
async list(params) {
|
|
170
|
+
return this.http.getPaginated("/tickets", params);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get a single ticket by UUID.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const ticket = await client.tickets.get('uuid-here')
|
|
177
|
+
*/
|
|
178
|
+
async get(uuid) {
|
|
179
|
+
assertUuid(uuid, "ticket uuid");
|
|
180
|
+
return this.http.get(`/tickets/${uuid}`);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Create a new ticket.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* const ticket = await client.tickets.create({
|
|
187
|
+
* subject: 'Login issue',
|
|
188
|
+
* description: 'Cannot login since update',
|
|
189
|
+
* requester_email: 'user@example.com',
|
|
190
|
+
* priority: 'high',
|
|
191
|
+
* })
|
|
192
|
+
*/
|
|
193
|
+
async create(input) {
|
|
194
|
+
if (!input.subject || input.subject.trim().length === 0) {
|
|
195
|
+
throw new Error("Xalantis SDK: subject is required");
|
|
196
|
+
}
|
|
197
|
+
if (!input.description || input.description.trim().length === 0) {
|
|
198
|
+
throw new Error("Xalantis SDK: description is required");
|
|
199
|
+
}
|
|
200
|
+
if (!input.requester_email || input.requester_email.trim().length === 0) {
|
|
201
|
+
throw new Error("Xalantis SDK: requester_email is required");
|
|
202
|
+
}
|
|
203
|
+
if (input.priority && !VALID_PRIORITIES.includes(input.priority)) {
|
|
204
|
+
throw new Error(`Xalantis SDK: invalid priority "${input.priority}" (must be ${VALID_PRIORITIES.join(", ")})`);
|
|
205
|
+
}
|
|
206
|
+
if (input.channel && !VALID_CHANNELS.includes(input.channel)) {
|
|
207
|
+
throw new Error(`Xalantis SDK: invalid channel "${input.channel}" (must be ${VALID_CHANNELS.join(", ")})`);
|
|
208
|
+
}
|
|
209
|
+
return this.http.post("/tickets", input);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Update an existing ticket.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* const ticket = await client.tickets.update('uuid-here', { status: 'resolved' })
|
|
216
|
+
*/
|
|
217
|
+
async update(uuid, input) {
|
|
218
|
+
assertUuid(uuid, "ticket uuid");
|
|
219
|
+
if (input.priority && !VALID_PRIORITIES.includes(input.priority)) {
|
|
220
|
+
throw new Error(`Xalantis SDK: invalid priority "${input.priority}" (must be ${VALID_PRIORITIES.join(", ")})`);
|
|
221
|
+
}
|
|
222
|
+
if (input.status && !VALID_STATUSES.includes(input.status)) {
|
|
223
|
+
throw new Error(`Xalantis SDK: invalid status "${input.status}" (must be ${VALID_STATUSES.join(", ")})`);
|
|
224
|
+
}
|
|
225
|
+
return this.http.patch(`/tickets/${uuid}`, input);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* List replies for a ticket.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* const replies = await client.tickets.listReplies('uuid-here')
|
|
232
|
+
*/
|
|
233
|
+
async listReplies(ticketUuid, params) {
|
|
234
|
+
assertUuid(ticketUuid, "ticket uuid");
|
|
235
|
+
return this.http.getPaginated(`/tickets/${ticketUuid}/replies`, params);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Add a reply to a ticket.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* await client.tickets.reply('uuid-here', { content: 'Thanks for reporting!' })
|
|
242
|
+
*/
|
|
243
|
+
async reply(ticketUuid, input) {
|
|
244
|
+
assertUuid(ticketUuid, "ticket uuid");
|
|
245
|
+
if (!input.content || input.content.trim().length === 0) {
|
|
246
|
+
throw new Error("Xalantis SDK: reply content is required");
|
|
247
|
+
}
|
|
248
|
+
return this.http.post(`/tickets/${ticketUuid}/replies`, input);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/client.ts
|
|
253
|
+
var Xalantis = class {
|
|
254
|
+
constructor(config) {
|
|
255
|
+
if (!config.apiKey) {
|
|
256
|
+
throw new Error("Xalantis SDK: apiKey is required");
|
|
257
|
+
}
|
|
258
|
+
this.http = new HttpClient(config);
|
|
259
|
+
this.tickets = new TicketsResource(this.http);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
263
|
+
0 && (module.exports = {
|
|
264
|
+
Xalantis,
|
|
265
|
+
XalantisError
|
|
266
|
+
});
|
|
267
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/http.ts","../src/resources/tickets.ts","../src/client.ts"],"sourcesContent":["export { Xalantis } from './client'\nexport { XalantisError } from './http'\nexport type {\n // Config\n XalantisConfig,\n // API\n ApiResponse,\n PaginatedResponse,\n ApiError,\n // Tickets\n Ticket,\n TicketPriority,\n TicketStatus,\n TicketChannel,\n CreateTicketInput,\n UpdateTicketInput,\n ListTicketsParams,\n // Replies\n TicketReply,\n CreateTicketReplyInput,\n ListTicketRepliesParams,\n} from './types'\n","import type { ApiError, ApiResponse, PaginatedResponse, XalantisConfig } from './types'\n\n/**\n * Error thrown when the Xalantis API returns a non-success response.\n *\n * @example\n * ```ts\n * try {\n * await client.tickets.create({ ... })\n * } catch (error) {\n * if (error instanceof XalantisError) {\n * console.error(error.code) // 'VALIDATION_ERROR'\n * console.error(error.status) // 422\n * console.error(error.details) // { subject: ['Required'] }\n * }\n * }\n * ```\n */\nexport class XalantisError extends Error {\n /** Machine-readable error code (e.g. `VALIDATION_ERROR`, `NOT_FOUND`, `UNAUTHORIZED`). */\n code: string\n /** HTTP status code. */\n status: number\n /** Field-level validation errors, keyed by field name. Only present for 422 responses. */\n details?: Record<string, string[]>\n /** Value of `Retry-After` header when rate-limited (429). */\n retryAfter?: number\n\n constructor(message: string, code: string, status: number, details?: Record<string, string[]>, retryAfter?: number) {\n super(message)\n this.name = 'XalantisError'\n this.code = code\n this.status = status\n this.details = details\n this.retryAfter = retryAfter\n }\n}\n\n/** Default request timeout in milliseconds (30 seconds). */\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Low-level HTTP client for the Xalantis API.\n * Handles authentication, request building, and error mapping.\n * @internal Use the `Xalantis` class instead.\n */\nexport class HttpClient {\n private baseUrl: string\n private apiKey: string\n private timeoutMs: number\n\n constructor(config: XalantisConfig) {\n this.apiKey = config.apiKey\n this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS\n\n // [SECURITY] Validate baseUrl\n const raw = config.baseUrl || 'https://xalantis.com'\n const parsed = new URL(raw)\n\n if (parsed.protocol !== 'https:' && !config.baseUrl) {\n throw new Error('Xalantis SDK: baseUrl must use HTTPS')\n }\n\n this.baseUrl = parsed.origin\n\n // [SECURITY] Prevent API key from appearing in JSON.stringify / console.log\n Object.defineProperty(this, 'apiKey', { enumerable: false })\n }\n\n /** @internal Prevent accidental serialization of sensitive data. */\n toJSON(): Record<string, unknown> {\n return { baseUrl: this.baseUrl }\n }\n\n private async request<T>(method: string, path: string, body?: unknown, params?: Record<string, unknown>): Promise<T> {\n const url = new URL(`${this.baseUrl}/api/v1${path}`)\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value))\n }\n }\n }\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Accept': 'application/json',\n 'Content-Type': 'application/json',\n }\n\n // [SECURITY] AbortController timeout to prevent slowloris / hanging connections\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs)\n\n let response: Response\n try {\n response = await fetch(url.toString(), {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n })\n } catch (err: unknown) {\n clearTimeout(timeoutId)\n if (err instanceof DOMException && err.name === 'AbortError') {\n throw new XalantisError(`Request timed out after ${this.timeoutMs}ms`, 'TIMEOUT', 0)\n }\n throw new XalantisError(\n err instanceof Error ? err.message : 'Network request failed',\n 'NETWORK_ERROR',\n 0,\n )\n } finally {\n clearTimeout(timeoutId)\n }\n\n // [SECURITY] Handle non-JSON responses (502/503 HTML pages, etc.)\n let json: unknown\n try {\n json = await response.json()\n } catch {\n throw new XalantisError(\n `Server returned non-JSON response (HTTP ${response.status})`,\n 'INVALID_RESPONSE',\n response.status,\n )\n }\n\n // [SECURITY] Validate response structure\n if (typeof json !== 'object' || json === null || !('success' in json)) {\n throw new XalantisError(\n 'Invalid API response format',\n 'INVALID_RESPONSE',\n response.status,\n )\n }\n\n if (!response.ok || (json as Record<string, unknown>).success === false) {\n const error = json as ApiError\n\n // Handle 429 Rate Limiting with Retry-After header\n let retryAfter: number | undefined\n if (response.status === 429) {\n const retryHeader = response.headers.get('Retry-After')\n retryAfter = retryHeader ? parseInt(retryHeader, 10) : undefined\n }\n\n throw new XalantisError(\n error.error?.message || `Request failed with status ${response.status}`,\n error.error?.code || 'UNKNOWN_ERROR',\n response.status,\n error.error?.details,\n retryAfter,\n )\n }\n\n return json as T\n }\n\n /** Send a GET request and return a single resource. */\n async get<T>(path: string, params?: Record<string, unknown>): Promise<ApiResponse<T>> {\n return this.request('GET', path, undefined, params)\n }\n\n /** Send a GET request and return a paginated list. */\n async getPaginated<T>(path: string, params?: Record<string, unknown>): Promise<PaginatedResponse<T>> {\n return this.request('GET', path, undefined, params)\n }\n\n /** Send a POST request. */\n async post<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return this.request('POST', path, body)\n }\n\n /** Send a PATCH request. */\n async patch<T>(path: string, body: unknown): Promise<ApiResponse<T>> {\n return this.request('PATCH', path, body)\n }\n\n /** Send a DELETE request. */\n async delete(path: string): Promise<ApiResponse<null>> {\n return this.request('DELETE', path)\n }\n}\n","import type { HttpClient } from '../http'\nimport type {\n ApiResponse,\n CreateTicketInput,\n CreateTicketReplyInput,\n ListTicketRepliesParams,\n ListTicketsParams,\n PaginatedResponse,\n Ticket,\n TicketReply,\n UpdateTicketInput,\n} from '../types'\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nconst VALID_PRIORITIES = ['low', 'medium', 'high', 'urgent']\nconst VALID_STATUSES = ['new', 'open', 'pending', 'on_hold', 'resolved', 'closed']\nconst VALID_CHANNELS = ['api', 'email', 'phone', 'chat', 'web', 'widget']\n\n/** Validate UUID format to prevent path traversal. */\nfunction assertUuid(value: string, label: string): void {\n if (!UUID_RE.test(value)) {\n throw new Error(`Xalantis SDK: ${label} must be a valid UUID (got \"${value}\")`)\n }\n}\n\nexport class TicketsResource {\n constructor(private http: HttpClient) {}\n\n /**\n * List tickets with optional filters and pagination.\n *\n * @example\n * const tickets = await client.tickets.list({ status: 'open', per_page: 10 })\n */\n async list(params?: ListTicketsParams): Promise<PaginatedResponse<Ticket>> {\n return this.http.getPaginated<Ticket>('/tickets', params as Record<string, unknown>)\n }\n\n /**\n * Get a single ticket by UUID.\n *\n * @example\n * const ticket = await client.tickets.get('uuid-here')\n */\n async get(uuid: string): Promise<ApiResponse<Ticket>> {\n assertUuid(uuid, 'ticket uuid')\n return this.http.get<Ticket>(`/tickets/${uuid}`)\n }\n\n /**\n * Create a new ticket.\n *\n * @example\n * const ticket = await client.tickets.create({\n * subject: 'Login issue',\n * description: 'Cannot login since update',\n * requester_email: 'user@example.com',\n * priority: 'high',\n * })\n */\n async create(input: CreateTicketInput): Promise<ApiResponse<Ticket>> {\n if (!input.subject || input.subject.trim().length === 0) {\n throw new Error('Xalantis SDK: subject is required')\n }\n if (!input.description || input.description.trim().length === 0) {\n throw new Error('Xalantis SDK: description is required')\n }\n if (!input.requester_email || input.requester_email.trim().length === 0) {\n throw new Error('Xalantis SDK: requester_email is required')\n }\n if (input.priority && !VALID_PRIORITIES.includes(input.priority)) {\n throw new Error(`Xalantis SDK: invalid priority \"${input.priority}\" (must be ${VALID_PRIORITIES.join(', ')})`)\n }\n if (input.channel && !VALID_CHANNELS.includes(input.channel)) {\n throw new Error(`Xalantis SDK: invalid channel \"${input.channel}\" (must be ${VALID_CHANNELS.join(', ')})`)\n }\n\n return this.http.post<Ticket>('/tickets', input)\n }\n\n /**\n * Update an existing ticket.\n *\n * @example\n * const ticket = await client.tickets.update('uuid-here', { status: 'resolved' })\n */\n async update(uuid: string, input: UpdateTicketInput): Promise<ApiResponse<Ticket>> {\n assertUuid(uuid, 'ticket uuid')\n\n if (input.priority && !VALID_PRIORITIES.includes(input.priority)) {\n throw new Error(`Xalantis SDK: invalid priority \"${input.priority}\" (must be ${VALID_PRIORITIES.join(', ')})`)\n }\n if (input.status && !VALID_STATUSES.includes(input.status)) {\n throw new Error(`Xalantis SDK: invalid status \"${input.status}\" (must be ${VALID_STATUSES.join(', ')})`)\n }\n\n return this.http.patch<Ticket>(`/tickets/${uuid}`, input)\n }\n\n /**\n * List replies for a ticket.\n *\n * @example\n * const replies = await client.tickets.listReplies('uuid-here')\n */\n async listReplies(ticketUuid: string, params?: ListTicketRepliesParams): Promise<PaginatedResponse<TicketReply>> {\n assertUuid(ticketUuid, 'ticket uuid')\n return this.http.getPaginated<TicketReply>(`/tickets/${ticketUuid}/replies`, params as Record<string, unknown>)\n }\n\n /**\n * Add a reply to a ticket.\n *\n * @example\n * await client.tickets.reply('uuid-here', { content: 'Thanks for reporting!' })\n */\n async reply(ticketUuid: string, input: CreateTicketReplyInput): Promise<ApiResponse<TicketReply>> {\n assertUuid(ticketUuid, 'ticket uuid')\n\n if (!input.content || input.content.trim().length === 0) {\n throw new Error('Xalantis SDK: reply content is required')\n }\n\n return this.http.post<TicketReply>(`/tickets/${ticketUuid}/replies`, input)\n }\n}\n","import { HttpClient } from './http'\nimport { TicketsResource } from './resources/tickets'\nimport type { XalantisConfig } from './types'\n\n/**\n * Xalantis SDK Client.\n *\n * @example\n * ```ts\n * import { Xalantis } from '@xalantis/sdk'\n *\n * const client = new Xalantis({ apiKey: 'xal_live_...' })\n *\n * // Create a ticket\n * const { data: ticket } = await client.tickets.create({\n * subject: 'Bug report',\n * description: 'Something is broken',\n * requester_email: 'user@example.com',\n * })\n *\n * // List tickets\n * const { data: tickets } = await client.tickets.list({ status: 'open' })\n *\n * // Get a ticket\n * const { data: ticket } = await client.tickets.get('uuid-here')\n *\n * // Update a ticket\n * await client.tickets.update('uuid-here', { status: 'resolved' })\n *\n * // Reply to a ticket\n * await client.tickets.reply('uuid-here', { content: 'Fixed!' })\n * ```\n */\nexport class Xalantis {\n private http: HttpClient\n\n /** Ticket management (create, list, get, update, reply). */\n public tickets: TicketsResource\n\n constructor(config: XalantisConfig) {\n if (!config.apiKey) {\n throw new Error('Xalantis SDK: apiKey is required')\n }\n\n this.http = new HttpClient(config)\n this.tickets = new TicketsResource(this.http)\n }\n}\n"],"mappings":"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","names":[]}
|