semaphoreco 1.0.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.
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Context information about an HTTP request, used for error reporting.
3
+ * API keys in headers are redacted for security.
4
+ */
5
+ interface RequestContext {
6
+ url: string;
7
+ method: string;
8
+ headers?: Record<string, string>;
9
+ }
10
+ /**
11
+ * Options for HTTP requests made by the client.
12
+ */
13
+ interface HttpRequestOptions {
14
+ method?: 'GET' | 'POST' | 'DELETE';
15
+ body?: unknown;
16
+ timeout?: number;
17
+ headers?: Record<string, string>;
18
+ }
19
+ /**
20
+ * Options for configuring retry behavior.
21
+ */
22
+ interface RetryOptions {
23
+ maxRetries?: number;
24
+ onRetry?: (attempt: number, delayMs: number) => void;
25
+ }
26
+
27
+ /**
28
+ * Configuration for HttpClient.
29
+ */
30
+ interface HttpClientConfig {
31
+ baseUrl: string;
32
+ apiKey: string;
33
+ timeout: number;
34
+ maxRetries?: number;
35
+ onRetry?: (attempt: number, delayMs: number) => void;
36
+ }
37
+ /**
38
+ * HTTP client with timeout and retry support for the Semaphore API.
39
+ *
40
+ * Features:
41
+ * - Configurable timeout using AbortSignal.timeout()
42
+ * - Automatic retry on 429 (rate limit) responses with Retry-After header support
43
+ * - Maps HTTP status codes to typed error classes
44
+ * - Includes sanitized request context in errors for debugging
45
+ */
46
+ declare class HttpClient {
47
+ private readonly baseUrl;
48
+ private readonly apiKey;
49
+ private readonly timeout;
50
+ private readonly maxRetries;
51
+ private readonly onRetry?;
52
+ constructor(config: HttpClientConfig);
53
+ /**
54
+ * Map HTTP response to appropriate error class.
55
+ *
56
+ * @param response - The HTTP response
57
+ * @param bodyText - Response body text (first 500 chars)
58
+ * @returns Appropriate error instance
59
+ */
60
+ private mapResponseToError;
61
+ /**
62
+ * Make an HTTP request to the Semaphore API.
63
+ *
64
+ * @param path - API path (e.g., "/messages")
65
+ * @param options - Request and retry options
66
+ * @returns Parsed JSON response
67
+ * @throws {NetworkError} On connection failures
68
+ * @throws {TimeoutError} On request timeout
69
+ * @throws {AuthenticationError} On 401 responses
70
+ * @throws {RateLimitError} On 429 responses (after retries exhausted)
71
+ * @throws {ValidationError} On 4xx responses
72
+ * @throws {SemaphoreError} On 5xx responses
73
+ */
74
+ request<T>(path: string, options?: HttpRequestOptions & RetryOptions): Promise<T>;
75
+ }
76
+
77
+ /** Configuration options for SemaphoreClient */
78
+ interface SemaphoreClientOptions {
79
+ /** API key. Falls back to SEMAPHORE_API_KEY env var if not provided */
80
+ apiKey?: string;
81
+ /** Base URL. Defaults to https://api.semaphore.co/api/v4 */
82
+ baseUrl?: string;
83
+ /** Request timeout in ms. Defaults to 30000 */
84
+ timeout?: number;
85
+ /** Callback invoked when a rate-limited request is retried */
86
+ onRetry?: (attempt: number, delayMs: number) => void;
87
+ }
88
+ /** Generic paginated response wrapper */
89
+ interface PaginatedResponse<T> {
90
+ data: T[];
91
+ total?: number;
92
+ page?: number;
93
+ limit?: number;
94
+ }
95
+
96
+ /** Request to send SMS message(s) */
97
+ interface SendMessageRequest {
98
+ /** Recipient number(s). Single string or array up to 1000 */
99
+ number: string | string[];
100
+ /** Message content */
101
+ message: string;
102
+ /** Approved sender name */
103
+ sendername: string;
104
+ }
105
+ /** Response from send message API */
106
+ interface SendMessageResponse {
107
+ message_id: number;
108
+ user_id: number;
109
+ user: string;
110
+ account_id: number;
111
+ account: string;
112
+ recipient: string;
113
+ message: string;
114
+ sender_name: string;
115
+ network: string;
116
+ status: string;
117
+ type: string;
118
+ source: string;
119
+ created_at: string;
120
+ updated_at: string;
121
+ }
122
+ /** Bulk send returns array of responses */
123
+ type BulkSendMessageResponse = SendMessageResponse[];
124
+ /** Request params for listing messages */
125
+ interface MessageListRequest {
126
+ page?: number;
127
+ limit?: number;
128
+ startDate?: string;
129
+ endDate?: string;
130
+ network?: string;
131
+ status?: string;
132
+ sendername?: string;
133
+ }
134
+ /** Message object from list/get endpoints */
135
+ interface Message {
136
+ id: number;
137
+ message_id: number;
138
+ user_id: number;
139
+ user: string;
140
+ account_id: number;
141
+ account: string;
142
+ recipient: string;
143
+ message: string;
144
+ sender_name: string;
145
+ network: string;
146
+ status: string;
147
+ type: string;
148
+ source: string;
149
+ created_at: string;
150
+ updated_at: string;
151
+ }
152
+
153
+ /** Request to send OTP message */
154
+ interface SendOtpRequest {
155
+ /** Recipient phone number */
156
+ number: string;
157
+ /** OTP message template. Use {otp} placeholder */
158
+ message: string;
159
+ /** Approved sender name */
160
+ sendername: string;
161
+ /** OTP code length (default 6) */
162
+ code_length?: number;
163
+ }
164
+ /** Response from send OTP API */
165
+ interface SendOtpResponse {
166
+ message_id: number;
167
+ user_id: number;
168
+ user: string;
169
+ account_id: number;
170
+ account: string;
171
+ recipient: string;
172
+ message: string;
173
+ sender_name: string;
174
+ network: string;
175
+ status: string;
176
+ type: string;
177
+ source: string;
178
+ code: string;
179
+ created_at: string;
180
+ updated_at: string;
181
+ }
182
+
183
+ /** Account information and balance */
184
+ interface AccountInfo {
185
+ account_id: number;
186
+ account_name: string;
187
+ status: string;
188
+ credit_balance: string;
189
+ }
190
+ /** Transaction history entry */
191
+ interface Transaction {
192
+ id: number;
193
+ account_id: number;
194
+ user_id: number;
195
+ type: string;
196
+ amount: string;
197
+ balance_before: string;
198
+ balance_after: string;
199
+ description: string;
200
+ created_at: string;
201
+ }
202
+ /** Request params for transaction history */
203
+ interface TransactionListRequest {
204
+ page?: number;
205
+ limit?: number;
206
+ startDate?: string;
207
+ endDate?: string;
208
+ }
209
+ /** Approved sender name */
210
+ interface SenderName {
211
+ name: string;
212
+ status: string;
213
+ created_at: string;
214
+ }
215
+
216
+ /**
217
+ * Resource class for SMS message operations.
218
+ *
219
+ * Provides methods for sending messages, listing sent messages,
220
+ * and retrieving message details by ID.
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const client = new SemaphoreClient({ apiKey: 'your-api-key' });
225
+ *
226
+ * // Send a single message
227
+ * const response = await client.messages.send({
228
+ * number: '09171234567',
229
+ * message: 'Hello!',
230
+ * sendername: 'SEMAPHORE',
231
+ * });
232
+ * ```
233
+ */
234
+ declare class MessagesResource {
235
+ private readonly http;
236
+ /**
237
+ * Create a new MessagesResource instance.
238
+ *
239
+ * @param http - HttpClient instance for making API requests
240
+ */
241
+ constructor(http: HttpClient);
242
+ /**
243
+ * Send an SMS message to one or more recipients.
244
+ *
245
+ * When sending to multiple recipients (up to 1000), pass an array of numbers.
246
+ * The API will return an array of responses, one for each recipient.
247
+ *
248
+ * @param params - Message parameters including recipient(s), message content, and sender name
249
+ * @returns Single response for one recipient, array of responses for multiple recipients
250
+ *
251
+ * @example
252
+ * ```typescript
253
+ * // Send to single recipient
254
+ * const response = await client.messages.send({
255
+ * number: '09171234567',
256
+ * message: 'Hello!',
257
+ * sendername: 'SEMAPHORE',
258
+ * });
259
+ * console.log(response.message_id);
260
+ *
261
+ * // Send to multiple recipients (bulk)
262
+ * const responses = await client.messages.send({
263
+ * number: ['09171234567', '09181234567'],
264
+ * message: 'Hello everyone!',
265
+ * sendername: 'SEMAPHORE',
266
+ * });
267
+ * console.log(responses.length); // 2
268
+ * ```
269
+ */
270
+ send(params: SendMessageRequest): Promise<SendMessageResponse | BulkSendMessageResponse>;
271
+ /**
272
+ * List sent messages with optional filtering.
273
+ *
274
+ * Supports pagination and filtering by date range, network, status, and sender name.
275
+ *
276
+ * @param params - Optional filter parameters
277
+ * @returns Array of message objects
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * // List all messages
282
+ * const messages = await client.messages.list();
283
+ *
284
+ * // List with filters
285
+ * const filtered = await client.messages.list({
286
+ * page: 1,
287
+ * limit: 50,
288
+ * status: 'Sent',
289
+ * startDate: '2024-01-01',
290
+ * endDate: '2024-01-31',
291
+ * });
292
+ * ```
293
+ */
294
+ list(params?: MessageListRequest): Promise<Message[]>;
295
+ /**
296
+ * Get a specific message by ID.
297
+ *
298
+ * @param id - The message ID to retrieve
299
+ * @returns The message object
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const message = await client.messages.get(12345);
304
+ * console.log(message.status); // 'Sent'
305
+ * console.log(message.recipient); // '09171234567'
306
+ * ```
307
+ */
308
+ get(id: number): Promise<Message>;
309
+ /**
310
+ * Build query string from optional MessageListRequest parameters.
311
+ *
312
+ * @param params - Optional filter parameters
313
+ * @returns Query string including leading '?' if params exist, empty string otherwise
314
+ */
315
+ private buildQueryString;
316
+ }
317
+
318
+ /**
319
+ * Resource for sending OTP (One-Time Password) messages.
320
+ *
321
+ * OTP messages use a priority route with no rate limit.
322
+ * Each message costs 2 credits per 160 characters.
323
+ */
324
+ declare class OtpResource {
325
+ private readonly http;
326
+ constructor(http: HttpClient);
327
+ /**
328
+ * Send an OTP message to a recipient.
329
+ *
330
+ * Uses priority route - no rate limit, 2 credits per 160-char message.
331
+ * The API generates the OTP code and replaces the {otp} placeholder in your message.
332
+ *
333
+ * @param params - OTP parameters including recipient, message template, sender name
334
+ * @returns Response including the generated OTP code
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * const response = await client.otp.send({
339
+ * number: '+639171234567',
340
+ * message: 'Your verification code is {otp}',
341
+ * sendername: 'MyApp',
342
+ * });
343
+ * console.log('OTP sent:', response.code);
344
+ * ```
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * // With custom code length
349
+ * const response = await client.otp.send({
350
+ * number: '+639171234567',
351
+ * message: 'Your code is {otp}',
352
+ * sendername: 'MyApp',
353
+ * code_length: 4,
354
+ * });
355
+ * ```
356
+ */
357
+ send(params: SendOtpRequest): Promise<SendOtpResponse>;
358
+ }
359
+
360
+ /**
361
+ * Resource for retrieving account information.
362
+ */
363
+ declare class AccountResource {
364
+ private readonly http;
365
+ constructor(http: HttpClient);
366
+ /**
367
+ * Get account information including credit balance.
368
+ *
369
+ * @returns Account info with balance
370
+ *
371
+ * @example
372
+ * ```typescript
373
+ * const info = await client.account.info();
374
+ * console.log('Account:', info.account_name);
375
+ * console.log('Balance:', info.credit_balance);
376
+ * ```
377
+ */
378
+ info(): Promise<AccountInfo>;
379
+ /**
380
+ * Get transaction history.
381
+ *
382
+ * @param params - Optional pagination and date filter parameters
383
+ * @returns Array of transactions
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * // Get all transactions
388
+ * const transactions = await client.account.transactions();
389
+ * ```
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * // Get paginated transactions with date range
394
+ * const transactions = await client.account.transactions({
395
+ * page: 1,
396
+ * limit: 50,
397
+ * startDate: '2026-01-01',
398
+ * endDate: '2026-01-31',
399
+ * });
400
+ * ```
401
+ */
402
+ transactions(params?: TransactionListRequest): Promise<Transaction[]>;
403
+ /**
404
+ * Get list of approved sender names.
405
+ *
406
+ * @returns Array of sender names with status
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const senderNames = await client.account.senderNames();
411
+ * for (const sender of senderNames) {
412
+ * console.log(`${sender.name}: ${sender.status}`);
413
+ * }
414
+ * ```
415
+ */
416
+ senderNames(): Promise<SenderName[]>;
417
+ /**
418
+ * Build query string from optional transaction list parameters.
419
+ *
420
+ * @param params - Optional filter parameters
421
+ * @returns Query string (empty string or ?param1=value1&param2=value2)
422
+ */
423
+ private buildQueryString;
424
+ }
425
+
426
+ /**
427
+ * Semaphore API client for sending SMS and OTP messages.
428
+ *
429
+ * @example
430
+ * ```typescript
431
+ * // Send SMS
432
+ * const client = new SemaphoreClient({ apiKey: 'your-api-key' });
433
+ * await client.messages.send({
434
+ * number: '+639171234567',
435
+ * message: 'Hello!',
436
+ * sendername: 'MyApp',
437
+ * });
438
+ *
439
+ * // Check balance
440
+ * const info = await client.account.info();
441
+ * console.log('Balance:', info.credit_balance);
442
+ * ```
443
+ */
444
+ declare class SemaphoreClient {
445
+ /** @internal HTTP client used by resource classes. */
446
+ protected readonly http: HttpClient;
447
+ /** SMS message operations */
448
+ readonly messages: MessagesResource;
449
+ /** OTP message operations (priority route, no rate limit) */
450
+ readonly otp: OtpResource;
451
+ /** Account information operations */
452
+ readonly account: AccountResource;
453
+ constructor(options?: SemaphoreClientOptions);
454
+ }
455
+
456
+ /**
457
+ * Base error class for all Semaphore API errors.
458
+ * Extend this for specific error types.
459
+ */
460
+ declare class SemaphoreError extends Error {
461
+ readonly statusCode?: number;
462
+ readonly code?: string;
463
+ constructor(message: string, statusCode?: number, code?: string);
464
+ }
465
+ /**
466
+ * Thrown when the API key is invalid or missing.
467
+ */
468
+ declare class AuthenticationError extends SemaphoreError {
469
+ constructor(message?: string);
470
+ }
471
+ /**
472
+ * Thrown when rate limits are exceeded.
473
+ * Check `retryAfter` for seconds to wait before retrying.
474
+ */
475
+ declare class RateLimitError extends SemaphoreError {
476
+ readonly retryAfter?: number;
477
+ constructor(message?: string, retryAfter?: number);
478
+ }
479
+ /**
480
+ * Thrown when request parameters are invalid.
481
+ */
482
+ declare class ValidationError extends SemaphoreError {
483
+ readonly field?: string;
484
+ constructor(message: string, field?: string);
485
+ }
486
+ /**
487
+ * Thrown when a network error occurs (connection failure, DNS resolution, etc.).
488
+ * The `request` property contains context about the failed request.
489
+ * The `cause` property contains the underlying error if available.
490
+ */
491
+ declare class NetworkError extends SemaphoreError {
492
+ readonly request: RequestContext;
493
+ readonly cause?: Error;
494
+ constructor(message: string, request: RequestContext, cause?: Error);
495
+ }
496
+ /**
497
+ * Thrown when a request times out.
498
+ * The `request` property contains context about the timed-out request.
499
+ * The `timeoutMs` property indicates the timeout duration that was exceeded.
500
+ */
501
+ declare class TimeoutError extends SemaphoreError {
502
+ readonly request: RequestContext;
503
+ readonly timeoutMs: number;
504
+ constructor(message: string, request: RequestContext, timeoutMs: number);
505
+ }
506
+
507
+ export { type AccountInfo, AccountResource, AuthenticationError, type BulkSendMessageResponse, HttpClient, type HttpRequestOptions, type Message, type MessageListRequest, MessagesResource, NetworkError, OtpResource, type PaginatedResponse, RateLimitError, type RequestContext, type RetryOptions, SemaphoreClient, type SemaphoreClientOptions, SemaphoreError, type SendMessageRequest, type SendMessageResponse, type SendOtpRequest, type SendOtpResponse, type SenderName, TimeoutError, type Transaction, type TransactionListRequest, ValidationError };