Skip to content

Commit 9e5135e

Browse files
committed
Export types
1 parent b72263f commit 9e5135e

File tree

1 file changed

+82
-22
lines changed

1 file changed

+82
-22
lines changed

src/index.ts

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
interface QueryOptions {
22
path: `v1/${string}`;
33
method?: "GET" | "POST" | "PUT";
4-
payload?: Record<string, any>;
4+
payload?: Record<string, unknown>;
55
params?: Record<string, string>;
66
}
77

@@ -14,8 +14,6 @@ interface ApiKeyErrorResponse {
1414
error: "Invalid API key";
1515
}
1616

17-
type ApiKeyResponse = ApiKeySuccessResponse | ApiKeyErrorResponse;
18-
1917
interface ContactSuccessResponse {
2018
success: true;
2119
/** The ID of the contact. */
@@ -188,13 +186,19 @@ interface TransactionalEmail {
188186

189187
interface ContactProperty {
190188
/**
189+
* The property's name.
190+
*/
191+
key: string;
192+
/**
193+
* The human-friendly label for this property.
191194
*/
192195
label: string;
193196
/**
194197
* The type of property.
195198
*/
196199
type: "string" | "number" | "boolean" | "date";
197200
}
201+
198202
interface ListTransactionalsResponse {
199203
pagination: PaginationData;
200204
data: TransactionalEmail[];
@@ -213,13 +217,29 @@ class RateLimitExceededError extends Error {
213217

214218
class APIError extends Error {
215219
statusCode: number;
216-
json: ErrorResponse | TransactionalError | TransactionalNestedError;
217-
json: ErrorResponse | TransactionalError | TransactionalNestedError
220+
json:
221+
| ErrorResponse
222+
| TransactionalError
223+
| TransactionalNestedError
224+
| ApiKeyErrorResponse;
225+
constructor(
226+
statusCode: number,
227+
json:
228+
| ErrorResponse
229+
| TransactionalError
230+
| TransactionalNestedError
231+
| ApiKeyErrorResponse
218232
) {
219233
let message: string | undefined;
220-
if ("error" in json && json.error?.message) {
234+
if (
235+
"error" in json &&
236+
typeof json.error === "object" &&
237+
json.error?.message
238+
) {
221239
message = json.error.message;
222-
} else if ("message" in json) {
240+
} else if ("error" in json && typeof json.error === "string") {
241+
message = json.error;
242+
} else if ("message" in json && typeof json.message === "string") {
223243
message = json.message;
224244
}
225245
super(`${statusCode}${message ? ` - ${message}` : ""}`);
@@ -234,6 +254,13 @@ class APIError extends Error {
234254
}
235255
}
236256

257+
class ValidationError extends Error {
258+
constructor(message: string) {
259+
super(message);
260+
this.name = "ValidationError";
261+
}
262+
}
263+
237264
class LoopsClient {
238265
apiKey: string;
239266
apiRoot = "https://app.loops.so/api/";
@@ -251,20 +278,20 @@ class LoopsClient {
251278
* @param {Object} params.payload Payload for PUT and POST requests
252279
* @param {Object} params.params URL query parameters
253280
*/
254-
private async _makeQuery({
281+
private async _makeQuery<T>({
255282
path,
256283
method = "GET",
257284
payload,
258285
params,
259-
}: QueryOptions) {
286+
}: QueryOptions): Promise<T> {
260287
const headers = new Headers();
261288
headers.set("Authorization", `Bearer ${this.apiKey}`);
262289
headers.set("Content-Type", "application/json");
263290

264291
const url = new URL(path, this.apiRoot);
265292
if (params && method === "GET") {
266293
Object.entries(params).forEach(([key, value]) =>
267-
url.searchParams.append(key, value)
294+
url.searchParams.append(key, value as string)
268295
);
269296
}
270297

@@ -305,9 +332,9 @@ class LoopsClient {
305332
*
306333
* @see https://loops.so/docs/api-reference/api-key
307334
*
308-
* @returns {Object} Success or error message (JSON)
335+
* @returns {Object} Success response (JSON)
309336
*/
310-
async testApiKey(): Promise<ApiKeyResponse> {
337+
async testApiKey(): Promise<ApiKeySuccessResponse> {
311338
return this._makeQuery({
312339
path: "v1/api-key",
313340
});
@@ -322,7 +349,7 @@ class LoopsClient {
322349
*
323350
* @see https://loops.so/docs/api-reference/create-contact
324351
*
325-
* @returns {Object} Contact record or error response (JSON)
352+
* @returns {Object} Contact record (JSON)
326353
*/
327354
async createContact(
328355
email: string,
@@ -346,7 +373,7 @@ class LoopsClient {
346373
*
347374
* @see https://loops.so/docs/api-reference/update-contact
348375
*
349-
* @returns {Object} Contact record or error response (JSON)
376+
* @returns {Object} Contact record (JSON)
350377
*/
351378
async updateContact(
352379
email: string,
@@ -379,11 +406,15 @@ class LoopsClient {
379406
email?: string;
380407
userId?: string;
381408
}): Promise<Contact[]> {
382-
if (email && userId) throw "Only one parameter is permitted.";
409+
if (email && userId)
410+
throw new ValidationError("Only one parameter is permitted.");
411+
if (!email && !userId)
412+
throw new ValidationError(
413+
"You must provide an `email` or `userId` value."
414+
);
383415
const params: { email?: string; userId?: string } = {};
384416
if (email) params["email"] = email;
385417
else if (userId) params["userId"] = userId;
386-
else throw "You must provide an `email` or `userId` value.";
387418
return this._makeQuery({
388419
path: "v1/contacts/find",
389420
params,
@@ -399,7 +430,7 @@ class LoopsClient {
399430
*
400431
* @see https://loops.so/docs/api-reference/delete-contact
401432
*
402-
* @returns {Object} Confirmation or error response (JSON)
433+
* @returns {Object} Confirmation (JSON)
403434
*/
404435
async deleteContact({
405436
email,
@@ -408,11 +439,15 @@ class LoopsClient {
408439
email?: string;
409440
userId?: string;
410441
}): Promise<DeleteSuccessResponse> {
411-
if (email && userId) throw "Only one parameter is permitted.";
442+
if (email && userId)
443+
throw new ValidationError("Only one parameter is permitted.");
444+
if (!email && !userId)
445+
throw new ValidationError(
446+
"You must provide an `email` or `userId` value."
447+
);
412448
const payload: { email?: string; userId?: string } = {};
413449
if (email) payload["email"] = email;
414450
else if (userId) payload["userId"] = userId;
415-
else throw "You must provide an `email` or `userId` value.";
416451
return this._makeQuery({
417452
path: "v1/contacts/delete",
418453
method: "POST",
@@ -428,7 +463,7 @@ class LoopsClient {
428463
*
429464
* @see https://loops.so/docs/api-reference/create-contact-property
430465
*
431-
* @returns {Object} Contact property record or error response (JSON)
466+
* @returns {Object} Contact property record (JSON)
432467
*/
433468
async createContactProperty(
434469
name: string,
@@ -540,7 +575,7 @@ class LoopsClient {
540575
*
541576
* @see https://loops.so/docs/api-reference/send-transactional-email
542577
*
543-
* @returns {Object} Confirmation or error response (JSON)
578+
* @returns {Object} Confirmation (JSON)
544579
*/
545580
async sendTransactionalEmail({
546581
transactionalId,
@@ -598,4 +633,29 @@ class LoopsClient {
598633
}
599634
}
600635

601-
export { LoopsClient, RateLimitExceededError, APIError };
636+
export {
637+
LoopsClient,
638+
RateLimitExceededError,
639+
APIError,
640+
ValidationError,
641+
ApiKeySuccessResponse,
642+
ApiKeyErrorResponse,
643+
ContactSuccessResponse,
644+
DeleteSuccessResponse,
645+
ErrorResponse,
646+
Contact,
647+
ContactProperty,
648+
ContactPropertySuccessResponse,
649+
EventSuccessResponse,
650+
TransactionalSuccess,
651+
TransactionalError,
652+
TransactionalNestedError,
653+
ContactProperties,
654+
EventProperties,
655+
TransactionalVariables,
656+
TransactionalAttachment,
657+
MailingList,
658+
PaginationData,
659+
TransactionalEmail,
660+
ListTransactionalsResponse,
661+
};

0 commit comments

Comments
 (0)