@reachflow/sdk 0.1.0 → 0.1.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @reachflow/sdk
2
2
 
3
- Client officiel **Node.js / TypeScript** pour l’[API publique ReachFlow](https://docs.reachflow.me/developpeurs) (REST v1).
3
+ Official **Node.js / TypeScript** client for the [ReachFlow public API](https://docs.reachflow.me/developpeurs) (REST v1).
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,7 +8,7 @@ Client officiel **Node.js / TypeScript** pour l’[API publique ReachFlow](https
8
8
  npm install @reachflow/sdk
9
9
  ```
10
10
 
11
- **Prérequis :** Node.js ≥ 18 (utilise `fetch` natif).
11
+ **Requirements:** Node.js ≥ 18 (native `fetch`).
12
12
 
13
13
  ## Configuration
14
14
 
@@ -16,22 +16,22 @@ npm install @reachflow/sdk
16
16
  import { ReachFlow } from '@reachflow/sdk';
17
17
 
18
18
  const client = new ReachFlow({
19
- apiKey: process.env.REACHFLOW_API_KEY!, // rfl_live_… ou rfl_test_…
20
- baseUrl: 'https://sandbox-api.reachflow.me', // optionnel
21
- timeoutMs: 30_000, // optionnel
22
- maxRetries: 2, // optionnel — 429 / 5xx
19
+ apiKey: process.env.REACHFLOW_API_KEY!, // rfl_live_… or rfl_test_…
20
+ baseUrl: 'https://sandbox-api.reachflow.me', // optional
21
+ timeoutMs: 30_000, // optional
22
+ maxRetries: 2, // optional — 429 / 5xx
23
23
  });
24
24
  ```
25
25
 
26
- ## Exemples
26
+ ## Examples
27
27
 
28
- ### Envoyer un message
28
+ ### Send a message
29
29
 
30
30
  ```typescript
31
31
  const { messageId } = await client.messages.send({
32
- providerId: 'uuid-du-provider',
32
+ providerId: 'your-provider-uuid',
33
33
  to: '22996123456',
34
- message: 'Votre commande est confirmée.',
34
+ message: 'Your order is confirmed.',
35
35
  });
36
36
 
37
37
  const status = await client.messages.waitForTerminal(messageId);
@@ -42,12 +42,12 @@ console.log(status.status); // sent | delivered | failed | …
42
42
 
43
43
  ```typescript
44
44
  const { otpId } = await client.otp.send({
45
- providerId: 'uuid-du-provider',
45
+ providerId: 'your-provider-uuid',
46
46
  phoneNumber: '22996123456',
47
- brandName: 'Mon App',
47
+ brandName: 'My App',
48
48
  });
49
49
 
50
- // Le code arrive sur WhatsApp — jamais dans la réponse JSON.
50
+ // The code is delivered on WhatsApp only never in the JSON response.
51
51
  const result = await client.otp.verify({ otpId, code: '482910' });
52
52
  console.log(result.valid);
53
53
  ```
@@ -59,7 +59,7 @@ const { providers } = await client.providers.list();
59
59
  const connected = await client.providers.findConnected();
60
60
  ```
61
61
 
62
- ## Gestion des erreurs
62
+ ## Error handling
63
63
 
64
64
  ```typescript
65
65
  import { ReachFlow, ReachFlowError } from '@reachflow/sdk';
@@ -70,24 +70,24 @@ try {
70
70
  if (err instanceof ReachFlowError) {
71
71
  console.error(err.statusCode, err.code, err.message);
72
72
  if (err.retryable) {
73
- // retry manuel possible
73
+ // manual retry if needed
74
74
  }
75
75
  }
76
76
  }
77
77
  ```
78
78
 
79
- | Code | Signification |
80
- |------|---------------|
81
- | `unauthorized` | Clé API invalide ou révoquée |
82
- | `plan_required` | Plan ou add-on API manquant |
83
- | `insufficient_scope` | Scope clé insuffisant |
84
- | `rate_limit_exceeded` | Limite req/min dépassée |
85
- | `validation_error` | Corps de requête invalide |
86
- | `not_found` | Ressource introuvable |
79
+ | Code | Meaning |
80
+ |------|---------|
81
+ | `unauthorized` | Invalid or revoked API key |
82
+ | `plan_required` | Plan or API add-on missing |
83
+ | `insufficient_scope` | API key scope insufficient |
84
+ | `rate_limit_exceeded` | HTTP rate limit exceeded |
85
+ | `validation_error` | Invalid request body |
86
+ | `not_found` | Resource not found |
87
87
 
88
- ## Idempotence
88
+ ## Idempotency
89
89
 
90
- Passez `idempotencyKey` sur `messages.send`, `sendMedia` ou `sendBulk` :
90
+ Pass `idempotencyKey` on `messages.send`, `sendMedia`, or `sendBulk`:
91
91
 
92
92
  ```typescript
93
93
  await client.messages.send({
@@ -96,7 +96,7 @@ await client.messages.send({
96
96
  });
97
97
  ```
98
98
 
99
- ## Développement
99
+ ## Development
100
100
 
101
101
  ```bash
102
102
  npm install
@@ -104,6 +104,6 @@ npm test
104
104
  npm run build
105
105
  ```
106
106
 
107
- ## Licence
107
+ ## License
108
108
 
109
109
  MIT
package/dist/index.cjs CHANGED
@@ -228,7 +228,7 @@ var MessagesResource = class {
228
228
  this.http = http;
229
229
  }
230
230
  http;
231
- /** Envoie un message texte (réponse HTTP 202). */
231
+ /** Send a text message (HTTP 202). */
232
232
  async send(params) {
233
233
  const { idempotencyKey, ...body } = params;
234
234
  return this.http.request({
@@ -238,7 +238,7 @@ var MessagesResource = class {
238
238
  idempotencyKey
239
239
  });
240
240
  }
241
- /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */
241
+ /** Send media via a public HTTPS URL (HTTP 202). */
242
242
  async sendMedia(params) {
243
243
  const { idempotencyKey, ...body } = params;
244
244
  return this.http.request({
@@ -248,7 +248,7 @@ var MessagesResource = class {
248
248
  idempotencyKey
249
249
  });
250
250
  }
251
- /** Envoie un lot de messages (même modèle, destinataires multiples). */
251
+ /** Send a bulk batch (same template, multiple recipients). */
252
252
  async sendBulk(params) {
253
253
  const { idempotencyKey, ...body } = params;
254
254
  return this.http.request({
@@ -258,7 +258,7 @@ var MessagesResource = class {
258
258
  idempotencyKey
259
259
  });
260
260
  }
261
- /** Consulte le statut d'un message précédemment accepté. */
261
+ /** Get the status of a previously accepted message. */
262
262
  async getStatus(messageId) {
263
263
  return this.http.request({
264
264
  method: "GET",
@@ -266,8 +266,8 @@ var MessagesResource = class {
266
266
  });
267
267
  }
268
268
  /**
269
- * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).
270
- * Lance `ReachFlowError` si timeout.
269
+ * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
270
+ * Throws `ReachFlowError` on timeout.
271
271
  */
272
272
  async waitForTerminal(messageId, options) {
273
273
  const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
@@ -299,8 +299,8 @@ var OtpResource = class {
299
299
  }
300
300
  http;
301
301
  /**
302
- * Génère et envoie un code OTP par WhatsApp.
303
- * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.
302
+ * Generate and send an OTP via WhatsApp.
303
+ * The code is never returned by the API — only delivered on WhatsApp.
304
304
  */
305
305
  async send(params) {
306
306
  return this.http.request({
@@ -309,7 +309,7 @@ var OtpResource = class {
309
309
  body: params
310
310
  });
311
311
  }
312
- /** Vérifie un code saisi par l'utilisateur final. */
312
+ /** Verify a code entered by the end user. */
313
313
  async verify(params) {
314
314
  return this.http.request({
315
315
  method: "POST",
@@ -325,14 +325,14 @@ var ProvidersResource = class {
325
325
  this.http = http;
326
326
  }
327
327
  http;
328
- /** Liste les numéros WhatsApp accessibles via l'API. */
328
+ /** List WhatsApp numbers available via the API. */
329
329
  async list() {
330
330
  return this.http.request({
331
331
  method: "GET",
332
332
  path: "/providers"
333
333
  });
334
334
  }
335
- /** Détail d'un provider avec statistiques journalières. */
335
+ /** Get a provider with daily statistics. */
336
336
  async get(providerId) {
337
337
  return this.http.request({
338
338
  method: "GET",
@@ -340,8 +340,8 @@ var ProvidersResource = class {
340
340
  });
341
341
  }
342
342
  /**
343
- * Retourne le premier provider au statut `connected`, ou le premier de la liste.
344
- * Utile pour les scripts de test / démarrage rapide.
343
+ * Return the first `connected` provider, or the first in the list.
344
+ * Handy for smoke tests and quick starts.
345
345
  */
346
346
  async findConnected() {
347
347
  const { providers } = await this.list();
@@ -367,7 +367,7 @@ var ReachFlow = class {
367
367
  this.providers = new ProvidersResource(this.http);
368
368
  this.otp = new OtpResource(this.http);
369
369
  }
370
- /** URL de base configurée (sans `/api/v1`). */
370
+ /** Configured base URL (without `/api/v1`). */
371
371
  get baseUrl() {
372
372
  return this.config.baseUrl;
373
373
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/types.ts","../src/http.ts","../src/resources/messages.ts","../src/resources/otp.ts","../src/resources/providers.ts","../src/client.ts"],"sourcesContent":["export { ReachFlow } from './client.js';\nexport { ReachFlowError } from './errors.js';\nexport type { ReachFlowErrorCode } from './errors.js';\nexport type {\n BulkRecipient,\n FailureCode,\n MediaType,\n MessageStatus,\n MessageStatusResponse,\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n OtpVerifyReason,\n ProviderDailyStats,\n ProviderDetail,\n ProviderListResponse,\n ProviderSummary,\n ReachFlowClientOptions,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from './types.js';\nexport {\n DEFAULT_BASE_URL,\n TERMINAL_MESSAGE_STATUSES,\n} from './types.js';\n","export type ReachFlowErrorCode =\n | 'unauthorized'\n | 'plan_required'\n | 'insufficient_scope'\n | 'rate_limit_exceeded'\n | 'too_many_auth_failures'\n | 'validation_error'\n | 'not_found'\n | 'api_error'\n | 'network_error'\n | 'timeout';\n\nexport interface ReachFlowErrorBody {\n statusCode?: number;\n error?: string;\n message?: string;\n}\n\nexport class ReachFlowError extends Error {\n readonly statusCode: number;\n readonly code: ReachFlowErrorCode;\n readonly retryable: boolean;\n readonly retryAfterMs?: number;\n readonly body?: unknown;\n\n constructor(params: {\n message: string;\n statusCode: number;\n code: ReachFlowErrorCode;\n retryable?: boolean;\n retryAfterMs?: number;\n body?: unknown;\n cause?: unknown;\n }) {\n super(params.message, params.cause ? { cause: params.cause } : undefined);\n this.name = 'ReachFlowError';\n this.statusCode = params.statusCode;\n this.code = params.code;\n this.retryable = params.retryable ?? false;\n this.retryAfterMs = params.retryAfterMs;\n this.body = params.body;\n }\n\n static fromResponse(\n status: number,\n body: unknown,\n fallbackMessage?: string,\n ): ReachFlowError {\n const parsed = (body ?? {}) as ReachFlowErrorBody;\n const apiError = parsed.error;\n const message =\n parsed.message ??\n fallbackMessage ??\n `ReachFlow API error (HTTP ${status})`;\n\n const code = mapApiErrorToCode(status, apiError);\n const retryable =\n status === 429 || status === 408 || (status >= 500 && status < 600);\n\n return new ReachFlowError({\n message,\n statusCode: status,\n code,\n retryable,\n body,\n });\n }\n\n static network(cause: unknown): ReachFlowError {\n return new ReachFlowError({\n message: 'Network error while calling ReachFlow API',\n statusCode: 0,\n code: 'network_error',\n retryable: true,\n cause,\n });\n }\n\n static timeout(timeoutMs: number): ReachFlowError {\n return new ReachFlowError({\n message: `Request timed out after ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: true,\n });\n }\n}\n\nfunction mapApiErrorToCode(\n status: number,\n apiError?: string,\n): ReachFlowErrorCode {\n switch (apiError) {\n case 'unauthorized':\n return 'unauthorized';\n case 'plan_required':\n return 'plan_required';\n case 'insufficient_scope':\n return 'insufficient_scope';\n case 'rate_limit_exceeded':\n return 'rate_limit_exceeded';\n case 'too_many_auth_failures':\n return 'too_many_auth_failures';\n default:\n break;\n }\n\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'insufficient_scope';\n if (status === 404) return 'not_found';\n if (status === 400 || status === 422) return 'validation_error';\n if (status === 429) return 'rate_limit_exceeded';\n return 'api_error';\n}\n\nexport function parseRetryAfterMs(header: string | null): number | undefined {\n if (!header) return undefined;\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n","/** Statut de cycle de vie d'un message API. */\nexport type MessageStatus =\n | 'queued'\n | 'processing'\n | 'sent'\n | 'delivered'\n | 'failed'\n | 'cancelled';\n\nexport type MediaType = 'image' | 'document' | 'audio' | 'video';\n\nexport type FailureCode =\n | 'warmup_daily_limit'\n | 'risk_circuit_open'\n | 'provider_disconnected'\n | 'provider_banned'\n | 'send_not_allowed'\n | 'instance_busy'\n | 'delivery_timeout'\n | 'delivery_failed'\n | 'send_error'\n | 'provider_not_found';\n\nexport type OtpVerifyReason =\n | 'invalidated'\n | 'already_used'\n | 'expired'\n | 'max_attempts_reached'\n | 'invalid_code'\n | 'not_found';\n\nexport interface ReachFlowClientOptions {\n /** Clé API `rfl_live_…` ou `rfl_test_…`. */\n apiKey: string;\n /**\n * URL de base sans `/api/v1` (défaut : sandbox ReachFlow).\n * @default \"https://sandbox-api.reachflow.me\"\n */\n baseUrl?: string;\n /** Timeout HTTP en millisecondes. @default 30000 */\n timeoutMs?: number;\n /** Implémentation fetch (tests, Node < 18). @default global fetch */\n fetch?: typeof fetch;\n /** Nombre max de retries sur 429 / 5xx retryables. @default 2 */\n maxRetries?: number;\n}\n\nexport interface SendMessageParams {\n providerId: string;\n to: string;\n message: string;\n variables?: Record<string, string>;\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendMediaParams {\n providerId: string;\n to: string;\n mediaUrl: string;\n mediaType: MediaType;\n caption?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface BulkRecipient {\n to: string;\n variables?: Record<string, string>;\n}\n\nexport interface SendBulkParams {\n providerId: string;\n messageTemplate: string;\n recipients: BulkRecipient[];\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendAcceptedResponse {\n messageId: string;\n status: 'queued';\n queuedAt: string;\n}\n\nexport interface BulkRejection {\n to: string;\n reason: string;\n}\n\nexport interface SendBulkResponse {\n bulkId: string;\n accepted: number;\n rejected: number;\n rejections: BulkRejection[];\n messageIds: string[];\n}\n\nexport interface MessageStatusResponse {\n messageId: string;\n status: MessageStatus;\n to: string;\n providerId: string;\n queuedAt: string;\n sentAt: string | null;\n deliveredAt: string | null;\n failedAt: string | null;\n failureCode: FailureCode | null;\n failureReason: string | null;\n}\n\nexport interface ProviderSummary {\n id: string;\n name: string;\n phoneNumber: string | null;\n status: string;\n warmupDay: number;\n riskScore: number;\n}\n\nexport interface ProviderDailyStats {\n sent: number;\n delivered: number;\n failed: number;\n messagesLimit: number | null;\n newContactsLimit: number | null;\n}\n\nexport interface ProviderDetail extends ProviderSummary {\n dailyStats: ProviderDailyStats;\n}\n\nexport interface ProviderListResponse {\n providers: ProviderSummary[];\n}\n\nexport interface OtpSendParams {\n providerId: string;\n phoneNumber: string;\n codeLength?: number;\n expiresIn?: number;\n brandName?: string;\n template?: string;\n saveContact?: boolean;\n}\n\nexport interface OtpSendResponse {\n otpId: string;\n messageId: string;\n expiresAt: string;\n}\n\nexport interface OtpVerifyParams {\n otpId: string;\n code: string;\n}\n\nexport interface OtpVerifyResponse {\n valid: boolean;\n reason?: OtpVerifyReason;\n attemptsLeft?: number;\n}\n\nexport interface WaitForTerminalOptions {\n /** Intervalle entre polls en ms. @default 2000 */\n pollIntervalMs?: number;\n /** Timeout total en ms. @default 120000 */\n timeoutMs?: number;\n}\n\nexport const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus> = new Set([\n 'sent',\n 'delivered',\n 'failed',\n 'cancelled',\n]);\n\nexport const DEFAULT_BASE_URL = 'https://sandbox-api.reachflow.me';\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport { DEFAULT_BASE_URL } from './types.js';\n\nexport interface RequestOptions {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idempotencyKey?: string;\n}\n\nexport interface ResolvedClientConfig {\n apiKey: string;\n baseUrl: string;\n apiPrefix: string;\n timeoutMs: number;\n fetchImpl: typeof fetch;\n maxRetries: number;\n}\n\nexport function resolveConfig(\n options: ReachFlowClientOptions,\n): ResolvedClientConfig {\n const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n baseUrl,\n apiPrefix: `${baseUrl}/api/v1`,\n timeoutMs: options.timeoutMs ?? 30_000,\n fetchImpl: options.fetch ?? globalThis.fetch,\n maxRetries: options.maxRetries ?? 2,\n };\n}\n\nexport class HttpClient {\n constructor(private readonly config: ResolvedClientConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n let attempt = 0;\n let lastError: ReachFlowError | undefined;\n\n while (attempt <= this.config.maxRetries) {\n try {\n return await this.requestOnce<T>(options);\n } catch (err) {\n if (!(err instanceof ReachFlowError)) throw err;\n lastError = err;\n if (!err.retryable || attempt >= this.config.maxRetries) throw err;\n const delayMs = err.retryAfterMs ?? backoffMs(attempt);\n await sleep(delayMs);\n attempt += 1;\n }\n }\n\n throw lastError ?? new ReachFlowError({\n message: 'Request failed',\n statusCode: 0,\n code: 'api_error',\n });\n }\n\n private async requestOnce<T>(options: RequestOptions): Promise<T> {\n const url = `${this.config.apiPrefix}${options.path}`;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'X-API-Key': this.config.apiKey,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const response = await this.config.fetchImpl(url, {\n method: options.method,\n headers,\n body:\n options.body !== undefined\n ? JSON.stringify(options.body)\n : undefined,\n signal: controller.signal,\n });\n\n const text = await response.text();\n let data: unknown = null;\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n data = { raw: text.slice(0, 500) };\n }\n }\n\n if (!response.ok) {\n const err = ReachFlowError.fromResponse(\n response.status,\n data,\n );\n const retryAfterMs = parseRetryAfterMs(\n response.headers.get('Retry-After'),\n );\n if (retryAfterMs !== undefined) {\n throw new ReachFlowError({\n message: err.message,\n statusCode: err.statusCode,\n code: err.code,\n retryable: err.retryable,\n retryAfterMs,\n body: err.body,\n });\n }\n throw err;\n }\n\n return data as T;\n } catch (err) {\n if (err instanceof ReachFlowError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw ReachFlowError.timeout(this.config.timeoutMs);\n }\n throw ReachFlowError.network(err);\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 10_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport { ReachFlowError } from '../errors.js';\nimport type {\n MessageStatusResponse,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from '../types.js';\nimport { TERMINAL_MESSAGE_STATUSES } from '../types.js';\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Envoie un message texte (réponse HTTP 202). */\n async send(params: SendMessageParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send',\n body,\n idempotencyKey,\n });\n }\n\n /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */\n async sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send-media',\n body,\n idempotencyKey,\n });\n }\n\n /** Envoie un lot de messages (même modèle, destinataires multiples). */\n async sendBulk(params: SendBulkParams): Promise<SendBulkResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendBulkResponse>({\n method: 'POST',\n path: '/messages/send-bulk',\n body,\n idempotencyKey,\n });\n }\n\n /** Consulte le statut d'un message précédemment accepté. */\n async getStatus(messageId: string): Promise<MessageStatusResponse> {\n return this.http.request<MessageStatusResponse>({\n method: 'GET',\n path: `/messages/${messageId}`,\n });\n }\n\n /**\n * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).\n * Lance `ReachFlowError` si timeout.\n */\n async waitForTerminal(\n messageId: string,\n options?: WaitForTerminalOptions,\n ): Promise<MessageStatusResponse> {\n const pollIntervalMs = options?.pollIntervalMs ?? 2_000;\n const timeoutMs = options?.timeoutMs ?? 120_000;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const status = await this.getStatus(messageId);\n if (TERMINAL_MESSAGE_STATUSES.has(status.status)) {\n return status;\n }\n await sleep(pollIntervalMs);\n }\n\n throw new ReachFlowError({\n message: `Message ${messageId} did not reach a terminal status within ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: false,\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n} from '../types.js';\n\nexport class OtpResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Génère et envoie un code OTP par WhatsApp.\n * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.\n */\n async send(params: OtpSendParams): Promise<OtpSendResponse> {\n return this.http.request<OtpSendResponse>({\n method: 'POST',\n path: '/otp/send',\n body: params,\n });\n }\n\n /** Vérifie un code saisi par l'utilisateur final. */\n async verify(params: OtpVerifyParams): Promise<OtpVerifyResponse> {\n return this.http.request<OtpVerifyResponse>({\n method: 'POST',\n path: '/otp/verify',\n body: params,\n });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { ProviderDetail, ProviderListResponse } from '../types.js';\n\nexport class ProvidersResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Liste les numéros WhatsApp accessibles via l'API. */\n async list(): Promise<ProviderListResponse> {\n return this.http.request<ProviderListResponse>({\n method: 'GET',\n path: '/providers',\n });\n }\n\n /** Détail d'un provider avec statistiques journalières. */\n async get(providerId: string): Promise<ProviderDetail> {\n return this.http.request<ProviderDetail>({\n method: 'GET',\n path: `/providers/${providerId}`,\n });\n }\n\n /**\n * Retourne le premier provider au statut `connected`, ou le premier de la liste.\n * Utile pour les scripts de test / démarrage rapide.\n */\n async findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null> {\n const { providers } = await this.list();\n if (providers.length === 0) return null;\n return (\n providers.find((p) => p.status.toLowerCase() === 'connected') ??\n providers[0] ??\n null\n );\n }\n}\n","import { HttpClient, resolveConfig } from './http.js';\nimport { MessagesResource } from './resources/messages.js';\nimport { OtpResource } from './resources/otp.js';\nimport { ProvidersResource } from './resources/providers.js';\nimport type { ReachFlowClientOptions } from './types.js';\n\n/**\n * Client officiel pour l'API publique ReachFlow (REST v1).\n *\n * @example\n * ```ts\n * const client = new ReachFlow({ apiKey: process.env.REACHFLOW_API_KEY! });\n * const { providers } = await client.providers.list();\n * ```\n */\nexport class ReachFlow {\n readonly messages: MessagesResource;\n readonly providers: ProvidersResource;\n readonly otp: OtpResource;\n\n private readonly http: HttpClient;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n constructor(options: ReachFlowClientOptions) {\n if (!options.apiKey?.trim()) {\n throw new Error('ReachFlow: apiKey is required');\n }\n\n this.config = resolveConfig(options);\n this.http = new HttpClient(this.config);\n this.messages = new MessagesResource(this.http);\n this.providers = new ProvidersResource(this.http);\n this.otp = new OtpResource(this.http);\n }\n\n /** URL de base configurée (sans `/api/v1`). */\n get baseUrl(): string {\n return this.config.baseUrl;\n }\n}\n"],"mappings":"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","names":["sleep"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/types.ts","../src/http.ts","../src/resources/messages.ts","../src/resources/otp.ts","../src/resources/providers.ts","../src/client.ts"],"sourcesContent":["export { ReachFlow } from './client.js';\nexport { ReachFlowError } from './errors.js';\nexport type { ReachFlowErrorCode } from './errors.js';\nexport type {\n BulkRecipient,\n FailureCode,\n MediaType,\n MessageStatus,\n MessageStatusResponse,\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n OtpVerifyReason,\n ProviderDailyStats,\n ProviderDetail,\n ProviderListResponse,\n ProviderSummary,\n ReachFlowClientOptions,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from './types.js';\nexport {\n DEFAULT_BASE_URL,\n TERMINAL_MESSAGE_STATUSES,\n} from './types.js';\n","export type ReachFlowErrorCode =\n | 'unauthorized'\n | 'plan_required'\n | 'insufficient_scope'\n | 'rate_limit_exceeded'\n | 'too_many_auth_failures'\n | 'validation_error'\n | 'not_found'\n | 'api_error'\n | 'network_error'\n | 'timeout';\n\nexport interface ReachFlowErrorBody {\n statusCode?: number;\n error?: string;\n message?: string;\n}\n\nexport class ReachFlowError extends Error {\n readonly statusCode: number;\n readonly code: ReachFlowErrorCode;\n readonly retryable: boolean;\n readonly retryAfterMs?: number;\n readonly body?: unknown;\n\n constructor(params: {\n message: string;\n statusCode: number;\n code: ReachFlowErrorCode;\n retryable?: boolean;\n retryAfterMs?: number;\n body?: unknown;\n cause?: unknown;\n }) {\n super(params.message, params.cause ? { cause: params.cause } : undefined);\n this.name = 'ReachFlowError';\n this.statusCode = params.statusCode;\n this.code = params.code;\n this.retryable = params.retryable ?? false;\n this.retryAfterMs = params.retryAfterMs;\n this.body = params.body;\n }\n\n static fromResponse(\n status: number,\n body: unknown,\n fallbackMessage?: string,\n ): ReachFlowError {\n const parsed = (body ?? {}) as ReachFlowErrorBody;\n const apiError = parsed.error;\n const message =\n parsed.message ??\n fallbackMessage ??\n `ReachFlow API error (HTTP ${status})`;\n\n const code = mapApiErrorToCode(status, apiError);\n const retryable =\n status === 429 || status === 408 || (status >= 500 && status < 600);\n\n return new ReachFlowError({\n message,\n statusCode: status,\n code,\n retryable,\n body,\n });\n }\n\n static network(cause: unknown): ReachFlowError {\n return new ReachFlowError({\n message: 'Network error while calling ReachFlow API',\n statusCode: 0,\n code: 'network_error',\n retryable: true,\n cause,\n });\n }\n\n static timeout(timeoutMs: number): ReachFlowError {\n return new ReachFlowError({\n message: `Request timed out after ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: true,\n });\n }\n}\n\nfunction mapApiErrorToCode(\n status: number,\n apiError?: string,\n): ReachFlowErrorCode {\n switch (apiError) {\n case 'unauthorized':\n return 'unauthorized';\n case 'plan_required':\n return 'plan_required';\n case 'insufficient_scope':\n return 'insufficient_scope';\n case 'rate_limit_exceeded':\n return 'rate_limit_exceeded';\n case 'too_many_auth_failures':\n return 'too_many_auth_failures';\n default:\n break;\n }\n\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'insufficient_scope';\n if (status === 404) return 'not_found';\n if (status === 400 || status === 422) return 'validation_error';\n if (status === 429) return 'rate_limit_exceeded';\n return 'api_error';\n}\n\nexport function parseRetryAfterMs(header: string | null): number | undefined {\n if (!header) return undefined;\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n","/** Lifecycle status of an API message. */\nexport type MessageStatus =\n | 'queued'\n | 'processing'\n | 'sent'\n | 'delivered'\n | 'failed'\n | 'cancelled';\n\nexport type MediaType = 'image' | 'document' | 'audio' | 'video';\n\nexport type FailureCode =\n | 'warmup_daily_limit'\n | 'risk_circuit_open'\n | 'provider_disconnected'\n | 'provider_banned'\n | 'send_not_allowed'\n | 'instance_busy'\n | 'delivery_timeout'\n | 'delivery_failed'\n | 'send_error'\n | 'provider_not_found';\n\nexport type OtpVerifyReason =\n | 'invalidated'\n | 'already_used'\n | 'expired'\n | 'max_attempts_reached'\n | 'invalid_code'\n | 'not_found';\n\nexport interface ReachFlowClientOptions {\n /** API key (`rfl_live_…` or `rfl_test_…`). */\n apiKey: string;\n /**\n * Base URL without `/api/v1` (default: ReachFlow sandbox).\n * @default \"https://sandbox-api.reachflow.me\"\n */\n baseUrl?: string;\n /** HTTP timeout in milliseconds. @default 30000 */\n timeoutMs?: number;\n /** Custom fetch implementation (tests, Node < 18). @default global fetch */\n fetch?: typeof fetch;\n /** Max retries on 429 / retryable 5xx. @default 2 */\n maxRetries?: number;\n}\n\nexport interface SendMessageParams {\n providerId: string;\n to: string;\n message: string;\n variables?: Record<string, string>;\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendMediaParams {\n providerId: string;\n to: string;\n mediaUrl: string;\n mediaType: MediaType;\n caption?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface BulkRecipient {\n to: string;\n variables?: Record<string, string>;\n}\n\nexport interface SendBulkParams {\n providerId: string;\n messageTemplate: string;\n recipients: BulkRecipient[];\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendAcceptedResponse {\n messageId: string;\n status: 'queued';\n queuedAt: string;\n}\n\nexport interface BulkRejection {\n to: string;\n reason: string;\n}\n\nexport interface SendBulkResponse {\n bulkId: string;\n accepted: number;\n rejected: number;\n rejections: BulkRejection[];\n messageIds: string[];\n}\n\nexport interface MessageStatusResponse {\n messageId: string;\n status: MessageStatus;\n to: string;\n providerId: string;\n queuedAt: string;\n sentAt: string | null;\n deliveredAt: string | null;\n failedAt: string | null;\n failureCode: FailureCode | null;\n failureReason: string | null;\n}\n\nexport interface ProviderSummary {\n id: string;\n name: string;\n phoneNumber: string | null;\n status: string;\n warmupDay: number;\n riskScore: number;\n}\n\nexport interface ProviderDailyStats {\n sent: number;\n delivered: number;\n failed: number;\n messagesLimit: number | null;\n newContactsLimit: number | null;\n}\n\nexport interface ProviderDetail extends ProviderSummary {\n dailyStats: ProviderDailyStats;\n}\n\nexport interface ProviderListResponse {\n providers: ProviderSummary[];\n}\n\nexport interface OtpSendParams {\n providerId: string;\n phoneNumber: string;\n codeLength?: number;\n expiresIn?: number;\n brandName?: string;\n template?: string;\n saveContact?: boolean;\n}\n\nexport interface OtpSendResponse {\n otpId: string;\n messageId: string;\n expiresAt: string;\n}\n\nexport interface OtpVerifyParams {\n otpId: string;\n code: string;\n}\n\nexport interface OtpVerifyResponse {\n valid: boolean;\n reason?: OtpVerifyReason;\n attemptsLeft?: number;\n}\n\nexport interface WaitForTerminalOptions {\n /** Poll interval in ms. @default 2000 */\n pollIntervalMs?: number;\n /** Total timeout in ms. @default 120000 */\n timeoutMs?: number;\n}\n\nexport const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus> = new Set([\n 'sent',\n 'delivered',\n 'failed',\n 'cancelled',\n]);\n\nexport const DEFAULT_BASE_URL = 'https://sandbox-api.reachflow.me';\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport { DEFAULT_BASE_URL } from './types.js';\n\nexport interface RequestOptions {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idempotencyKey?: string;\n}\n\nexport interface ResolvedClientConfig {\n apiKey: string;\n baseUrl: string;\n apiPrefix: string;\n timeoutMs: number;\n fetchImpl: typeof fetch;\n maxRetries: number;\n}\n\nexport function resolveConfig(\n options: ReachFlowClientOptions,\n): ResolvedClientConfig {\n const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n baseUrl,\n apiPrefix: `${baseUrl}/api/v1`,\n timeoutMs: options.timeoutMs ?? 30_000,\n fetchImpl: options.fetch ?? globalThis.fetch,\n maxRetries: options.maxRetries ?? 2,\n };\n}\n\nexport class HttpClient {\n constructor(private readonly config: ResolvedClientConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n let attempt = 0;\n let lastError: ReachFlowError | undefined;\n\n while (attempt <= this.config.maxRetries) {\n try {\n return await this.requestOnce<T>(options);\n } catch (err) {\n if (!(err instanceof ReachFlowError)) throw err;\n lastError = err;\n if (!err.retryable || attempt >= this.config.maxRetries) throw err;\n const delayMs = err.retryAfterMs ?? backoffMs(attempt);\n await sleep(delayMs);\n attempt += 1;\n }\n }\n\n throw lastError ?? new ReachFlowError({\n message: 'Request failed',\n statusCode: 0,\n code: 'api_error',\n });\n }\n\n private async requestOnce<T>(options: RequestOptions): Promise<T> {\n const url = `${this.config.apiPrefix}${options.path}`;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'X-API-Key': this.config.apiKey,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const response = await this.config.fetchImpl(url, {\n method: options.method,\n headers,\n body:\n options.body !== undefined\n ? JSON.stringify(options.body)\n : undefined,\n signal: controller.signal,\n });\n\n const text = await response.text();\n let data: unknown = null;\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n data = { raw: text.slice(0, 500) };\n }\n }\n\n if (!response.ok) {\n const err = ReachFlowError.fromResponse(\n response.status,\n data,\n );\n const retryAfterMs = parseRetryAfterMs(\n response.headers.get('Retry-After'),\n );\n if (retryAfterMs !== undefined) {\n throw new ReachFlowError({\n message: err.message,\n statusCode: err.statusCode,\n code: err.code,\n retryable: err.retryable,\n retryAfterMs,\n body: err.body,\n });\n }\n throw err;\n }\n\n return data as T;\n } catch (err) {\n if (err instanceof ReachFlowError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw ReachFlowError.timeout(this.config.timeoutMs);\n }\n throw ReachFlowError.network(err);\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 10_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport { ReachFlowError } from '../errors.js';\nimport type {\n MessageStatusResponse,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from '../types.js';\nimport { TERMINAL_MESSAGE_STATUSES } from '../types.js';\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Send a text message (HTTP 202). */\n async send(params: SendMessageParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send',\n body,\n idempotencyKey,\n });\n }\n\n /** Send media via a public HTTPS URL (HTTP 202). */\n async sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send-media',\n body,\n idempotencyKey,\n });\n }\n\n /** Send a bulk batch (same template, multiple recipients). */\n async sendBulk(params: SendBulkParams): Promise<SendBulkResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendBulkResponse>({\n method: 'POST',\n path: '/messages/send-bulk',\n body,\n idempotencyKey,\n });\n }\n\n /** Get the status of a previously accepted message. */\n async getStatus(messageId: string): Promise<MessageStatusResponse> {\n return this.http.request<MessageStatusResponse>({\n method: 'GET',\n path: `/messages/${messageId}`,\n });\n }\n\n /**\n * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).\n * Throws `ReachFlowError` on timeout.\n */\n async waitForTerminal(\n messageId: string,\n options?: WaitForTerminalOptions,\n ): Promise<MessageStatusResponse> {\n const pollIntervalMs = options?.pollIntervalMs ?? 2_000;\n const timeoutMs = options?.timeoutMs ?? 120_000;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const status = await this.getStatus(messageId);\n if (TERMINAL_MESSAGE_STATUSES.has(status.status)) {\n return status;\n }\n await sleep(pollIntervalMs);\n }\n\n throw new ReachFlowError({\n message: `Message ${messageId} did not reach a terminal status within ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: false,\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n} from '../types.js';\n\nexport class OtpResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Generate and send an OTP via WhatsApp.\n * The code is never returned by the API — only delivered on WhatsApp.\n */\n async send(params: OtpSendParams): Promise<OtpSendResponse> {\n return this.http.request<OtpSendResponse>({\n method: 'POST',\n path: '/otp/send',\n body: params,\n });\n }\n\n /** Verify a code entered by the end user. */\n async verify(params: OtpVerifyParams): Promise<OtpVerifyResponse> {\n return this.http.request<OtpVerifyResponse>({\n method: 'POST',\n path: '/otp/verify',\n body: params,\n });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { ProviderDetail, ProviderListResponse } from '../types.js';\n\nexport class ProvidersResource {\n constructor(private readonly http: HttpClient) {}\n\n /** List WhatsApp numbers available via the API. */\n async list(): Promise<ProviderListResponse> {\n return this.http.request<ProviderListResponse>({\n method: 'GET',\n path: '/providers',\n });\n }\n\n /** Get a provider with daily statistics. */\n async get(providerId: string): Promise<ProviderDetail> {\n return this.http.request<ProviderDetail>({\n method: 'GET',\n path: `/providers/${providerId}`,\n });\n }\n\n /**\n * Return the first `connected` provider, or the first in the list.\n * Handy for smoke tests and quick starts.\n */\n async findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null> {\n const { providers } = await this.list();\n if (providers.length === 0) return null;\n return (\n providers.find((p) => p.status.toLowerCase() === 'connected') ??\n providers[0] ??\n null\n );\n }\n}\n","import { HttpClient, resolveConfig } from './http.js';\nimport { MessagesResource } from './resources/messages.js';\nimport { OtpResource } from './resources/otp.js';\nimport { ProvidersResource } from './resources/providers.js';\nimport type { ReachFlowClientOptions } from './types.js';\n\n/**\n * Official client for the ReachFlow public API (REST v1).\n *\n * @example\n * ```ts\n * const client = new ReachFlow({ apiKey: process.env.REACHFLOW_API_KEY! });\n * const { providers } = await client.providers.list();\n * ```\n */\nexport class ReachFlow {\n readonly messages: MessagesResource;\n readonly providers: ProvidersResource;\n readonly otp: OtpResource;\n\n private readonly http: HttpClient;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n constructor(options: ReachFlowClientOptions) {\n if (!options.apiKey?.trim()) {\n throw new Error('ReachFlow: apiKey is required');\n }\n\n this.config = resolveConfig(options);\n this.http = new HttpClient(this.config);\n this.messages = new MessagesResource(this.http);\n this.providers = new ProvidersResource(this.http);\n this.otp = new OtpResource(this.http);\n }\n\n /** Configured base URL (without `/api/v1`). */\n get baseUrl(): string {\n return this.config.baseUrl;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBO,IAAM,iBAAN,MAAM,wBAAuB,MAAM;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,QAQT;AACD,UAAM,OAAO,SAAS,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,MAAS;AACxE,SAAK,OAAO;AACZ,SAAK,aAAa,OAAO;AACzB,SAAK,OAAO,OAAO;AACnB,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,eAAe,OAAO;AAC3B,SAAK,OAAO,OAAO;AAAA,EACrB;AAAA,EAEA,OAAO,aACL,QACA,MACA,iBACgB;AAChB,UAAM,SAAU,QAAQ,CAAC;AACzB,UAAM,WAAW,OAAO;AACxB,UAAM,UACJ,OAAO,WACP,mBACA,6BAA6B,MAAM;AAErC,UAAM,OAAO,kBAAkB,QAAQ,QAAQ;AAC/C,UAAM,YACJ,WAAW,OAAO,WAAW,OAAQ,UAAU,OAAO,SAAS;AAEjE,WAAO,IAAI,gBAAe;AAAA,MACxB;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,QAAQ,OAAgC;AAC7C,WAAO,IAAI,gBAAe;AAAA,MACxB,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,QAAQ,WAAmC;AAChD,WAAO,IAAI,gBAAe;AAAA,MACxB,SAAS,2BAA2B,SAAS;AAAA,MAC7C,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACF;AAEA,SAAS,kBACP,QACA,UACoB;AACpB,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE;AAAA,EACJ;AAEA,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,MAAI,WAAW,OAAO,WAAW,IAAK,QAAO;AAC7C,MAAI,WAAW,IAAK,QAAO;AAC3B,SAAO;AACT;AAEO,SAAS,kBAAkB,QAA2C;AAC3E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,CAAC,OAAO,MAAM,OAAO,EAAG,QAAO,KAAK,IAAI,GAAG,UAAU,GAAI;AAC7D,QAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,MAAI,CAAC,OAAO,MAAM,IAAI,EAAG,QAAO,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC;AAC7D,SAAO;AACT;;;ACkDO,IAAM,4BAAwD,oBAAI,IAAI;AAAA,EAC3E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,mBAAmB;;;AC/JzB,SAAS,cACd,SACsB;AACtB,QAAM,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AACvE,SAAO;AAAA,IACL,QAAQ,QAAQ,OAAO,KAAK;AAAA,IAC5B;AAAA,IACA,WAAW,GAAG,OAAO;AAAA,IACrB,WAAW,QAAQ,aAAa;AAAA,IAChC,WAAW,QAAQ,SAAS,WAAW;AAAA,IACvC,YAAY,QAAQ,cAAc;AAAA,EACpC;AACF;AAEO,IAAM,aAAN,MAAiB;AAAA,EACtB,YAA6B,QAA8B;AAA9B;AAAA,EAA+B;AAAA,EAA/B;AAAA,EAE7B,MAAM,QAAW,SAAqC;AACpD,QAAI,UAAU;AACd,QAAI;AAEJ,WAAO,WAAW,KAAK,OAAO,YAAY;AACxC,UAAI;AACF,eAAO,MAAM,KAAK,YAAe,OAAO;AAAA,MAC1C,SAAS,KAAK;AACZ,YAAI,EAAE,eAAe,gBAAiB,OAAM;AAC5C,oBAAY;AACZ,YAAI,CAAC,IAAI,aAAa,WAAW,KAAK,OAAO,WAAY,OAAM;AAC/D,cAAM,UAAU,IAAI,gBAAgB,UAAU,OAAO;AACrD,cAAM,MAAM,OAAO;AACnB,mBAAW;AAAA,MACb;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,eAAe;AAAA,MACpC,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,YAAe,SAAqC;AAChE,UAAM,MAAM,GAAG,KAAK,OAAO,SAAS,GAAG,QAAQ,IAAI;AACnD,UAAM,UAAkC;AAAA,MACtC,QAAQ;AAAA,MACR,aAAa,KAAK,OAAO;AAAA,IAC3B;AAEA,QAAI,QAAQ,SAAS,QAAW;AAC9B,cAAQ,cAAc,IAAI;AAAA,IAC5B;AACA,QAAI,QAAQ,gBAAgB;AAC1B,cAAQ,iBAAiB,IAAI,QAAQ;AAAA,IACvC;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,SAAS;AAExE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,UAAU,KAAK;AAAA,QAChD,QAAQ,QAAQ;AAAA,QAChB;AAAA,QACA,MACE,QAAQ,SAAS,SACb,KAAK,UAAU,QAAQ,IAAI,IAC3B;AAAA,QACN,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI,OAAgB;AACpB,UAAI,MAAM;AACR,YAAI;AACF,iBAAO,KAAK,MAAM,IAAI;AAAA,QACxB,QAAQ;AACN,iBAAO,EAAE,KAAK,KAAK,MAAM,GAAG,GAAG,EAAE;AAAA,QACnC;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,MAAM,eAAe;AAAA,UACzB,SAAS;AAAA,UACT;AAAA,QACF;AACA,cAAM,eAAe;AAAA,UACnB,SAAS,QAAQ,IAAI,aAAa;AAAA,QACpC;AACA,YAAI,iBAAiB,QAAW;AAC9B,gBAAM,IAAI,eAAe;AAAA,YACvB,SAAS,IAAI;AAAA,YACb,YAAY,IAAI;AAAA,YAChB,MAAM,IAAI;AAAA,YACV,WAAW,IAAI;AAAA,YACf;AAAA,YACA,MAAM,IAAI;AAAA,UACZ,CAAC;AAAA,QACH;AACA,cAAM;AAAA,MACR;AAEA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,eAAe,eAAgB,OAAM;AACzC,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,eAAe,QAAQ,KAAK,OAAO,SAAS;AAAA,MACpD;AACA,YAAM,eAAe,QAAQ,GAAG;AAAA,IAClC,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;AAEA,SAAS,UAAU,SAAyB;AAC1C,SAAO,KAAK,IAAI,MAAO,KAAK,SAAS,GAAM;AAC7C;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;AC9HO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,MAAM,KAAK,QAA0D;AACnE,UAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,WAAO,KAAK,KAAK,QAA8B;AAAA,MAC7C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UAAU,QAAwD;AACtE,UAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,WAAO,KAAK,KAAK,QAA8B;AAAA,MAC7C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAS,QAAmD;AAChE,UAAM,EAAE,gBAAgB,GAAG,KAAK,IAAI;AACpC,WAAO,KAAK,KAAK,QAA0B;AAAA,MACzC,QAAQ;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UAAU,WAAmD;AACjE,WAAO,KAAK,KAAK,QAA+B;AAAA,MAC9C,QAAQ;AAAA,MACR,MAAM,aAAa,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBACJ,WACA,SACgC;AAChC,UAAM,iBAAiB,SAAS,kBAAkB;AAClD,UAAM,YAAY,SAAS,aAAa;AACxC,UAAM,UAAU,KAAK,IAAI;AAEzB,WAAO,KAAK,IAAI,IAAI,UAAU,WAAW;AACvC,YAAM,SAAS,MAAM,KAAK,UAAU,SAAS;AAC7C,UAAI,0BAA0B,IAAI,OAAO,MAAM,GAAG;AAChD,eAAO;AAAA,MACT;AACA,YAAMA,OAAM,cAAc;AAAA,IAC5B;AAEA,UAAM,IAAI,eAAe;AAAA,MACvB,SAAS,WAAW,SAAS,2CAA2C,SAAS;AAAA,MACjF,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACF;AAEA,SAASA,OAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;AChFO,IAAM,cAAN,MAAkB;AAAA,EACvB,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAM,KAAK,QAAiD;AAC1D,WAAO,KAAK,KAAK,QAAyB;AAAA,MACxC,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,OAAO,QAAqD;AAChE,WAAO,KAAK,KAAK,QAA2B;AAAA,MAC1C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;;;AC5BO,IAAM,oBAAN,MAAwB;AAAA,EAC7B,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,MAAM,OAAsC;AAC1C,WAAO,KAAK,KAAK,QAA8B;AAAA,MAC7C,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,IAAI,YAA6C;AACrD,WAAO,KAAK,KAAK,QAAwB;AAAA,MACvC,QAAQ;AAAA,MACR,MAAM,cAAc,UAAU;AAAA,IAChC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAA4F;AAChG,UAAM,EAAE,UAAU,IAAI,MAAM,KAAK,KAAK;AACtC,QAAI,UAAU,WAAW,EAAG,QAAO;AACnC,WACE,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,YAAY,MAAM,WAAW,KAC5D,UAAU,CAAC,KACX;AAAA,EAEJ;AACF;;;ACpBO,IAAM,YAAN,MAAgB;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EAEjB,YAAY,SAAiC;AAC3C,QAAI,CAAC,QAAQ,QAAQ,KAAK,GAAG;AAC3B,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,SAAK,SAAS,cAAc,OAAO;AACnC,SAAK,OAAO,IAAI,WAAW,KAAK,MAAM;AACtC,SAAK,WAAW,IAAI,iBAAiB,KAAK,IAAI;AAC9C,SAAK,YAAY,IAAI,kBAAkB,KAAK,IAAI;AAChD,SAAK,MAAM,IAAI,YAAY,KAAK,IAAI;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,UAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AACF;","names":["sleep"]}
package/dist/index.d.cts CHANGED
@@ -1,21 +1,21 @@
1
- /** Statut de cycle de vie d'un message API. */
1
+ /** Lifecycle status of an API message. */
2
2
  type MessageStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'failed' | 'cancelled';
3
3
  type MediaType = 'image' | 'document' | 'audio' | 'video';
4
4
  type FailureCode = 'warmup_daily_limit' | 'risk_circuit_open' | 'provider_disconnected' | 'provider_banned' | 'send_not_allowed' | 'instance_busy' | 'delivery_timeout' | 'delivery_failed' | 'send_error' | 'provider_not_found';
5
5
  type OtpVerifyReason = 'invalidated' | 'already_used' | 'expired' | 'max_attempts_reached' | 'invalid_code' | 'not_found';
6
6
  interface ReachFlowClientOptions {
7
- /** Clé API `rfl_live_…` ou `rfl_test_…`. */
7
+ /** API key (`rfl_live_…` or `rfl_test_…`). */
8
8
  apiKey: string;
9
9
  /**
10
- * URL de base sans `/api/v1` (défaut : sandbox ReachFlow).
10
+ * Base URL without `/api/v1` (default: ReachFlow sandbox).
11
11
  * @default "https://sandbox-api.reachflow.me"
12
12
  */
13
13
  baseUrl?: string;
14
- /** Timeout HTTP en millisecondes. @default 30000 */
14
+ /** HTTP timeout in milliseconds. @default 30000 */
15
15
  timeoutMs?: number;
16
- /** Implémentation fetch (tests, Node < 18). @default global fetch */
16
+ /** Custom fetch implementation (tests, Node < 18). @default global fetch */
17
17
  fetch?: typeof fetch;
18
- /** Nombre max de retries sur 429 / 5xx retryables. @default 2 */
18
+ /** Max retries on 429 / retryable 5xx. @default 2 */
19
19
  maxRetries?: number;
20
20
  }
21
21
  interface SendMessageParams {
@@ -121,9 +121,9 @@ interface OtpVerifyResponse {
121
121
  attemptsLeft?: number;
122
122
  }
123
123
  interface WaitForTerminalOptions {
124
- /** Intervalle entre polls en ms. @default 2000 */
124
+ /** Poll interval in ms. @default 2000 */
125
125
  pollIntervalMs?: number;
126
- /** Timeout total en ms. @default 120000 */
126
+ /** Total timeout in ms. @default 120000 */
127
127
  timeoutMs?: number;
128
128
  }
129
129
  declare const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus>;
@@ -153,17 +153,17 @@ declare class HttpClient {
153
153
  declare class MessagesResource {
154
154
  private readonly http;
155
155
  constructor(http: HttpClient);
156
- /** Envoie un message texte (réponse HTTP 202). */
156
+ /** Send a text message (HTTP 202). */
157
157
  send(params: SendMessageParams): Promise<SendAcceptedResponse>;
158
- /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */
158
+ /** Send media via a public HTTPS URL (HTTP 202). */
159
159
  sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse>;
160
- /** Envoie un lot de messages (même modèle, destinataires multiples). */
160
+ /** Send a bulk batch (same template, multiple recipients). */
161
161
  sendBulk(params: SendBulkParams): Promise<SendBulkResponse>;
162
- /** Consulte le statut d'un message précédemment accepté. */
162
+ /** Get the status of a previously accepted message. */
163
163
  getStatus(messageId: string): Promise<MessageStatusResponse>;
164
164
  /**
165
- * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).
166
- * Lance `ReachFlowError` si timeout.
165
+ * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
166
+ * Throws `ReachFlowError` on timeout.
167
167
  */
168
168
  waitForTerminal(messageId: string, options?: WaitForTerminalOptions): Promise<MessageStatusResponse>;
169
169
  }
@@ -172,30 +172,30 @@ declare class OtpResource {
172
172
  private readonly http;
173
173
  constructor(http: HttpClient);
174
174
  /**
175
- * Génère et envoie un code OTP par WhatsApp.
176
- * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.
175
+ * Generate and send an OTP via WhatsApp.
176
+ * The code is never returned by the API — only delivered on WhatsApp.
177
177
  */
178
178
  send(params: OtpSendParams): Promise<OtpSendResponse>;
179
- /** Vérifie un code saisi par l'utilisateur final. */
179
+ /** Verify a code entered by the end user. */
180
180
  verify(params: OtpVerifyParams): Promise<OtpVerifyResponse>;
181
181
  }
182
182
 
183
183
  declare class ProvidersResource {
184
184
  private readonly http;
185
185
  constructor(http: HttpClient);
186
- /** Liste les numéros WhatsApp accessibles via l'API. */
186
+ /** List WhatsApp numbers available via the API. */
187
187
  list(): Promise<ProviderListResponse>;
188
- /** Détail d'un provider avec statistiques journalières. */
188
+ /** Get a provider with daily statistics. */
189
189
  get(providerId: string): Promise<ProviderDetail>;
190
190
  /**
191
- * Retourne le premier provider au statut `connected`, ou le premier de la liste.
192
- * Utile pour les scripts de test / démarrage rapide.
191
+ * Return the first `connected` provider, or the first in the list.
192
+ * Handy for smoke tests and quick starts.
193
193
  */
194
194
  findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null>;
195
195
  }
196
196
 
197
197
  /**
198
- * Client officiel pour l'API publique ReachFlow (REST v1).
198
+ * Official client for the ReachFlow public API (REST v1).
199
199
  *
200
200
  * @example
201
201
  * ```ts
@@ -210,7 +210,7 @@ declare class ReachFlow {
210
210
  private readonly http;
211
211
  private readonly config;
212
212
  constructor(options: ReachFlowClientOptions);
213
- /** URL de base configurée (sans `/api/v1`). */
213
+ /** Configured base URL (without `/api/v1`). */
214
214
  get baseUrl(): string;
215
215
  }
216
216
 
package/dist/index.d.ts CHANGED
@@ -1,21 +1,21 @@
1
- /** Statut de cycle de vie d'un message API. */
1
+ /** Lifecycle status of an API message. */
2
2
  type MessageStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'failed' | 'cancelled';
3
3
  type MediaType = 'image' | 'document' | 'audio' | 'video';
4
4
  type FailureCode = 'warmup_daily_limit' | 'risk_circuit_open' | 'provider_disconnected' | 'provider_banned' | 'send_not_allowed' | 'instance_busy' | 'delivery_timeout' | 'delivery_failed' | 'send_error' | 'provider_not_found';
5
5
  type OtpVerifyReason = 'invalidated' | 'already_used' | 'expired' | 'max_attempts_reached' | 'invalid_code' | 'not_found';
6
6
  interface ReachFlowClientOptions {
7
- /** Clé API `rfl_live_…` ou `rfl_test_…`. */
7
+ /** API key (`rfl_live_…` or `rfl_test_…`). */
8
8
  apiKey: string;
9
9
  /**
10
- * URL de base sans `/api/v1` (défaut : sandbox ReachFlow).
10
+ * Base URL without `/api/v1` (default: ReachFlow sandbox).
11
11
  * @default "https://sandbox-api.reachflow.me"
12
12
  */
13
13
  baseUrl?: string;
14
- /** Timeout HTTP en millisecondes. @default 30000 */
14
+ /** HTTP timeout in milliseconds. @default 30000 */
15
15
  timeoutMs?: number;
16
- /** Implémentation fetch (tests, Node < 18). @default global fetch */
16
+ /** Custom fetch implementation (tests, Node < 18). @default global fetch */
17
17
  fetch?: typeof fetch;
18
- /** Nombre max de retries sur 429 / 5xx retryables. @default 2 */
18
+ /** Max retries on 429 / retryable 5xx. @default 2 */
19
19
  maxRetries?: number;
20
20
  }
21
21
  interface SendMessageParams {
@@ -121,9 +121,9 @@ interface OtpVerifyResponse {
121
121
  attemptsLeft?: number;
122
122
  }
123
123
  interface WaitForTerminalOptions {
124
- /** Intervalle entre polls en ms. @default 2000 */
124
+ /** Poll interval in ms. @default 2000 */
125
125
  pollIntervalMs?: number;
126
- /** Timeout total en ms. @default 120000 */
126
+ /** Total timeout in ms. @default 120000 */
127
127
  timeoutMs?: number;
128
128
  }
129
129
  declare const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus>;
@@ -153,17 +153,17 @@ declare class HttpClient {
153
153
  declare class MessagesResource {
154
154
  private readonly http;
155
155
  constructor(http: HttpClient);
156
- /** Envoie un message texte (réponse HTTP 202). */
156
+ /** Send a text message (HTTP 202). */
157
157
  send(params: SendMessageParams): Promise<SendAcceptedResponse>;
158
- /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */
158
+ /** Send media via a public HTTPS URL (HTTP 202). */
159
159
  sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse>;
160
- /** Envoie un lot de messages (même modèle, destinataires multiples). */
160
+ /** Send a bulk batch (same template, multiple recipients). */
161
161
  sendBulk(params: SendBulkParams): Promise<SendBulkResponse>;
162
- /** Consulte le statut d'un message précédemment accepté. */
162
+ /** Get the status of a previously accepted message. */
163
163
  getStatus(messageId: string): Promise<MessageStatusResponse>;
164
164
  /**
165
- * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).
166
- * Lance `ReachFlowError` si timeout.
165
+ * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
166
+ * Throws `ReachFlowError` on timeout.
167
167
  */
168
168
  waitForTerminal(messageId: string, options?: WaitForTerminalOptions): Promise<MessageStatusResponse>;
169
169
  }
@@ -172,30 +172,30 @@ declare class OtpResource {
172
172
  private readonly http;
173
173
  constructor(http: HttpClient);
174
174
  /**
175
- * Génère et envoie un code OTP par WhatsApp.
176
- * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.
175
+ * Generate and send an OTP via WhatsApp.
176
+ * The code is never returned by the API — only delivered on WhatsApp.
177
177
  */
178
178
  send(params: OtpSendParams): Promise<OtpSendResponse>;
179
- /** Vérifie un code saisi par l'utilisateur final. */
179
+ /** Verify a code entered by the end user. */
180
180
  verify(params: OtpVerifyParams): Promise<OtpVerifyResponse>;
181
181
  }
182
182
 
183
183
  declare class ProvidersResource {
184
184
  private readonly http;
185
185
  constructor(http: HttpClient);
186
- /** Liste les numéros WhatsApp accessibles via l'API. */
186
+ /** List WhatsApp numbers available via the API. */
187
187
  list(): Promise<ProviderListResponse>;
188
- /** Détail d'un provider avec statistiques journalières. */
188
+ /** Get a provider with daily statistics. */
189
189
  get(providerId: string): Promise<ProviderDetail>;
190
190
  /**
191
- * Retourne le premier provider au statut `connected`, ou le premier de la liste.
192
- * Utile pour les scripts de test / démarrage rapide.
191
+ * Return the first `connected` provider, or the first in the list.
192
+ * Handy for smoke tests and quick starts.
193
193
  */
194
194
  findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null>;
195
195
  }
196
196
 
197
197
  /**
198
- * Client officiel pour l'API publique ReachFlow (REST v1).
198
+ * Official client for the ReachFlow public API (REST v1).
199
199
  *
200
200
  * @example
201
201
  * ```ts
@@ -210,7 +210,7 @@ declare class ReachFlow {
210
210
  private readonly http;
211
211
  private readonly config;
212
212
  constructor(options: ReachFlowClientOptions);
213
- /** URL de base configurée (sans `/api/v1`). */
213
+ /** Configured base URL (without `/api/v1`). */
214
214
  get baseUrl(): string;
215
215
  }
216
216
 
package/dist/index.js CHANGED
@@ -199,7 +199,7 @@ var MessagesResource = class {
199
199
  this.http = http;
200
200
  }
201
201
  http;
202
- /** Envoie un message texte (réponse HTTP 202). */
202
+ /** Send a text message (HTTP 202). */
203
203
  async send(params) {
204
204
  const { idempotencyKey, ...body } = params;
205
205
  return this.http.request({
@@ -209,7 +209,7 @@ var MessagesResource = class {
209
209
  idempotencyKey
210
210
  });
211
211
  }
212
- /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */
212
+ /** Send media via a public HTTPS URL (HTTP 202). */
213
213
  async sendMedia(params) {
214
214
  const { idempotencyKey, ...body } = params;
215
215
  return this.http.request({
@@ -219,7 +219,7 @@ var MessagesResource = class {
219
219
  idempotencyKey
220
220
  });
221
221
  }
222
- /** Envoie un lot de messages (même modèle, destinataires multiples). */
222
+ /** Send a bulk batch (same template, multiple recipients). */
223
223
  async sendBulk(params) {
224
224
  const { idempotencyKey, ...body } = params;
225
225
  return this.http.request({
@@ -229,7 +229,7 @@ var MessagesResource = class {
229
229
  idempotencyKey
230
230
  });
231
231
  }
232
- /** Consulte le statut d'un message précédemment accepté. */
232
+ /** Get the status of a previously accepted message. */
233
233
  async getStatus(messageId) {
234
234
  return this.http.request({
235
235
  method: "GET",
@@ -237,8 +237,8 @@ var MessagesResource = class {
237
237
  });
238
238
  }
239
239
  /**
240
- * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).
241
- * Lance `ReachFlowError` si timeout.
240
+ * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
241
+ * Throws `ReachFlowError` on timeout.
242
242
  */
243
243
  async waitForTerminal(messageId, options) {
244
244
  const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
@@ -270,8 +270,8 @@ var OtpResource = class {
270
270
  }
271
271
  http;
272
272
  /**
273
- * Génère et envoie un code OTP par WhatsApp.
274
- * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.
273
+ * Generate and send an OTP via WhatsApp.
274
+ * The code is never returned by the API — only delivered on WhatsApp.
275
275
  */
276
276
  async send(params) {
277
277
  return this.http.request({
@@ -280,7 +280,7 @@ var OtpResource = class {
280
280
  body: params
281
281
  });
282
282
  }
283
- /** Vérifie un code saisi par l'utilisateur final. */
283
+ /** Verify a code entered by the end user. */
284
284
  async verify(params) {
285
285
  return this.http.request({
286
286
  method: "POST",
@@ -296,14 +296,14 @@ var ProvidersResource = class {
296
296
  this.http = http;
297
297
  }
298
298
  http;
299
- /** Liste les numéros WhatsApp accessibles via l'API. */
299
+ /** List WhatsApp numbers available via the API. */
300
300
  async list() {
301
301
  return this.http.request({
302
302
  method: "GET",
303
303
  path: "/providers"
304
304
  });
305
305
  }
306
- /** Détail d'un provider avec statistiques journalières. */
306
+ /** Get a provider with daily statistics. */
307
307
  async get(providerId) {
308
308
  return this.http.request({
309
309
  method: "GET",
@@ -311,8 +311,8 @@ var ProvidersResource = class {
311
311
  });
312
312
  }
313
313
  /**
314
- * Retourne le premier provider au statut `connected`, ou le premier de la liste.
315
- * Utile pour les scripts de test / démarrage rapide.
314
+ * Return the first `connected` provider, or the first in the list.
315
+ * Handy for smoke tests and quick starts.
316
316
  */
317
317
  async findConnected() {
318
318
  const { providers } = await this.list();
@@ -338,7 +338,7 @@ var ReachFlow = class {
338
338
  this.providers = new ProvidersResource(this.http);
339
339
  this.otp = new OtpResource(this.http);
340
340
  }
341
- /** URL de base configurée (sans `/api/v1`). */
341
+ /** Configured base URL (without `/api/v1`). */
342
342
  get baseUrl() {
343
343
  return this.config.baseUrl;
344
344
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/http.ts","../src/resources/messages.ts","../src/resources/otp.ts","../src/resources/providers.ts","../src/client.ts"],"sourcesContent":["export type ReachFlowErrorCode =\n | 'unauthorized'\n | 'plan_required'\n | 'insufficient_scope'\n | 'rate_limit_exceeded'\n | 'too_many_auth_failures'\n | 'validation_error'\n | 'not_found'\n | 'api_error'\n | 'network_error'\n | 'timeout';\n\nexport interface ReachFlowErrorBody {\n statusCode?: number;\n error?: string;\n message?: string;\n}\n\nexport class ReachFlowError extends Error {\n readonly statusCode: number;\n readonly code: ReachFlowErrorCode;\n readonly retryable: boolean;\n readonly retryAfterMs?: number;\n readonly body?: unknown;\n\n constructor(params: {\n message: string;\n statusCode: number;\n code: ReachFlowErrorCode;\n retryable?: boolean;\n retryAfterMs?: number;\n body?: unknown;\n cause?: unknown;\n }) {\n super(params.message, params.cause ? { cause: params.cause } : undefined);\n this.name = 'ReachFlowError';\n this.statusCode = params.statusCode;\n this.code = params.code;\n this.retryable = params.retryable ?? false;\n this.retryAfterMs = params.retryAfterMs;\n this.body = params.body;\n }\n\n static fromResponse(\n status: number,\n body: unknown,\n fallbackMessage?: string,\n ): ReachFlowError {\n const parsed = (body ?? {}) as ReachFlowErrorBody;\n const apiError = parsed.error;\n const message =\n parsed.message ??\n fallbackMessage ??\n `ReachFlow API error (HTTP ${status})`;\n\n const code = mapApiErrorToCode(status, apiError);\n const retryable =\n status === 429 || status === 408 || (status >= 500 && status < 600);\n\n return new ReachFlowError({\n message,\n statusCode: status,\n code,\n retryable,\n body,\n });\n }\n\n static network(cause: unknown): ReachFlowError {\n return new ReachFlowError({\n message: 'Network error while calling ReachFlow API',\n statusCode: 0,\n code: 'network_error',\n retryable: true,\n cause,\n });\n }\n\n static timeout(timeoutMs: number): ReachFlowError {\n return new ReachFlowError({\n message: `Request timed out after ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: true,\n });\n }\n}\n\nfunction mapApiErrorToCode(\n status: number,\n apiError?: string,\n): ReachFlowErrorCode {\n switch (apiError) {\n case 'unauthorized':\n return 'unauthorized';\n case 'plan_required':\n return 'plan_required';\n case 'insufficient_scope':\n return 'insufficient_scope';\n case 'rate_limit_exceeded':\n return 'rate_limit_exceeded';\n case 'too_many_auth_failures':\n return 'too_many_auth_failures';\n default:\n break;\n }\n\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'insufficient_scope';\n if (status === 404) return 'not_found';\n if (status === 400 || status === 422) return 'validation_error';\n if (status === 429) return 'rate_limit_exceeded';\n return 'api_error';\n}\n\nexport function parseRetryAfterMs(header: string | null): number | undefined {\n if (!header) return undefined;\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n","/** Statut de cycle de vie d'un message API. */\nexport type MessageStatus =\n | 'queued'\n | 'processing'\n | 'sent'\n | 'delivered'\n | 'failed'\n | 'cancelled';\n\nexport type MediaType = 'image' | 'document' | 'audio' | 'video';\n\nexport type FailureCode =\n | 'warmup_daily_limit'\n | 'risk_circuit_open'\n | 'provider_disconnected'\n | 'provider_banned'\n | 'send_not_allowed'\n | 'instance_busy'\n | 'delivery_timeout'\n | 'delivery_failed'\n | 'send_error'\n | 'provider_not_found';\n\nexport type OtpVerifyReason =\n | 'invalidated'\n | 'already_used'\n | 'expired'\n | 'max_attempts_reached'\n | 'invalid_code'\n | 'not_found';\n\nexport interface ReachFlowClientOptions {\n /** Clé API `rfl_live_…` ou `rfl_test_…`. */\n apiKey: string;\n /**\n * URL de base sans `/api/v1` (défaut : sandbox ReachFlow).\n * @default \"https://sandbox-api.reachflow.me\"\n */\n baseUrl?: string;\n /** Timeout HTTP en millisecondes. @default 30000 */\n timeoutMs?: number;\n /** Implémentation fetch (tests, Node < 18). @default global fetch */\n fetch?: typeof fetch;\n /** Nombre max de retries sur 429 / 5xx retryables. @default 2 */\n maxRetries?: number;\n}\n\nexport interface SendMessageParams {\n providerId: string;\n to: string;\n message: string;\n variables?: Record<string, string>;\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendMediaParams {\n providerId: string;\n to: string;\n mediaUrl: string;\n mediaType: MediaType;\n caption?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface BulkRecipient {\n to: string;\n variables?: Record<string, string>;\n}\n\nexport interface SendBulkParams {\n providerId: string;\n messageTemplate: string;\n recipients: BulkRecipient[];\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendAcceptedResponse {\n messageId: string;\n status: 'queued';\n queuedAt: string;\n}\n\nexport interface BulkRejection {\n to: string;\n reason: string;\n}\n\nexport interface SendBulkResponse {\n bulkId: string;\n accepted: number;\n rejected: number;\n rejections: BulkRejection[];\n messageIds: string[];\n}\n\nexport interface MessageStatusResponse {\n messageId: string;\n status: MessageStatus;\n to: string;\n providerId: string;\n queuedAt: string;\n sentAt: string | null;\n deliveredAt: string | null;\n failedAt: string | null;\n failureCode: FailureCode | null;\n failureReason: string | null;\n}\n\nexport interface ProviderSummary {\n id: string;\n name: string;\n phoneNumber: string | null;\n status: string;\n warmupDay: number;\n riskScore: number;\n}\n\nexport interface ProviderDailyStats {\n sent: number;\n delivered: number;\n failed: number;\n messagesLimit: number | null;\n newContactsLimit: number | null;\n}\n\nexport interface ProviderDetail extends ProviderSummary {\n dailyStats: ProviderDailyStats;\n}\n\nexport interface ProviderListResponse {\n providers: ProviderSummary[];\n}\n\nexport interface OtpSendParams {\n providerId: string;\n phoneNumber: string;\n codeLength?: number;\n expiresIn?: number;\n brandName?: string;\n template?: string;\n saveContact?: boolean;\n}\n\nexport interface OtpSendResponse {\n otpId: string;\n messageId: string;\n expiresAt: string;\n}\n\nexport interface OtpVerifyParams {\n otpId: string;\n code: string;\n}\n\nexport interface OtpVerifyResponse {\n valid: boolean;\n reason?: OtpVerifyReason;\n attemptsLeft?: number;\n}\n\nexport interface WaitForTerminalOptions {\n /** Intervalle entre polls en ms. @default 2000 */\n pollIntervalMs?: number;\n /** Timeout total en ms. @default 120000 */\n timeoutMs?: number;\n}\n\nexport const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus> = new Set([\n 'sent',\n 'delivered',\n 'failed',\n 'cancelled',\n]);\n\nexport const DEFAULT_BASE_URL = 'https://sandbox-api.reachflow.me';\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport { DEFAULT_BASE_URL } from './types.js';\n\nexport interface RequestOptions {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idempotencyKey?: string;\n}\n\nexport interface ResolvedClientConfig {\n apiKey: string;\n baseUrl: string;\n apiPrefix: string;\n timeoutMs: number;\n fetchImpl: typeof fetch;\n maxRetries: number;\n}\n\nexport function resolveConfig(\n options: ReachFlowClientOptions,\n): ResolvedClientConfig {\n const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n baseUrl,\n apiPrefix: `${baseUrl}/api/v1`,\n timeoutMs: options.timeoutMs ?? 30_000,\n fetchImpl: options.fetch ?? globalThis.fetch,\n maxRetries: options.maxRetries ?? 2,\n };\n}\n\nexport class HttpClient {\n constructor(private readonly config: ResolvedClientConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n let attempt = 0;\n let lastError: ReachFlowError | undefined;\n\n while (attempt <= this.config.maxRetries) {\n try {\n return await this.requestOnce<T>(options);\n } catch (err) {\n if (!(err instanceof ReachFlowError)) throw err;\n lastError = err;\n if (!err.retryable || attempt >= this.config.maxRetries) throw err;\n const delayMs = err.retryAfterMs ?? backoffMs(attempt);\n await sleep(delayMs);\n attempt += 1;\n }\n }\n\n throw lastError ?? new ReachFlowError({\n message: 'Request failed',\n statusCode: 0,\n code: 'api_error',\n });\n }\n\n private async requestOnce<T>(options: RequestOptions): Promise<T> {\n const url = `${this.config.apiPrefix}${options.path}`;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'X-API-Key': this.config.apiKey,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const response = await this.config.fetchImpl(url, {\n method: options.method,\n headers,\n body:\n options.body !== undefined\n ? JSON.stringify(options.body)\n : undefined,\n signal: controller.signal,\n });\n\n const text = await response.text();\n let data: unknown = null;\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n data = { raw: text.slice(0, 500) };\n }\n }\n\n if (!response.ok) {\n const err = ReachFlowError.fromResponse(\n response.status,\n data,\n );\n const retryAfterMs = parseRetryAfterMs(\n response.headers.get('Retry-After'),\n );\n if (retryAfterMs !== undefined) {\n throw new ReachFlowError({\n message: err.message,\n statusCode: err.statusCode,\n code: err.code,\n retryable: err.retryable,\n retryAfterMs,\n body: err.body,\n });\n }\n throw err;\n }\n\n return data as T;\n } catch (err) {\n if (err instanceof ReachFlowError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw ReachFlowError.timeout(this.config.timeoutMs);\n }\n throw ReachFlowError.network(err);\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 10_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport { ReachFlowError } from '../errors.js';\nimport type {\n MessageStatusResponse,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from '../types.js';\nimport { TERMINAL_MESSAGE_STATUSES } from '../types.js';\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Envoie un message texte (réponse HTTP 202). */\n async send(params: SendMessageParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send',\n body,\n idempotencyKey,\n });\n }\n\n /** Envoie un média via URL HTTPS publique (réponse HTTP 202). */\n async sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send-media',\n body,\n idempotencyKey,\n });\n }\n\n /** Envoie un lot de messages (même modèle, destinataires multiples). */\n async sendBulk(params: SendBulkParams): Promise<SendBulkResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendBulkResponse>({\n method: 'POST',\n path: '/messages/send-bulk',\n body,\n idempotencyKey,\n });\n }\n\n /** Consulte le statut d'un message précédemment accepté. */\n async getStatus(messageId: string): Promise<MessageStatusResponse> {\n return this.http.request<MessageStatusResponse>({\n method: 'GET',\n path: `/messages/${messageId}`,\n });\n }\n\n /**\n * Poll jusqu'à un statut terminal (`sent`, `delivered`, `failed`, `cancelled`).\n * Lance `ReachFlowError` si timeout.\n */\n async waitForTerminal(\n messageId: string,\n options?: WaitForTerminalOptions,\n ): Promise<MessageStatusResponse> {\n const pollIntervalMs = options?.pollIntervalMs ?? 2_000;\n const timeoutMs = options?.timeoutMs ?? 120_000;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const status = await this.getStatus(messageId);\n if (TERMINAL_MESSAGE_STATUSES.has(status.status)) {\n return status;\n }\n await sleep(pollIntervalMs);\n }\n\n throw new ReachFlowError({\n message: `Message ${messageId} did not reach a terminal status within ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: false,\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n} from '../types.js';\n\nexport class OtpResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Génère et envoie un code OTP par WhatsApp.\n * Le code n'est jamais retourné par l'API — uniquement sur WhatsApp.\n */\n async send(params: OtpSendParams): Promise<OtpSendResponse> {\n return this.http.request<OtpSendResponse>({\n method: 'POST',\n path: '/otp/send',\n body: params,\n });\n }\n\n /** Vérifie un code saisi par l'utilisateur final. */\n async verify(params: OtpVerifyParams): Promise<OtpVerifyResponse> {\n return this.http.request<OtpVerifyResponse>({\n method: 'POST',\n path: '/otp/verify',\n body: params,\n });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { ProviderDetail, ProviderListResponse } from '../types.js';\n\nexport class ProvidersResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Liste les numéros WhatsApp accessibles via l'API. */\n async list(): Promise<ProviderListResponse> {\n return this.http.request<ProviderListResponse>({\n method: 'GET',\n path: '/providers',\n });\n }\n\n /** Détail d'un provider avec statistiques journalières. */\n async get(providerId: string): Promise<ProviderDetail> {\n return this.http.request<ProviderDetail>({\n method: 'GET',\n path: `/providers/${providerId}`,\n });\n }\n\n /**\n * Retourne le premier provider au statut `connected`, ou le premier de la liste.\n * Utile pour les scripts de test / démarrage rapide.\n */\n async findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null> {\n const { providers } = await this.list();\n if (providers.length === 0) return null;\n return (\n providers.find((p) => p.status.toLowerCase() === 'connected') ??\n providers[0] ??\n null\n );\n }\n}\n","import { HttpClient, resolveConfig } from './http.js';\nimport { MessagesResource } from './resources/messages.js';\nimport { OtpResource } from './resources/otp.js';\nimport { ProvidersResource } from './resources/providers.js';\nimport type { ReachFlowClientOptions } from './types.js';\n\n/**\n * Client officiel pour l'API publique ReachFlow (REST v1).\n *\n * @example\n * ```ts\n * const client = new ReachFlow({ apiKey: process.env.REACHFLOW_API_KEY! });\n * const { providers } = await client.providers.list();\n * ```\n */\nexport class ReachFlow {\n readonly messages: MessagesResource;\n readonly providers: ProvidersResource;\n readonly otp: OtpResource;\n\n private readonly http: HttpClient;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n constructor(options: ReachFlowClientOptions) {\n if (!options.apiKey?.trim()) {\n throw new Error('ReachFlow: apiKey is required');\n }\n\n this.config = resolveConfig(options);\n this.http = new HttpClient(this.config);\n this.messages = new MessagesResource(this.http);\n this.providers = new ProvidersResource(this.http);\n this.otp = new OtpResource(this.http);\n }\n\n /** URL de base configurée (sans `/api/v1`). */\n get baseUrl(): string {\n return this.config.baseUrl;\n }\n}\n"],"mappings":"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","names":["sleep"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/http.ts","../src/resources/messages.ts","../src/resources/otp.ts","../src/resources/providers.ts","../src/client.ts"],"sourcesContent":["export type ReachFlowErrorCode =\n | 'unauthorized'\n | 'plan_required'\n | 'insufficient_scope'\n | 'rate_limit_exceeded'\n | 'too_many_auth_failures'\n | 'validation_error'\n | 'not_found'\n | 'api_error'\n | 'network_error'\n | 'timeout';\n\nexport interface ReachFlowErrorBody {\n statusCode?: number;\n error?: string;\n message?: string;\n}\n\nexport class ReachFlowError extends Error {\n readonly statusCode: number;\n readonly code: ReachFlowErrorCode;\n readonly retryable: boolean;\n readonly retryAfterMs?: number;\n readonly body?: unknown;\n\n constructor(params: {\n message: string;\n statusCode: number;\n code: ReachFlowErrorCode;\n retryable?: boolean;\n retryAfterMs?: number;\n body?: unknown;\n cause?: unknown;\n }) {\n super(params.message, params.cause ? { cause: params.cause } : undefined);\n this.name = 'ReachFlowError';\n this.statusCode = params.statusCode;\n this.code = params.code;\n this.retryable = params.retryable ?? false;\n this.retryAfterMs = params.retryAfterMs;\n this.body = params.body;\n }\n\n static fromResponse(\n status: number,\n body: unknown,\n fallbackMessage?: string,\n ): ReachFlowError {\n const parsed = (body ?? {}) as ReachFlowErrorBody;\n const apiError = parsed.error;\n const message =\n parsed.message ??\n fallbackMessage ??\n `ReachFlow API error (HTTP ${status})`;\n\n const code = mapApiErrorToCode(status, apiError);\n const retryable =\n status === 429 || status === 408 || (status >= 500 && status < 600);\n\n return new ReachFlowError({\n message,\n statusCode: status,\n code,\n retryable,\n body,\n });\n }\n\n static network(cause: unknown): ReachFlowError {\n return new ReachFlowError({\n message: 'Network error while calling ReachFlow API',\n statusCode: 0,\n code: 'network_error',\n retryable: true,\n cause,\n });\n }\n\n static timeout(timeoutMs: number): ReachFlowError {\n return new ReachFlowError({\n message: `Request timed out after ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: true,\n });\n }\n}\n\nfunction mapApiErrorToCode(\n status: number,\n apiError?: string,\n): ReachFlowErrorCode {\n switch (apiError) {\n case 'unauthorized':\n return 'unauthorized';\n case 'plan_required':\n return 'plan_required';\n case 'insufficient_scope':\n return 'insufficient_scope';\n case 'rate_limit_exceeded':\n return 'rate_limit_exceeded';\n case 'too_many_auth_failures':\n return 'too_many_auth_failures';\n default:\n break;\n }\n\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'insufficient_scope';\n if (status === 404) return 'not_found';\n if (status === 400 || status === 422) return 'validation_error';\n if (status === 429) return 'rate_limit_exceeded';\n return 'api_error';\n}\n\nexport function parseRetryAfterMs(header: string | null): number | undefined {\n if (!header) return undefined;\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n","/** Lifecycle status of an API message. */\nexport type MessageStatus =\n | 'queued'\n | 'processing'\n | 'sent'\n | 'delivered'\n | 'failed'\n | 'cancelled';\n\nexport type MediaType = 'image' | 'document' | 'audio' | 'video';\n\nexport type FailureCode =\n | 'warmup_daily_limit'\n | 'risk_circuit_open'\n | 'provider_disconnected'\n | 'provider_banned'\n | 'send_not_allowed'\n | 'instance_busy'\n | 'delivery_timeout'\n | 'delivery_failed'\n | 'send_error'\n | 'provider_not_found';\n\nexport type OtpVerifyReason =\n | 'invalidated'\n | 'already_used'\n | 'expired'\n | 'max_attempts_reached'\n | 'invalid_code'\n | 'not_found';\n\nexport interface ReachFlowClientOptions {\n /** API key (`rfl_live_…` or `rfl_test_…`). */\n apiKey: string;\n /**\n * Base URL without `/api/v1` (default: ReachFlow sandbox).\n * @default \"https://sandbox-api.reachflow.me\"\n */\n baseUrl?: string;\n /** HTTP timeout in milliseconds. @default 30000 */\n timeoutMs?: number;\n /** Custom fetch implementation (tests, Node < 18). @default global fetch */\n fetch?: typeof fetch;\n /** Max retries on 429 / retryable 5xx. @default 2 */\n maxRetries?: number;\n}\n\nexport interface SendMessageParams {\n providerId: string;\n to: string;\n message: string;\n variables?: Record<string, string>;\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendMediaParams {\n providerId: string;\n to: string;\n mediaUrl: string;\n mediaType: MediaType;\n caption?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface BulkRecipient {\n to: string;\n variables?: Record<string, string>;\n}\n\nexport interface SendBulkParams {\n providerId: string;\n messageTemplate: string;\n recipients: BulkRecipient[];\n scheduleAt?: string;\n saveContact?: boolean;\n idempotencyKey?: string;\n}\n\nexport interface SendAcceptedResponse {\n messageId: string;\n status: 'queued';\n queuedAt: string;\n}\n\nexport interface BulkRejection {\n to: string;\n reason: string;\n}\n\nexport interface SendBulkResponse {\n bulkId: string;\n accepted: number;\n rejected: number;\n rejections: BulkRejection[];\n messageIds: string[];\n}\n\nexport interface MessageStatusResponse {\n messageId: string;\n status: MessageStatus;\n to: string;\n providerId: string;\n queuedAt: string;\n sentAt: string | null;\n deliveredAt: string | null;\n failedAt: string | null;\n failureCode: FailureCode | null;\n failureReason: string | null;\n}\n\nexport interface ProviderSummary {\n id: string;\n name: string;\n phoneNumber: string | null;\n status: string;\n warmupDay: number;\n riskScore: number;\n}\n\nexport interface ProviderDailyStats {\n sent: number;\n delivered: number;\n failed: number;\n messagesLimit: number | null;\n newContactsLimit: number | null;\n}\n\nexport interface ProviderDetail extends ProviderSummary {\n dailyStats: ProviderDailyStats;\n}\n\nexport interface ProviderListResponse {\n providers: ProviderSummary[];\n}\n\nexport interface OtpSendParams {\n providerId: string;\n phoneNumber: string;\n codeLength?: number;\n expiresIn?: number;\n brandName?: string;\n template?: string;\n saveContact?: boolean;\n}\n\nexport interface OtpSendResponse {\n otpId: string;\n messageId: string;\n expiresAt: string;\n}\n\nexport interface OtpVerifyParams {\n otpId: string;\n code: string;\n}\n\nexport interface OtpVerifyResponse {\n valid: boolean;\n reason?: OtpVerifyReason;\n attemptsLeft?: number;\n}\n\nexport interface WaitForTerminalOptions {\n /** Poll interval in ms. @default 2000 */\n pollIntervalMs?: number;\n /** Total timeout in ms. @default 120000 */\n timeoutMs?: number;\n}\n\nexport const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus> = new Set([\n 'sent',\n 'delivered',\n 'failed',\n 'cancelled',\n]);\n\nexport const DEFAULT_BASE_URL = 'https://sandbox-api.reachflow.me';\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport { DEFAULT_BASE_URL } from './types.js';\n\nexport interface RequestOptions {\n method: 'GET' | 'POST';\n path: string;\n body?: unknown;\n idempotencyKey?: string;\n}\n\nexport interface ResolvedClientConfig {\n apiKey: string;\n baseUrl: string;\n apiPrefix: string;\n timeoutMs: number;\n fetchImpl: typeof fetch;\n maxRetries: number;\n}\n\nexport function resolveConfig(\n options: ReachFlowClientOptions,\n): ResolvedClientConfig {\n const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n baseUrl,\n apiPrefix: `${baseUrl}/api/v1`,\n timeoutMs: options.timeoutMs ?? 30_000,\n fetchImpl: options.fetch ?? globalThis.fetch,\n maxRetries: options.maxRetries ?? 2,\n };\n}\n\nexport class HttpClient {\n constructor(private readonly config: ResolvedClientConfig) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n let attempt = 0;\n let lastError: ReachFlowError | undefined;\n\n while (attempt <= this.config.maxRetries) {\n try {\n return await this.requestOnce<T>(options);\n } catch (err) {\n if (!(err instanceof ReachFlowError)) throw err;\n lastError = err;\n if (!err.retryable || attempt >= this.config.maxRetries) throw err;\n const delayMs = err.retryAfterMs ?? backoffMs(attempt);\n await sleep(delayMs);\n attempt += 1;\n }\n }\n\n throw lastError ?? new ReachFlowError({\n message: 'Request failed',\n statusCode: 0,\n code: 'api_error',\n });\n }\n\n private async requestOnce<T>(options: RequestOptions): Promise<T> {\n const url = `${this.config.apiPrefix}${options.path}`;\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'X-API-Key': this.config.apiKey,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n if (options.idempotencyKey) {\n headers['Idempotency-Key'] = options.idempotencyKey;\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const response = await this.config.fetchImpl(url, {\n method: options.method,\n headers,\n body:\n options.body !== undefined\n ? JSON.stringify(options.body)\n : undefined,\n signal: controller.signal,\n });\n\n const text = await response.text();\n let data: unknown = null;\n if (text) {\n try {\n data = JSON.parse(text) as unknown;\n } catch {\n data = { raw: text.slice(0, 500) };\n }\n }\n\n if (!response.ok) {\n const err = ReachFlowError.fromResponse(\n response.status,\n data,\n );\n const retryAfterMs = parseRetryAfterMs(\n response.headers.get('Retry-After'),\n );\n if (retryAfterMs !== undefined) {\n throw new ReachFlowError({\n message: err.message,\n statusCode: err.statusCode,\n code: err.code,\n retryable: err.retryable,\n retryAfterMs,\n body: err.body,\n });\n }\n throw err;\n }\n\n return data as T;\n } catch (err) {\n if (err instanceof ReachFlowError) throw err;\n if (err instanceof Error && err.name === 'AbortError') {\n throw ReachFlowError.timeout(this.config.timeoutMs);\n }\n throw ReachFlowError.network(err);\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(1000 * 2 ** attempt, 10_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport { ReachFlowError } from '../errors.js';\nimport type {\n MessageStatusResponse,\n SendAcceptedResponse,\n SendBulkParams,\n SendBulkResponse,\n SendMediaParams,\n SendMessageParams,\n WaitForTerminalOptions,\n} from '../types.js';\nimport { TERMINAL_MESSAGE_STATUSES } from '../types.js';\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Send a text message (HTTP 202). */\n async send(params: SendMessageParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send',\n body,\n idempotencyKey,\n });\n }\n\n /** Send media via a public HTTPS URL (HTTP 202). */\n async sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendAcceptedResponse>({\n method: 'POST',\n path: '/messages/send-media',\n body,\n idempotencyKey,\n });\n }\n\n /** Send a bulk batch (same template, multiple recipients). */\n async sendBulk(params: SendBulkParams): Promise<SendBulkResponse> {\n const { idempotencyKey, ...body } = params;\n return this.http.request<SendBulkResponse>({\n method: 'POST',\n path: '/messages/send-bulk',\n body,\n idempotencyKey,\n });\n }\n\n /** Get the status of a previously accepted message. */\n async getStatus(messageId: string): Promise<MessageStatusResponse> {\n return this.http.request<MessageStatusResponse>({\n method: 'GET',\n path: `/messages/${messageId}`,\n });\n }\n\n /**\n * Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).\n * Throws `ReachFlowError` on timeout.\n */\n async waitForTerminal(\n messageId: string,\n options?: WaitForTerminalOptions,\n ): Promise<MessageStatusResponse> {\n const pollIntervalMs = options?.pollIntervalMs ?? 2_000;\n const timeoutMs = options?.timeoutMs ?? 120_000;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const status = await this.getStatus(messageId);\n if (TERMINAL_MESSAGE_STATUSES.has(status.status)) {\n return status;\n }\n await sleep(pollIntervalMs);\n }\n\n throw new ReachFlowError({\n message: `Message ${messageId} did not reach a terminal status within ${timeoutMs}ms`,\n statusCode: 408,\n code: 'timeout',\n retryable: false,\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { HttpClient } from '../http.js';\nimport type {\n OtpSendParams,\n OtpSendResponse,\n OtpVerifyParams,\n OtpVerifyResponse,\n} from '../types.js';\n\nexport class OtpResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Generate and send an OTP via WhatsApp.\n * The code is never returned by the API — only delivered on WhatsApp.\n */\n async send(params: OtpSendParams): Promise<OtpSendResponse> {\n return this.http.request<OtpSendResponse>({\n method: 'POST',\n path: '/otp/send',\n body: params,\n });\n }\n\n /** Verify a code entered by the end user. */\n async verify(params: OtpVerifyParams): Promise<OtpVerifyResponse> {\n return this.http.request<OtpVerifyResponse>({\n method: 'POST',\n path: '/otp/verify',\n body: params,\n });\n }\n}\n","import type { HttpClient } from '../http.js';\nimport type { ProviderDetail, ProviderListResponse } from '../types.js';\n\nexport class ProvidersResource {\n constructor(private readonly http: HttpClient) {}\n\n /** List WhatsApp numbers available via the API. */\n async list(): Promise<ProviderListResponse> {\n return this.http.request<ProviderListResponse>({\n method: 'GET',\n path: '/providers',\n });\n }\n\n /** Get a provider with daily statistics. */\n async get(providerId: string): Promise<ProviderDetail> {\n return this.http.request<ProviderDetail>({\n method: 'GET',\n path: `/providers/${providerId}`,\n });\n }\n\n /**\n * Return the first `connected` provider, or the first in the list.\n * Handy for smoke tests and quick starts.\n */\n async findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null> {\n const { providers } = await this.list();\n if (providers.length === 0) return null;\n return (\n providers.find((p) => p.status.toLowerCase() === 'connected') ??\n providers[0] ??\n null\n );\n }\n}\n","import { HttpClient, resolveConfig } from './http.js';\nimport { MessagesResource } from './resources/messages.js';\nimport { OtpResource } from './resources/otp.js';\nimport { ProvidersResource } from './resources/providers.js';\nimport type { ReachFlowClientOptions } from './types.js';\n\n/**\n * Official client for the ReachFlow public API (REST v1).\n *\n * @example\n * ```ts\n * const client = new ReachFlow({ apiKey: process.env.REACHFLOW_API_KEY! });\n * const { providers } = await client.providers.list();\n * ```\n */\nexport class ReachFlow {\n readonly messages: MessagesResource;\n readonly providers: ProvidersResource;\n readonly otp: OtpResource;\n\n private readonly http: HttpClient;\n private readonly config: ReturnType<typeof resolveConfig>;\n\n constructor(options: ReachFlowClientOptions) {\n if (!options.apiKey?.trim()) {\n throw new Error('ReachFlow: apiKey is required');\n }\n\n this.config = resolveConfig(options);\n this.http = new HttpClient(this.config);\n this.messages = new MessagesResource(this.http);\n this.providers = new ProvidersResource(this.http);\n this.otp = new OtpResource(this.http);\n }\n\n /** Configured base URL (without `/api/v1`). */\n get baseUrl(): string {\n return this.config.baseUrl;\n }\n}\n"],"mappings":"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","names":["sleep"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reachflow/sdk",
3
- "version": "0.1.0",
4
- "description": "Client officiel Node.js / TypeScript pour l'API publique ReachFlow",
3
+ "version": "0.1.1",
4
+ "description": "Official Node.js / TypeScript client for the ReachFlow public REST API (WhatsApp, OTP)",
5
5
  "author": "ReachFlow",
6
6
  "license": "MIT",
7
7
  "homepage": "https://docs.reachflow.me/developpeurs",