sendrly
Version:
Official Sendrly TypeScript/JavaScript SDK - Simple, type-safe email automation for developers
321 lines (320 loc) • 15 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Sendrly_instances, _Sendrly_apiKey, _Sendrly_debug, _Sendrly_baseUrl, _Sendrly_timeout, _Sendrly_maxRetries, _Sendrly_fetch, _Sendrly_logger, _Sendrly_log, _Sendrly_logError, _Sendrly_makeRequest;
import { validatePayload } from './validation';
import { SendrlyError } from './types';
import { ZodError } from 'zod';
const DEFAULT_BASE_URL = 'https://stebgknkomdhjikpcytk.supabase.co/functions/v1';
const DEFAULT_TIMEOUT = 10000;
const DEFAULT_MAX_RETRIES = 3;
const DEFAULT_LOGGER = {
debug: console.log,
error: console.error
};
/**
* Main Sendrly SDK class for tracking events and managing contacts
* @example
* // Basic usage - just API key
* const sendrly = new Sendrly('your_api_key')
*
* // Send an event with minimal config
* await sendrly.sendEvent({
* contact: {
* email: 'user@example.com'
* },
* event: {
* name: 'signup.completed'
* }
* })
*
* // Advanced usage with full config
* const sendrly = new Sendrly({
* apiKey: 'your_api_key',
* debug: true,
* timeout: 5000
* })
*/
export class Sendrly {
constructor(config) {
_Sendrly_instances.add(this);
_Sendrly_apiKey.set(this, void 0);
_Sendrly_debug.set(this, void 0);
_Sendrly_baseUrl.set(this, void 0);
_Sendrly_timeout.set(this, void 0);
_Sendrly_maxRetries.set(this, void 0);
_Sendrly_fetch.set(this, void 0);
_Sendrly_logger.set(this, void 0);
// Handle string constructor for simple API key usage
if (typeof config === 'string') {
__classPrivateFieldSet(this, _Sendrly_apiKey, config, "f");
__classPrivateFieldSet(this, _Sendrly_debug, false, "f");
__classPrivateFieldSet(this, _Sendrly_baseUrl, DEFAULT_BASE_URL, "f");
__classPrivateFieldSet(this, _Sendrly_timeout, DEFAULT_TIMEOUT, "f");
__classPrivateFieldSet(this, _Sendrly_maxRetries, DEFAULT_MAX_RETRIES, "f");
__classPrivateFieldSet(this, _Sendrly_fetch, typeof window !== 'undefined' ? window.fetch.bind(window) : fetch, "f");
__classPrivateFieldSet(this, _Sendrly_logger, DEFAULT_LOGGER, "f");
}
else {
__classPrivateFieldSet(this, _Sendrly_apiKey, config.apiKey, "f");
__classPrivateFieldSet(this, _Sendrly_debug, config.debug ?? false, "f");
__classPrivateFieldSet(this, _Sendrly_baseUrl, config.baseUrl ?? DEFAULT_BASE_URL, "f");
__classPrivateFieldSet(this, _Sendrly_timeout, config.timeout ?? DEFAULT_TIMEOUT, "f");
__classPrivateFieldSet(this, _Sendrly_maxRetries, config.maxRetries ?? DEFAULT_MAX_RETRIES, "f");
__classPrivateFieldSet(this, _Sendrly_fetch, config.fetch ??
(typeof window !== 'undefined' ? window.fetch.bind(window) : fetch), "f");
__classPrivateFieldSet(this, _Sendrly_logger, config.logger ?? DEFAULT_LOGGER, "f");
}
if (__classPrivateFieldGet(this, _Sendrly_debug, "f")) {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Initialized with config:', {
apiKey: __classPrivateFieldGet(this, _Sendrly_apiKey, "f"),
debug: __classPrivateFieldGet(this, _Sendrly_debug, "f"),
baseUrl: __classPrivateFieldGet(this, _Sendrly_baseUrl, "f"),
timeout: __classPrivateFieldGet(this, _Sendrly_timeout, "f"),
maxRetries: __classPrivateFieldGet(this, _Sendrly_maxRetries, "f")
});
}
if (!__classPrivateFieldGet(this, _Sendrly_apiKey, "f")) {
throw new SendrlyError('API key is required', 'INVALID_CONFIG');
}
}
/**
* Send an event for a contact
* @example
* // Minimal usage
* await sendrly.sendEvent({
* contact: {
* email: 'user@example.com'
* },
* event: {
* name: 'signup.completed'
* }
* })
*
* // Full usage with all options
* await sendrly.sendEvent({
* contact: {
* email: 'sarah@startup.com',
* marketing_opt_out: false,
* properties: {
* name: 'Sarah',
* company: 'Startup Inc'
* }
* },
* event: {
* name: 'user.welcome',
* properties: {
* source: 'signup',
* referral: 'google'
* }
* }
* })
*/
async sendEvent(payload) {
try {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Sending event with payload:', payload);
const validatedPayload = validatePayload(payload);
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Payload validated successfully:', validatedPayload);
const response = await __classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_makeRequest).call(this, '/event', {
method: 'POST',
body: JSON.stringify(validatedPayload)
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new SendrlyError(data.error || 'Failed to send event', 'API_ERROR', response.status);
}
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Event sent successfully:', payload);
}
catch (error) {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_logError).call(this, 'Error sending event:', error);
if (error instanceof SendrlyError || error instanceof ZodError) {
throw error;
}
throw new SendrlyError(error instanceof Error ? error.message : 'An unknown error occurred', 'UNKNOWN_ERROR');
}
}
/**
* Get a contact by email address
* @example
* // Get contact details
* const contact = await sendrly.getContact('sarah@startup.com')
* console.log(contact.properties.name) // 'Sarah'
*
* // Handle non-existent contact
* try {
* const contact = await sendrly.getContact('unknown@email.com')
* } catch (error) {
* if (error.code === 'NOT_FOUND') {
* console.log('Contact does not exist')
* }
* }
*/
async getContact(email) {
try {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Getting contact:', email);
const response = await fetch(`${__classPrivateFieldGet(this, _Sendrly_baseUrl, "f")}/find-contact?email=${encodeURIComponent(email)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
apikey: __classPrivateFieldGet(this, _Sendrly_apiKey, "f")
}
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new SendrlyError(data.error || 'Failed to get contact', response.status === 404
? 'NOT_FOUND'
: response.status === 401
? 'INVALID_API_KEY'
: 'API_ERROR');
}
return data.contact;
}
catch (error) {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Error getting contact:', error);
if (error instanceof SendrlyError) {
throw error;
}
throw new SendrlyError(error instanceof Error ? error.message : 'An unknown error occurred', 'UNKNOWN_ERROR');
}
}
/**
* List all events with a specific name
* @example
* // List all signup events
* const signups = await sendrly.listEvents('user.signup')
* console.log(signups[0].event_properties.source)
*
* // Process completed orders
* const orders = await sendrly.listEvents('order.completed')
* const totalRevenue = orders.reduce((sum, order) =>
* sum + order.event_properties.amount, 0
* )
*/
async listEvents(eventName) {
try {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Listing events:', eventName);
const response = await fetch(`${__classPrivateFieldGet(this, _Sendrly_baseUrl, "f")}/get-event?event=${encodeURIComponent(eventName)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
apikey: __classPrivateFieldGet(this, _Sendrly_apiKey, "f")
}
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new SendrlyError(data.error || 'Failed to list events', response.status === 401 ? 'INVALID_API_KEY' : 'API_ERROR');
}
return data.events;
}
catch (error) {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Error listing events:', error);
if (error instanceof SendrlyError) {
throw error;
}
throw new SendrlyError(error instanceof Error ? error.message : 'An unknown error occurred', 'UNKNOWN_ERROR');
}
}
/**
* Delete a contact and all associated data
* @example
* // Delete a contact
* await sendrly.deleteContact('sarah@startup.com')
*
* // Handle non-existent contact
* try {
* await sendrly.deleteContact('unknown@email.com')
* } catch (error) {
* if (error.code === 'NOT_FOUND') {
* console.log('Contact already deleted')
* }
* }
*/
async deleteContact(email) {
try {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Deleting contact:', email);
const response = await fetch(`${__classPrivateFieldGet(this, _Sendrly_baseUrl, "f")}/delete-contact?email=${encodeURIComponent(email)}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
apikey: __classPrivateFieldGet(this, _Sendrly_apiKey, "f")
}
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new SendrlyError(data.error || 'Failed to delete contact', response.status === 404
? 'NOT_FOUND'
: response.status === 401
? 'INVALID_API_KEY'
: 'API_ERROR');
}
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Contact deleted successfully');
}
catch (error) {
__classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_log).call(this, 'Error deleting contact:', error);
if (error instanceof SendrlyError) {
throw error;
}
throw new SendrlyError(error instanceof Error ? error.message : 'An unknown error occurred', 'UNKNOWN_ERROR');
}
}
}
_Sendrly_apiKey = new WeakMap(), _Sendrly_debug = new WeakMap(), _Sendrly_baseUrl = new WeakMap(), _Sendrly_timeout = new WeakMap(), _Sendrly_maxRetries = new WeakMap(), _Sendrly_fetch = new WeakMap(), _Sendrly_logger = new WeakMap(), _Sendrly_instances = new WeakSet(), _Sendrly_log = function _Sendrly_log(message, data) {
if (__classPrivateFieldGet(this, _Sendrly_debug, "f")) {
__classPrivateFieldGet(this, _Sendrly_logger, "f").debug(`[Sendrly] ${message}`, data);
}
}, _Sendrly_logError = function _Sendrly_logError(message, error) {
if (__classPrivateFieldGet(this, _Sendrly_debug, "f")) {
__classPrivateFieldGet(this, _Sendrly_logger, "f").error(`[Sendrly] ${message}`, error);
}
}, _Sendrly_makeRequest =
/**
* Makes an API request with retry logic and timeout
*/
async function _Sendrly_makeRequest(endpoint, options, retryCount = 0) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), __classPrivateFieldGet(this, _Sendrly_timeout, "f"));
try {
const response = await __classPrivateFieldGet(this, _Sendrly_fetch, "f").call(this, `${__classPrivateFieldGet(this, _Sendrly_baseUrl, "f")}${endpoint}`, {
...options,
signal: controller.signal,
headers: {
'Content-Type': 'application/json',
apikey: __classPrivateFieldGet(this, _Sendrly_apiKey, "f"),
...options.headers
}
});
if (!response.ok && retryCount < __classPrivateFieldGet(this, _Sendrly_maxRetries, "f")) {
const error = new SendrlyError('Request failed', 'API_ERROR', response.status);
if (error.retryable) {
const delay = Math.min(1000 * 2 ** retryCount, 10000);
await new Promise((resolve) => setTimeout(resolve, delay));
return __classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_makeRequest).call(this, endpoint, options, retryCount + 1);
}
}
return response;
}
catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw new SendrlyError('Request timed out', 'TIMEOUT_ERROR');
}
if (retryCount < __classPrivateFieldGet(this, _Sendrly_maxRetries, "f")) {
const delay = Math.min(1000 * 2 ** retryCount, 10000);
await new Promise((resolve) => setTimeout(resolve, delay));
return __classPrivateFieldGet(this, _Sendrly_instances, "m", _Sendrly_makeRequest).call(this, endpoint, options, retryCount + 1);
}
throw new SendrlyError('Network request failed', 'NETWORK_ERROR');
}
finally {
clearTimeout(timeoutId);
}
};
// Default export for easier importing
export default Sendrly;