@reachflow/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -27
- package/dist/index.cjs +31 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -27
- package/dist/index.d.ts +40 -27
- package/dist/index.js +29 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @reachflow/sdk
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
**
|
|
11
|
+
**Requirements:** Node.js ≥ 18 (native `fetch`).
|
|
12
12
|
|
|
13
13
|
## Configuration
|
|
14
14
|
|
|
@@ -16,22 +16,27 @@ 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_…
|
|
20
|
-
|
|
21
|
-
timeoutMs: 30_000, //
|
|
22
|
-
maxRetries: 2, //
|
|
19
|
+
apiKey: process.env.REACHFLOW_API_KEY!, // rfl_live_… or rfl_test_…
|
|
20
|
+
environment: 'sandbox', // or 'live' — default: 'sandbox'
|
|
21
|
+
timeoutMs: 30_000, // optional
|
|
22
|
+
maxRetries: 2, // optional — 429 / 5xx
|
|
23
23
|
});
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
| `environment` | API host |
|
|
27
|
+
|---------------|----------|
|
|
28
|
+
| `sandbox` *(default)* | `sandbox-api.reachflow.me` |
|
|
29
|
+
| `live` | `api.reachflow.me` |
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
### Send a message
|
|
29
34
|
|
|
30
35
|
```typescript
|
|
31
36
|
const { messageId } = await client.messages.send({
|
|
32
|
-
providerId: '
|
|
37
|
+
providerId: 'your-provider-uuid',
|
|
33
38
|
to: '22996123456',
|
|
34
|
-
message: '
|
|
39
|
+
message: 'Your order is confirmed.',
|
|
35
40
|
});
|
|
36
41
|
|
|
37
42
|
const status = await client.messages.waitForTerminal(messageId);
|
|
@@ -42,12 +47,12 @@ console.log(status.status); // sent | delivered | failed | …
|
|
|
42
47
|
|
|
43
48
|
```typescript
|
|
44
49
|
const { otpId } = await client.otp.send({
|
|
45
|
-
providerId: '
|
|
50
|
+
providerId: 'your-provider-uuid',
|
|
46
51
|
phoneNumber: '22996123456',
|
|
47
|
-
brandName: '
|
|
52
|
+
brandName: 'My App',
|
|
48
53
|
});
|
|
49
54
|
|
|
50
|
-
//
|
|
55
|
+
// The code is delivered on WhatsApp only — never in the JSON response.
|
|
51
56
|
const result = await client.otp.verify({ otpId, code: '482910' });
|
|
52
57
|
console.log(result.valid);
|
|
53
58
|
```
|
|
@@ -59,7 +64,7 @@ const { providers } = await client.providers.list();
|
|
|
59
64
|
const connected = await client.providers.findConnected();
|
|
60
65
|
```
|
|
61
66
|
|
|
62
|
-
##
|
|
67
|
+
## Error handling
|
|
63
68
|
|
|
64
69
|
```typescript
|
|
65
70
|
import { ReachFlow, ReachFlowError } from '@reachflow/sdk';
|
|
@@ -70,24 +75,24 @@ try {
|
|
|
70
75
|
if (err instanceof ReachFlowError) {
|
|
71
76
|
console.error(err.statusCode, err.code, err.message);
|
|
72
77
|
if (err.retryable) {
|
|
73
|
-
// retry
|
|
78
|
+
// manual retry if needed
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
```
|
|
78
83
|
|
|
79
|
-
| Code |
|
|
80
|
-
|
|
81
|
-
| `unauthorized` |
|
|
82
|
-
| `plan_required` | Plan
|
|
83
|
-
| `insufficient_scope` |
|
|
84
|
-
| `rate_limit_exceeded` |
|
|
85
|
-
| `validation_error` |
|
|
86
|
-
| `not_found` |
|
|
84
|
+
| Code | Meaning |
|
|
85
|
+
|------|---------|
|
|
86
|
+
| `unauthorized` | Invalid or revoked API key |
|
|
87
|
+
| `plan_required` | Plan or API add-on missing |
|
|
88
|
+
| `insufficient_scope` | API key scope insufficient |
|
|
89
|
+
| `rate_limit_exceeded` | HTTP rate limit exceeded |
|
|
90
|
+
| `validation_error` | Invalid request body |
|
|
91
|
+
| `not_found` | Resource not found |
|
|
87
92
|
|
|
88
|
-
##
|
|
93
|
+
## Idempotency
|
|
89
94
|
|
|
90
|
-
|
|
95
|
+
Pass `idempotencyKey` on `messages.send`, `sendMedia`, or `sendBulk`:
|
|
91
96
|
|
|
92
97
|
```typescript
|
|
93
98
|
await client.messages.send({
|
|
@@ -96,7 +101,7 @@ await client.messages.send({
|
|
|
96
101
|
});
|
|
97
102
|
```
|
|
98
103
|
|
|
99
|
-
##
|
|
104
|
+
## Development
|
|
100
105
|
|
|
101
106
|
```bash
|
|
102
107
|
npm install
|
|
@@ -104,6 +109,6 @@ npm test
|
|
|
104
109
|
npm run build
|
|
105
110
|
```
|
|
106
111
|
|
|
107
|
-
##
|
|
112
|
+
## License
|
|
108
113
|
|
|
109
114
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
24
|
+
DEFAULT_ENVIRONMENT: () => DEFAULT_ENVIRONMENT,
|
|
25
|
+
REACHFLOW_ENVIRONMENTS: () => REACHFLOW_ENVIRONMENTS,
|
|
24
26
|
ReachFlow: () => ReachFlow,
|
|
25
27
|
ReachFlowError: () => ReachFlowError,
|
|
26
28
|
TERMINAL_MESSAGE_STATUSES: () => TERMINAL_MESSAGE_STATUSES
|
|
@@ -107,19 +109,26 @@ function parseRetryAfterMs(header) {
|
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
// src/types.ts
|
|
112
|
+
var REACHFLOW_ENVIRONMENTS = {
|
|
113
|
+
sandbox: "https://sandbox-api.reachflow.me",
|
|
114
|
+
live: "https://api.reachflow.me"
|
|
115
|
+
};
|
|
116
|
+
var DEFAULT_ENVIRONMENT = "sandbox";
|
|
110
117
|
var TERMINAL_MESSAGE_STATUSES = /* @__PURE__ */ new Set([
|
|
111
118
|
"sent",
|
|
112
119
|
"delivered",
|
|
113
120
|
"failed",
|
|
114
121
|
"cancelled"
|
|
115
122
|
]);
|
|
116
|
-
var DEFAULT_BASE_URL =
|
|
123
|
+
var DEFAULT_BASE_URL = REACHFLOW_ENVIRONMENTS.sandbox;
|
|
117
124
|
|
|
118
125
|
// src/http.ts
|
|
119
126
|
function resolveConfig(options) {
|
|
120
|
-
const
|
|
127
|
+
const environment = options.environment ?? DEFAULT_ENVIRONMENT;
|
|
128
|
+
const baseUrl = REACHFLOW_ENVIRONMENTS[environment].replace(/\/$/, "");
|
|
121
129
|
return {
|
|
122
130
|
apiKey: options.apiKey.trim(),
|
|
131
|
+
environment,
|
|
123
132
|
baseUrl,
|
|
124
133
|
apiPrefix: `${baseUrl}/api/v1`,
|
|
125
134
|
timeoutMs: options.timeoutMs ?? 3e4,
|
|
@@ -228,7 +237,7 @@ var MessagesResource = class {
|
|
|
228
237
|
this.http = http;
|
|
229
238
|
}
|
|
230
239
|
http;
|
|
231
|
-
/**
|
|
240
|
+
/** Send a text message (HTTP 202). */
|
|
232
241
|
async send(params) {
|
|
233
242
|
const { idempotencyKey, ...body } = params;
|
|
234
243
|
return this.http.request({
|
|
@@ -238,7 +247,7 @@ var MessagesResource = class {
|
|
|
238
247
|
idempotencyKey
|
|
239
248
|
});
|
|
240
249
|
}
|
|
241
|
-
/**
|
|
250
|
+
/** Send media via a public HTTPS URL (HTTP 202). */
|
|
242
251
|
async sendMedia(params) {
|
|
243
252
|
const { idempotencyKey, ...body } = params;
|
|
244
253
|
return this.http.request({
|
|
@@ -248,7 +257,7 @@ var MessagesResource = class {
|
|
|
248
257
|
idempotencyKey
|
|
249
258
|
});
|
|
250
259
|
}
|
|
251
|
-
/**
|
|
260
|
+
/** Send a bulk batch (same template, multiple recipients). */
|
|
252
261
|
async sendBulk(params) {
|
|
253
262
|
const { idempotencyKey, ...body } = params;
|
|
254
263
|
return this.http.request({
|
|
@@ -258,7 +267,7 @@ var MessagesResource = class {
|
|
|
258
267
|
idempotencyKey
|
|
259
268
|
});
|
|
260
269
|
}
|
|
261
|
-
/**
|
|
270
|
+
/** Get the status of a previously accepted message. */
|
|
262
271
|
async getStatus(messageId) {
|
|
263
272
|
return this.http.request({
|
|
264
273
|
method: "GET",
|
|
@@ -266,8 +275,8 @@ var MessagesResource = class {
|
|
|
266
275
|
});
|
|
267
276
|
}
|
|
268
277
|
/**
|
|
269
|
-
* Poll
|
|
270
|
-
*
|
|
278
|
+
* Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
|
|
279
|
+
* Throws `ReachFlowError` on timeout.
|
|
271
280
|
*/
|
|
272
281
|
async waitForTerminal(messageId, options) {
|
|
273
282
|
const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
|
|
@@ -299,8 +308,8 @@ var OtpResource = class {
|
|
|
299
308
|
}
|
|
300
309
|
http;
|
|
301
310
|
/**
|
|
302
|
-
*
|
|
303
|
-
*
|
|
311
|
+
* Generate and send an OTP via WhatsApp.
|
|
312
|
+
* The code is never returned by the API — only delivered on WhatsApp.
|
|
304
313
|
*/
|
|
305
314
|
async send(params) {
|
|
306
315
|
return this.http.request({
|
|
@@ -309,7 +318,7 @@ var OtpResource = class {
|
|
|
309
318
|
body: params
|
|
310
319
|
});
|
|
311
320
|
}
|
|
312
|
-
/**
|
|
321
|
+
/** Verify a code entered by the end user. */
|
|
313
322
|
async verify(params) {
|
|
314
323
|
return this.http.request({
|
|
315
324
|
method: "POST",
|
|
@@ -325,14 +334,14 @@ var ProvidersResource = class {
|
|
|
325
334
|
this.http = http;
|
|
326
335
|
}
|
|
327
336
|
http;
|
|
328
|
-
/**
|
|
337
|
+
/** List WhatsApp numbers available via the API. */
|
|
329
338
|
async list() {
|
|
330
339
|
return this.http.request({
|
|
331
340
|
method: "GET",
|
|
332
341
|
path: "/providers"
|
|
333
342
|
});
|
|
334
343
|
}
|
|
335
|
-
/**
|
|
344
|
+
/** Get a provider with daily statistics. */
|
|
336
345
|
async get(providerId) {
|
|
337
346
|
return this.http.request({
|
|
338
347
|
method: "GET",
|
|
@@ -340,8 +349,8 @@ var ProvidersResource = class {
|
|
|
340
349
|
});
|
|
341
350
|
}
|
|
342
351
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
352
|
+
* Return the first `connected` provider, or the first in the list.
|
|
353
|
+
* Handy for smoke tests and quick starts.
|
|
345
354
|
*/
|
|
346
355
|
async findConnected() {
|
|
347
356
|
const { providers } = await this.list();
|
|
@@ -367,7 +376,11 @@ var ReachFlow = class {
|
|
|
367
376
|
this.providers = new ProvidersResource(this.http);
|
|
368
377
|
this.otp = new OtpResource(this.http);
|
|
369
378
|
}
|
|
370
|
-
/**
|
|
379
|
+
/** Configured environment (`sandbox` or `live`). */
|
|
380
|
+
get environment() {
|
|
381
|
+
return this.config.environment;
|
|
382
|
+
}
|
|
383
|
+
/** Resolved API host (without `/api/v1`). */
|
|
371
384
|
get baseUrl() {
|
|
372
385
|
return this.config.baseUrl;
|
|
373
386
|
}
|
|
@@ -375,6 +388,8 @@ var ReachFlow = class {
|
|
|
375
388
|
// Annotate the CommonJS export names for ESM import in node:
|
|
376
389
|
0 && (module.exports = {
|
|
377
390
|
DEFAULT_BASE_URL,
|
|
391
|
+
DEFAULT_ENVIRONMENT,
|
|
392
|
+
REACHFLOW_ENVIRONMENTS,
|
|
378
393
|
ReachFlow,
|
|
379
394
|
ReachFlowError,
|
|
380
395
|
TERMINAL_MESSAGE_STATUSES
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 DEFAULT_ENVIRONMENT,\n REACHFLOW_ENVIRONMENTS,\n TERMINAL_MESSAGE_STATUSES,\n} from './types.js';\nexport type { ReachFlowEnvironment } 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\n/** Official ReachFlow API hosts (no custom base URL required). */\nexport const REACHFLOW_ENVIRONMENTS = {\n sandbox: 'https://sandbox-api.reachflow.me',\n live: 'https://api.reachflow.me',\n} as const;\n\nexport type ReachFlowEnvironment = keyof typeof REACHFLOW_ENVIRONMENTS;\n\nexport const DEFAULT_ENVIRONMENT: ReachFlowEnvironment = 'sandbox';\n\nexport interface ReachFlowClientOptions {\n /** API key (`rfl_live_…` or `rfl_test_…`). */\n apiKey: string;\n /**\n * ReachFlow API environment.\n * - `sandbox` → sandbox-api.reachflow.me\n * - `live` → api.reachflow.me\n * @default \"sandbox\"\n */\n environment?: ReachFlowEnvironment;\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\n/** @deprecated Use `REACHFLOW_ENVIRONMENTS` and `environment` instead. */\nexport const DEFAULT_BASE_URL = REACHFLOW_ENVIRONMENTS.sandbox;\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport {\n DEFAULT_ENVIRONMENT,\n REACHFLOW_ENVIRONMENTS,\n} 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 environment: keyof typeof REACHFLOW_ENVIRONMENTS;\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 environment = options.environment ?? DEFAULT_ENVIRONMENT;\n const baseUrl = REACHFLOW_ENVIRONMENTS[environment].replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n environment,\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, ReachFlowEnvironment } 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 environment (`sandbox` or `live`). */\n get environment(): ReachFlowEnvironment {\n return this.config.environment;\n }\n\n /** Resolved API host (without `/api/v1`). */\n get baseUrl(): string {\n return this.config.baseUrl;\n }\n}\n"],"mappings":"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","names":["sleep"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
/**
|
|
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
|
+
/** Official ReachFlow API hosts (no custom base URL required). */
|
|
7
|
+
declare const REACHFLOW_ENVIRONMENTS: {
|
|
8
|
+
readonly sandbox: "https://sandbox-api.reachflow.me";
|
|
9
|
+
readonly live: "https://api.reachflow.me";
|
|
10
|
+
};
|
|
11
|
+
type ReachFlowEnvironment = keyof typeof REACHFLOW_ENVIRONMENTS;
|
|
12
|
+
declare const DEFAULT_ENVIRONMENT: ReachFlowEnvironment;
|
|
6
13
|
interface ReachFlowClientOptions {
|
|
7
|
-
/**
|
|
14
|
+
/** API key (`rfl_live_…` or `rfl_test_…`). */
|
|
8
15
|
apiKey: string;
|
|
9
16
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
17
|
+
* ReachFlow API environment.
|
|
18
|
+
* - `sandbox` → sandbox-api.reachflow.me
|
|
19
|
+
* - `live` → api.reachflow.me
|
|
20
|
+
* @default "sandbox"
|
|
12
21
|
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
22
|
+
environment?: ReachFlowEnvironment;
|
|
23
|
+
/** HTTP timeout in milliseconds. @default 30000 */
|
|
15
24
|
timeoutMs?: number;
|
|
16
|
-
/**
|
|
25
|
+
/** Custom fetch implementation (tests, Node < 18). @default global fetch */
|
|
17
26
|
fetch?: typeof fetch;
|
|
18
|
-
/**
|
|
27
|
+
/** Max retries on 429 / retryable 5xx. @default 2 */
|
|
19
28
|
maxRetries?: number;
|
|
20
29
|
}
|
|
21
30
|
interface SendMessageParams {
|
|
@@ -121,13 +130,14 @@ interface OtpVerifyResponse {
|
|
|
121
130
|
attemptsLeft?: number;
|
|
122
131
|
}
|
|
123
132
|
interface WaitForTerminalOptions {
|
|
124
|
-
/**
|
|
133
|
+
/** Poll interval in ms. @default 2000 */
|
|
125
134
|
pollIntervalMs?: number;
|
|
126
|
-
/**
|
|
135
|
+
/** Total timeout in ms. @default 120000 */
|
|
127
136
|
timeoutMs?: number;
|
|
128
137
|
}
|
|
129
138
|
declare const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus>;
|
|
130
|
-
|
|
139
|
+
/** @deprecated Use `REACHFLOW_ENVIRONMENTS` and `environment` instead. */
|
|
140
|
+
declare const DEFAULT_BASE_URL: "https://sandbox-api.reachflow.me";
|
|
131
141
|
|
|
132
142
|
interface RequestOptions {
|
|
133
143
|
method: 'GET' | 'POST';
|
|
@@ -137,6 +147,7 @@ interface RequestOptions {
|
|
|
137
147
|
}
|
|
138
148
|
interface ResolvedClientConfig {
|
|
139
149
|
apiKey: string;
|
|
150
|
+
environment: keyof typeof REACHFLOW_ENVIRONMENTS;
|
|
140
151
|
baseUrl: string;
|
|
141
152
|
apiPrefix: string;
|
|
142
153
|
timeoutMs: number;
|
|
@@ -153,17 +164,17 @@ declare class HttpClient {
|
|
|
153
164
|
declare class MessagesResource {
|
|
154
165
|
private readonly http;
|
|
155
166
|
constructor(http: HttpClient);
|
|
156
|
-
/**
|
|
167
|
+
/** Send a text message (HTTP 202). */
|
|
157
168
|
send(params: SendMessageParams): Promise<SendAcceptedResponse>;
|
|
158
|
-
/**
|
|
169
|
+
/** Send media via a public HTTPS URL (HTTP 202). */
|
|
159
170
|
sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse>;
|
|
160
|
-
/**
|
|
171
|
+
/** Send a bulk batch (same template, multiple recipients). */
|
|
161
172
|
sendBulk(params: SendBulkParams): Promise<SendBulkResponse>;
|
|
162
|
-
/**
|
|
173
|
+
/** Get the status of a previously accepted message. */
|
|
163
174
|
getStatus(messageId: string): Promise<MessageStatusResponse>;
|
|
164
175
|
/**
|
|
165
|
-
* Poll
|
|
166
|
-
*
|
|
176
|
+
* Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
|
|
177
|
+
* Throws `ReachFlowError` on timeout.
|
|
167
178
|
*/
|
|
168
179
|
waitForTerminal(messageId: string, options?: WaitForTerminalOptions): Promise<MessageStatusResponse>;
|
|
169
180
|
}
|
|
@@ -172,30 +183,30 @@ declare class OtpResource {
|
|
|
172
183
|
private readonly http;
|
|
173
184
|
constructor(http: HttpClient);
|
|
174
185
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
186
|
+
* Generate and send an OTP via WhatsApp.
|
|
187
|
+
* The code is never returned by the API — only delivered on WhatsApp.
|
|
177
188
|
*/
|
|
178
189
|
send(params: OtpSendParams): Promise<OtpSendResponse>;
|
|
179
|
-
/**
|
|
190
|
+
/** Verify a code entered by the end user. */
|
|
180
191
|
verify(params: OtpVerifyParams): Promise<OtpVerifyResponse>;
|
|
181
192
|
}
|
|
182
193
|
|
|
183
194
|
declare class ProvidersResource {
|
|
184
195
|
private readonly http;
|
|
185
196
|
constructor(http: HttpClient);
|
|
186
|
-
/**
|
|
197
|
+
/** List WhatsApp numbers available via the API. */
|
|
187
198
|
list(): Promise<ProviderListResponse>;
|
|
188
|
-
/**
|
|
199
|
+
/** Get a provider with daily statistics. */
|
|
189
200
|
get(providerId: string): Promise<ProviderDetail>;
|
|
190
201
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
202
|
+
* Return the first `connected` provider, or the first in the list.
|
|
203
|
+
* Handy for smoke tests and quick starts.
|
|
193
204
|
*/
|
|
194
205
|
findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null>;
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
/**
|
|
198
|
-
*
|
|
209
|
+
* Official client for the ReachFlow public API (REST v1).
|
|
199
210
|
*
|
|
200
211
|
* @example
|
|
201
212
|
* ```ts
|
|
@@ -210,7 +221,9 @@ declare class ReachFlow {
|
|
|
210
221
|
private readonly http;
|
|
211
222
|
private readonly config;
|
|
212
223
|
constructor(options: ReachFlowClientOptions);
|
|
213
|
-
/**
|
|
224
|
+
/** Configured environment (`sandbox` or `live`). */
|
|
225
|
+
get environment(): ReachFlowEnvironment;
|
|
226
|
+
/** Resolved API host (without `/api/v1`). */
|
|
214
227
|
get baseUrl(): string;
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -235,4 +248,4 @@ declare class ReachFlowError extends Error {
|
|
|
235
248
|
static timeout(timeoutMs: number): ReachFlowError;
|
|
236
249
|
}
|
|
237
250
|
|
|
238
|
-
export { type BulkRecipient, DEFAULT_BASE_URL, type FailureCode, type MediaType, type MessageStatus, type MessageStatusResponse, type OtpSendParams, type OtpSendResponse, type OtpVerifyParams, type OtpVerifyReason, type OtpVerifyResponse, type ProviderDailyStats, type ProviderDetail, type ProviderListResponse, type ProviderSummary, ReachFlow, type ReachFlowClientOptions, ReachFlowError, type ReachFlowErrorCode, type SendAcceptedResponse, type SendBulkParams, type SendBulkResponse, type SendMediaParams, type SendMessageParams, TERMINAL_MESSAGE_STATUSES, type WaitForTerminalOptions };
|
|
251
|
+
export { type BulkRecipient, DEFAULT_BASE_URL, DEFAULT_ENVIRONMENT, type FailureCode, type MediaType, type MessageStatus, type MessageStatusResponse, type OtpSendParams, type OtpSendResponse, type OtpVerifyParams, type OtpVerifyReason, type OtpVerifyResponse, type ProviderDailyStats, type ProviderDetail, type ProviderListResponse, type ProviderSummary, REACHFLOW_ENVIRONMENTS, ReachFlow, type ReachFlowClientOptions, type ReachFlowEnvironment, ReachFlowError, type ReachFlowErrorCode, type SendAcceptedResponse, type SendBulkParams, type SendBulkResponse, type SendMediaParams, type SendMessageParams, TERMINAL_MESSAGE_STATUSES, type WaitForTerminalOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
/**
|
|
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
|
+
/** Official ReachFlow API hosts (no custom base URL required). */
|
|
7
|
+
declare const REACHFLOW_ENVIRONMENTS: {
|
|
8
|
+
readonly sandbox: "https://sandbox-api.reachflow.me";
|
|
9
|
+
readonly live: "https://api.reachflow.me";
|
|
10
|
+
};
|
|
11
|
+
type ReachFlowEnvironment = keyof typeof REACHFLOW_ENVIRONMENTS;
|
|
12
|
+
declare const DEFAULT_ENVIRONMENT: ReachFlowEnvironment;
|
|
6
13
|
interface ReachFlowClientOptions {
|
|
7
|
-
/**
|
|
14
|
+
/** API key (`rfl_live_…` or `rfl_test_…`). */
|
|
8
15
|
apiKey: string;
|
|
9
16
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
17
|
+
* ReachFlow API environment.
|
|
18
|
+
* - `sandbox` → sandbox-api.reachflow.me
|
|
19
|
+
* - `live` → api.reachflow.me
|
|
20
|
+
* @default "sandbox"
|
|
12
21
|
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
22
|
+
environment?: ReachFlowEnvironment;
|
|
23
|
+
/** HTTP timeout in milliseconds. @default 30000 */
|
|
15
24
|
timeoutMs?: number;
|
|
16
|
-
/**
|
|
25
|
+
/** Custom fetch implementation (tests, Node < 18). @default global fetch */
|
|
17
26
|
fetch?: typeof fetch;
|
|
18
|
-
/**
|
|
27
|
+
/** Max retries on 429 / retryable 5xx. @default 2 */
|
|
19
28
|
maxRetries?: number;
|
|
20
29
|
}
|
|
21
30
|
interface SendMessageParams {
|
|
@@ -121,13 +130,14 @@ interface OtpVerifyResponse {
|
|
|
121
130
|
attemptsLeft?: number;
|
|
122
131
|
}
|
|
123
132
|
interface WaitForTerminalOptions {
|
|
124
|
-
/**
|
|
133
|
+
/** Poll interval in ms. @default 2000 */
|
|
125
134
|
pollIntervalMs?: number;
|
|
126
|
-
/**
|
|
135
|
+
/** Total timeout in ms. @default 120000 */
|
|
127
136
|
timeoutMs?: number;
|
|
128
137
|
}
|
|
129
138
|
declare const TERMINAL_MESSAGE_STATUSES: ReadonlySet<MessageStatus>;
|
|
130
|
-
|
|
139
|
+
/** @deprecated Use `REACHFLOW_ENVIRONMENTS` and `environment` instead. */
|
|
140
|
+
declare const DEFAULT_BASE_URL: "https://sandbox-api.reachflow.me";
|
|
131
141
|
|
|
132
142
|
interface RequestOptions {
|
|
133
143
|
method: 'GET' | 'POST';
|
|
@@ -137,6 +147,7 @@ interface RequestOptions {
|
|
|
137
147
|
}
|
|
138
148
|
interface ResolvedClientConfig {
|
|
139
149
|
apiKey: string;
|
|
150
|
+
environment: keyof typeof REACHFLOW_ENVIRONMENTS;
|
|
140
151
|
baseUrl: string;
|
|
141
152
|
apiPrefix: string;
|
|
142
153
|
timeoutMs: number;
|
|
@@ -153,17 +164,17 @@ declare class HttpClient {
|
|
|
153
164
|
declare class MessagesResource {
|
|
154
165
|
private readonly http;
|
|
155
166
|
constructor(http: HttpClient);
|
|
156
|
-
/**
|
|
167
|
+
/** Send a text message (HTTP 202). */
|
|
157
168
|
send(params: SendMessageParams): Promise<SendAcceptedResponse>;
|
|
158
|
-
/**
|
|
169
|
+
/** Send media via a public HTTPS URL (HTTP 202). */
|
|
159
170
|
sendMedia(params: SendMediaParams): Promise<SendAcceptedResponse>;
|
|
160
|
-
/**
|
|
171
|
+
/** Send a bulk batch (same template, multiple recipients). */
|
|
161
172
|
sendBulk(params: SendBulkParams): Promise<SendBulkResponse>;
|
|
162
|
-
/**
|
|
173
|
+
/** Get the status of a previously accepted message. */
|
|
163
174
|
getStatus(messageId: string): Promise<MessageStatusResponse>;
|
|
164
175
|
/**
|
|
165
|
-
* Poll
|
|
166
|
-
*
|
|
176
|
+
* Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
|
|
177
|
+
* Throws `ReachFlowError` on timeout.
|
|
167
178
|
*/
|
|
168
179
|
waitForTerminal(messageId: string, options?: WaitForTerminalOptions): Promise<MessageStatusResponse>;
|
|
169
180
|
}
|
|
@@ -172,30 +183,30 @@ declare class OtpResource {
|
|
|
172
183
|
private readonly http;
|
|
173
184
|
constructor(http: HttpClient);
|
|
174
185
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
186
|
+
* Generate and send an OTP via WhatsApp.
|
|
187
|
+
* The code is never returned by the API — only delivered on WhatsApp.
|
|
177
188
|
*/
|
|
178
189
|
send(params: OtpSendParams): Promise<OtpSendResponse>;
|
|
179
|
-
/**
|
|
190
|
+
/** Verify a code entered by the end user. */
|
|
180
191
|
verify(params: OtpVerifyParams): Promise<OtpVerifyResponse>;
|
|
181
192
|
}
|
|
182
193
|
|
|
183
194
|
declare class ProvidersResource {
|
|
184
195
|
private readonly http;
|
|
185
196
|
constructor(http: HttpClient);
|
|
186
|
-
/**
|
|
197
|
+
/** List WhatsApp numbers available via the API. */
|
|
187
198
|
list(): Promise<ProviderListResponse>;
|
|
188
|
-
/**
|
|
199
|
+
/** Get a provider with daily statistics. */
|
|
189
200
|
get(providerId: string): Promise<ProviderDetail>;
|
|
190
201
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
202
|
+
* Return the first `connected` provider, or the first in the list.
|
|
203
|
+
* Handy for smoke tests and quick starts.
|
|
193
204
|
*/
|
|
194
205
|
findConnected(): Promise<ProviderDetail | ProviderListResponse['providers'][number] | null>;
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
/**
|
|
198
|
-
*
|
|
209
|
+
* Official client for the ReachFlow public API (REST v1).
|
|
199
210
|
*
|
|
200
211
|
* @example
|
|
201
212
|
* ```ts
|
|
@@ -210,7 +221,9 @@ declare class ReachFlow {
|
|
|
210
221
|
private readonly http;
|
|
211
222
|
private readonly config;
|
|
212
223
|
constructor(options: ReachFlowClientOptions);
|
|
213
|
-
/**
|
|
224
|
+
/** Configured environment (`sandbox` or `live`). */
|
|
225
|
+
get environment(): ReachFlowEnvironment;
|
|
226
|
+
/** Resolved API host (without `/api/v1`). */
|
|
214
227
|
get baseUrl(): string;
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -235,4 +248,4 @@ declare class ReachFlowError extends Error {
|
|
|
235
248
|
static timeout(timeoutMs: number): ReachFlowError;
|
|
236
249
|
}
|
|
237
250
|
|
|
238
|
-
export { type BulkRecipient, DEFAULT_BASE_URL, type FailureCode, type MediaType, type MessageStatus, type MessageStatusResponse, type OtpSendParams, type OtpSendResponse, type OtpVerifyParams, type OtpVerifyReason, type OtpVerifyResponse, type ProviderDailyStats, type ProviderDetail, type ProviderListResponse, type ProviderSummary, ReachFlow, type ReachFlowClientOptions, ReachFlowError, type ReachFlowErrorCode, type SendAcceptedResponse, type SendBulkParams, type SendBulkResponse, type SendMediaParams, type SendMessageParams, TERMINAL_MESSAGE_STATUSES, type WaitForTerminalOptions };
|
|
251
|
+
export { type BulkRecipient, DEFAULT_BASE_URL, DEFAULT_ENVIRONMENT, type FailureCode, type MediaType, type MessageStatus, type MessageStatusResponse, type OtpSendParams, type OtpSendResponse, type OtpVerifyParams, type OtpVerifyReason, type OtpVerifyResponse, type ProviderDailyStats, type ProviderDetail, type ProviderListResponse, type ProviderSummary, REACHFLOW_ENVIRONMENTS, ReachFlow, type ReachFlowClientOptions, type ReachFlowEnvironment, ReachFlowError, type ReachFlowErrorCode, type SendAcceptedResponse, type SendBulkParams, type SendBulkResponse, type SendMediaParams, type SendMessageParams, TERMINAL_MESSAGE_STATUSES, type WaitForTerminalOptions };
|
package/dist/index.js
CHANGED
|
@@ -78,19 +78,26 @@ function parseRetryAfterMs(header) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// src/types.ts
|
|
81
|
+
var REACHFLOW_ENVIRONMENTS = {
|
|
82
|
+
sandbox: "https://sandbox-api.reachflow.me",
|
|
83
|
+
live: "https://api.reachflow.me"
|
|
84
|
+
};
|
|
85
|
+
var DEFAULT_ENVIRONMENT = "sandbox";
|
|
81
86
|
var TERMINAL_MESSAGE_STATUSES = /* @__PURE__ */ new Set([
|
|
82
87
|
"sent",
|
|
83
88
|
"delivered",
|
|
84
89
|
"failed",
|
|
85
90
|
"cancelled"
|
|
86
91
|
]);
|
|
87
|
-
var DEFAULT_BASE_URL =
|
|
92
|
+
var DEFAULT_BASE_URL = REACHFLOW_ENVIRONMENTS.sandbox;
|
|
88
93
|
|
|
89
94
|
// src/http.ts
|
|
90
95
|
function resolveConfig(options) {
|
|
91
|
-
const
|
|
96
|
+
const environment = options.environment ?? DEFAULT_ENVIRONMENT;
|
|
97
|
+
const baseUrl = REACHFLOW_ENVIRONMENTS[environment].replace(/\/$/, "");
|
|
92
98
|
return {
|
|
93
99
|
apiKey: options.apiKey.trim(),
|
|
100
|
+
environment,
|
|
94
101
|
baseUrl,
|
|
95
102
|
apiPrefix: `${baseUrl}/api/v1`,
|
|
96
103
|
timeoutMs: options.timeoutMs ?? 3e4,
|
|
@@ -199,7 +206,7 @@ var MessagesResource = class {
|
|
|
199
206
|
this.http = http;
|
|
200
207
|
}
|
|
201
208
|
http;
|
|
202
|
-
/**
|
|
209
|
+
/** Send a text message (HTTP 202). */
|
|
203
210
|
async send(params) {
|
|
204
211
|
const { idempotencyKey, ...body } = params;
|
|
205
212
|
return this.http.request({
|
|
@@ -209,7 +216,7 @@ var MessagesResource = class {
|
|
|
209
216
|
idempotencyKey
|
|
210
217
|
});
|
|
211
218
|
}
|
|
212
|
-
/**
|
|
219
|
+
/** Send media via a public HTTPS URL (HTTP 202). */
|
|
213
220
|
async sendMedia(params) {
|
|
214
221
|
const { idempotencyKey, ...body } = params;
|
|
215
222
|
return this.http.request({
|
|
@@ -219,7 +226,7 @@ var MessagesResource = class {
|
|
|
219
226
|
idempotencyKey
|
|
220
227
|
});
|
|
221
228
|
}
|
|
222
|
-
/**
|
|
229
|
+
/** Send a bulk batch (same template, multiple recipients). */
|
|
223
230
|
async sendBulk(params) {
|
|
224
231
|
const { idempotencyKey, ...body } = params;
|
|
225
232
|
return this.http.request({
|
|
@@ -229,7 +236,7 @@ var MessagesResource = class {
|
|
|
229
236
|
idempotencyKey
|
|
230
237
|
});
|
|
231
238
|
}
|
|
232
|
-
/**
|
|
239
|
+
/** Get the status of a previously accepted message. */
|
|
233
240
|
async getStatus(messageId) {
|
|
234
241
|
return this.http.request({
|
|
235
242
|
method: "GET",
|
|
@@ -237,8 +244,8 @@ var MessagesResource = class {
|
|
|
237
244
|
});
|
|
238
245
|
}
|
|
239
246
|
/**
|
|
240
|
-
* Poll
|
|
241
|
-
*
|
|
247
|
+
* Poll until a terminal status (`sent`, `delivered`, `failed`, `cancelled`).
|
|
248
|
+
* Throws `ReachFlowError` on timeout.
|
|
242
249
|
*/
|
|
243
250
|
async waitForTerminal(messageId, options) {
|
|
244
251
|
const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
|
|
@@ -270,8 +277,8 @@ var OtpResource = class {
|
|
|
270
277
|
}
|
|
271
278
|
http;
|
|
272
279
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
280
|
+
* Generate and send an OTP via WhatsApp.
|
|
281
|
+
* The code is never returned by the API — only delivered on WhatsApp.
|
|
275
282
|
*/
|
|
276
283
|
async send(params) {
|
|
277
284
|
return this.http.request({
|
|
@@ -280,7 +287,7 @@ var OtpResource = class {
|
|
|
280
287
|
body: params
|
|
281
288
|
});
|
|
282
289
|
}
|
|
283
|
-
/**
|
|
290
|
+
/** Verify a code entered by the end user. */
|
|
284
291
|
async verify(params) {
|
|
285
292
|
return this.http.request({
|
|
286
293
|
method: "POST",
|
|
@@ -296,14 +303,14 @@ var ProvidersResource = class {
|
|
|
296
303
|
this.http = http;
|
|
297
304
|
}
|
|
298
305
|
http;
|
|
299
|
-
/**
|
|
306
|
+
/** List WhatsApp numbers available via the API. */
|
|
300
307
|
async list() {
|
|
301
308
|
return this.http.request({
|
|
302
309
|
method: "GET",
|
|
303
310
|
path: "/providers"
|
|
304
311
|
});
|
|
305
312
|
}
|
|
306
|
-
/**
|
|
313
|
+
/** Get a provider with daily statistics. */
|
|
307
314
|
async get(providerId) {
|
|
308
315
|
return this.http.request({
|
|
309
316
|
method: "GET",
|
|
@@ -311,8 +318,8 @@ var ProvidersResource = class {
|
|
|
311
318
|
});
|
|
312
319
|
}
|
|
313
320
|
/**
|
|
314
|
-
*
|
|
315
|
-
*
|
|
321
|
+
* Return the first `connected` provider, or the first in the list.
|
|
322
|
+
* Handy for smoke tests and quick starts.
|
|
316
323
|
*/
|
|
317
324
|
async findConnected() {
|
|
318
325
|
const { providers } = await this.list();
|
|
@@ -338,13 +345,19 @@ var ReachFlow = class {
|
|
|
338
345
|
this.providers = new ProvidersResource(this.http);
|
|
339
346
|
this.otp = new OtpResource(this.http);
|
|
340
347
|
}
|
|
341
|
-
/**
|
|
348
|
+
/** Configured environment (`sandbox` or `live`). */
|
|
349
|
+
get environment() {
|
|
350
|
+
return this.config.environment;
|
|
351
|
+
}
|
|
352
|
+
/** Resolved API host (without `/api/v1`). */
|
|
342
353
|
get baseUrl() {
|
|
343
354
|
return this.config.baseUrl;
|
|
344
355
|
}
|
|
345
356
|
};
|
|
346
357
|
export {
|
|
347
358
|
DEFAULT_BASE_URL,
|
|
359
|
+
DEFAULT_ENVIRONMENT,
|
|
360
|
+
REACHFLOW_ENVIRONMENTS,
|
|
348
361
|
ReachFlow,
|
|
349
362
|
ReachFlowError,
|
|
350
363
|
TERMINAL_MESSAGE_STATUSES
|
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\n/** Official ReachFlow API hosts (no custom base URL required). */\nexport const REACHFLOW_ENVIRONMENTS = {\n sandbox: 'https://sandbox-api.reachflow.me',\n live: 'https://api.reachflow.me',\n} as const;\n\nexport type ReachFlowEnvironment = keyof typeof REACHFLOW_ENVIRONMENTS;\n\nexport const DEFAULT_ENVIRONMENT: ReachFlowEnvironment = 'sandbox';\n\nexport interface ReachFlowClientOptions {\n /** API key (`rfl_live_…` or `rfl_test_…`). */\n apiKey: string;\n /**\n * ReachFlow API environment.\n * - `sandbox` → sandbox-api.reachflow.me\n * - `live` → api.reachflow.me\n * @default \"sandbox\"\n */\n environment?: ReachFlowEnvironment;\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\n/** @deprecated Use `REACHFLOW_ENVIRONMENTS` and `environment` instead. */\nexport const DEFAULT_BASE_URL = REACHFLOW_ENVIRONMENTS.sandbox;\n","import { ReachFlowError, parseRetryAfterMs } from './errors.js';\nimport type { ReachFlowClientOptions } from './types.js';\nimport {\n DEFAULT_ENVIRONMENT,\n REACHFLOW_ENVIRONMENTS,\n} 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 environment: keyof typeof REACHFLOW_ENVIRONMENTS;\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 environment = options.environment ?? DEFAULT_ENVIRONMENT;\n const baseUrl = REACHFLOW_ENVIRONMENTS[environment].replace(/\\/$/, '');\n return {\n apiKey: options.apiKey.trim(),\n environment,\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, ReachFlowEnvironment } 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 environment (`sandbox` or `live`). */\n get environment(): ReachFlowEnvironment {\n return this.config.environment;\n }\n\n /** Resolved API host (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.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|