sneeksdk 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/dist/index.d.mts +118 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.js +194 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +168 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# sneeksdk
|
|
2
|
+
|
|
3
|
+
Node.js SDK for the **Sneek Send** API — deliver OTPs and transactional
|
|
4
|
+
messages over SMS, WhatsApp, and email through Sneek's pre-approved DLT
|
|
5
|
+
sender, WhatsApp BSP number, and warmed email domain.
|
|
6
|
+
|
|
7
|
+
> Sneek **delivers**; your app generates and verifies its own OTP codes.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install sneeksdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Sneek } from 'sneeksdk';
|
|
19
|
+
|
|
20
|
+
const sneek = new Sneek({
|
|
21
|
+
apiUrl: process.env.SNEEK_API_URL ?? 'https://api.sneek.in',
|
|
22
|
+
apiKey: process.env.SNEEK_API_KEY!, // snk_live_… or snk_test_…
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Generic send (body OR template)
|
|
26
|
+
await sneek.messages.send({
|
|
27
|
+
to: '+919876543210',
|
|
28
|
+
body: 'Your OTP to login to ConfHub is 482917. Do not share it.',
|
|
29
|
+
channel: 'sms', // 'auto' | 'sms' | 'whatsapp' | 'email'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Channel helpers
|
|
33
|
+
await sneek.sendSMS('+919876543210', 'Your code is 482917');
|
|
34
|
+
await sneek.sendWhatsApp('+919876543210', 'Your code is 482917');
|
|
35
|
+
await sneek.sendEmail('user@example.com', 'Your code is 482917');
|
|
36
|
+
|
|
37
|
+
// OTP body-formatting helper (you generate the code yourself)
|
|
38
|
+
await sneek.sendOTP({
|
|
39
|
+
to: '+919876543210',
|
|
40
|
+
code: '482917',
|
|
41
|
+
appName: 'ConfHub',
|
|
42
|
+
channel: 'auto',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// DLT/provider templates (server-rendered)
|
|
46
|
+
await sneek.messages.send({
|
|
47
|
+
to: '+919876543210',
|
|
48
|
+
channel: 'sms',
|
|
49
|
+
template: 'otp_login',
|
|
50
|
+
variables: { appName: 'ConfHub', otp: '482917' },
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Message intent (`type`)
|
|
55
|
+
|
|
56
|
+
Declare whether a message is an OTP or a plain communication so Sneek picks the
|
|
57
|
+
correct approved provider template — instead of guessing from the body text:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// Auth / OTP template
|
|
61
|
+
await sneek.messages.send({ to, body: 'Your code is 482917', type: 'otp' });
|
|
62
|
+
|
|
63
|
+
// Utility / communication template
|
|
64
|
+
await sneek.messages.send({
|
|
65
|
+
to,
|
|
66
|
+
body: 'Your booking BK-7781 is confirmed.',
|
|
67
|
+
type: 'transactional',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`type` accepts `'otp'` or `'transactional'`. When omitted, Sneek infers intent
|
|
72
|
+
from the body (which can misclassify digit groups like `BK-7781` as an OTP), so
|
|
73
|
+
**passing `type` explicitly is recommended.** `sendOTP(...)` always tags
|
|
74
|
+
`type: 'otp'` for you.
|
|
75
|
+
|
|
76
|
+
## Authentication
|
|
77
|
+
|
|
78
|
+
Every request is sent with `Authorization: Bearer <apiKey>`. Mint keys from the
|
|
79
|
+
Sneek admin (`/o/<org>/applications/<id>` → API keys). Keys are shown **once**;
|
|
80
|
+
store them in your environment / secret manager. `snk_test_…` keys hit dev
|
|
81
|
+
adapters; `snk_live_…` keys deliver for real.
|
|
82
|
+
|
|
83
|
+
## Errors & retries
|
|
84
|
+
|
|
85
|
+
- Non-2xx responses throw `SneekApiError` with `{ status, type, title, detail }`
|
|
86
|
+
(RFC-7807 problem-details).
|
|
87
|
+
- Transient `5xx` and network failures are retried with exponential backoff
|
|
88
|
+
(`maxRetries`, default 2; `retryBaseMs`, default 200ms).
|
|
89
|
+
|
|
90
|
+
## Idempotency & rate limits
|
|
91
|
+
|
|
92
|
+
Pass `idempotencyKey` to make a send safely repeatable — the first call is
|
|
93
|
+
processed and its result is replayed for any retry with the same key (per
|
|
94
|
+
application), so a dropped connection never sends twice. This pairs with the
|
|
95
|
+
SDK's automatic `5xx` retries.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
await sneek.messages.send({
|
|
99
|
+
to: '+919876543210',
|
|
100
|
+
type: 'otp',
|
|
101
|
+
body: 'Your code is 482917',
|
|
102
|
+
idempotencyKey: 'order-4815-otp',
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Sends are rate-limited per application (your plan's per-minute / per-day quota),
|
|
107
|
+
per recipient, and per source IP. Exceeding a limit throws `SneekApiError` with
|
|
108
|
+
`status === 429`; the problem body carries `retry_after` (seconds).
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { SneekApiError } from 'sneeksdk';
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
await sneek.sendSMS('+91…', 'hi');
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (err instanceof SneekApiError && err.status === 401) {
|
|
118
|
+
// invalid / revoked key
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
| Method | Description |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `messages.send(input)` | Send a message (body or template; optional `type` intent). Returns `202` accepted envelope. |
|
|
128
|
+
| `messages.get(id)` | Fetch a previously-sent message by id. |
|
|
129
|
+
| `sendSMS/sendWhatsApp/sendEmail(to, body, clientRef?)` | Channel-pinned helpers. |
|
|
130
|
+
| `sendOTP({ to, code, appName?, channel?, template?, variables? })` | Format + send an OTP body. |
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
UNLICENSED — © Abblor Tech Pvt Ltd.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sneeksdk — Node.js SDK for the Sneek Send API.
|
|
3
|
+
*
|
|
4
|
+
* const sneek = new Sneek({
|
|
5
|
+
* apiUrl: 'https://api.sneek.in',
|
|
6
|
+
* apiKey: process.env.SNEEK_API_KEY!, // snk_live_… / snk_test_…
|
|
7
|
+
* });
|
|
8
|
+
*
|
|
9
|
+
* await sneek.messages.send({ to: '+91…', body: 'Your OTP is 123456' });
|
|
10
|
+
* await sneek.sendSMS('+91…', 'Your OTP is 123456');
|
|
11
|
+
* await sneek.sendOTP({ to: '+91…', code: '123456', appName: 'ConfHub' });
|
|
12
|
+
*
|
|
13
|
+
* Authenticates with `Authorization: Bearer snk_…` against the server's
|
|
14
|
+
* `BearerApiKeyGuard`. No request signing — the bearer key is the credential.
|
|
15
|
+
*/
|
|
16
|
+
interface SneekOptions {
|
|
17
|
+
/** Base URL of the Sneek API, e.g. https://api.sneek.in */
|
|
18
|
+
apiUrl: string;
|
|
19
|
+
/** Bearer API key: `snk_live_…` or `snk_test_…`. */
|
|
20
|
+
apiKey: string;
|
|
21
|
+
/** Override the global fetch (for tests / non-Node runtimes). */
|
|
22
|
+
fetch?: typeof fetch;
|
|
23
|
+
/** Max retry attempts for transient failures (5xx / network). Default 2. */
|
|
24
|
+
maxRetries?: number;
|
|
25
|
+
/** Base backoff in ms between retries (exponential). Default 200. */
|
|
26
|
+
retryBaseMs?: number;
|
|
27
|
+
}
|
|
28
|
+
type MessageChannel = 'sms' | 'whatsapp' | 'email';
|
|
29
|
+
interface SendMessageInput {
|
|
30
|
+
to: string;
|
|
31
|
+
/** Free-form message body. Required unless `template` is given. */
|
|
32
|
+
body?: string;
|
|
33
|
+
channel?: 'auto' | MessageChannel;
|
|
34
|
+
clientRef?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Declares intent so the right approved provider template is used:
|
|
37
|
+
* `otp` → auth/OTP template, `transactional` → utility/communication.
|
|
38
|
+
* When omitted, Sneek infers it from the body text.
|
|
39
|
+
*/
|
|
40
|
+
type?: 'otp' | 'transactional';
|
|
41
|
+
/** Named/DLT provider template key (server-rendered). */
|
|
42
|
+
template?: string;
|
|
43
|
+
/** Variables substituted into `template`. */
|
|
44
|
+
variables?: Record<string, string | number>;
|
|
45
|
+
/**
|
|
46
|
+
* Replay-protection key. Two sends with the same key (per application)
|
|
47
|
+
* deliver only once — the first result is returned for every retry. Pair
|
|
48
|
+
* with the SDK's automatic 5xx retries to make sends safely repeatable.
|
|
49
|
+
*/
|
|
50
|
+
idempotencyKey?: string;
|
|
51
|
+
}
|
|
52
|
+
interface SendMessageResult {
|
|
53
|
+
id: string;
|
|
54
|
+
status: 'accepted';
|
|
55
|
+
channel: MessageChannel;
|
|
56
|
+
provider: string;
|
|
57
|
+
accepted_at: string;
|
|
58
|
+
client_ref?: string;
|
|
59
|
+
}
|
|
60
|
+
interface SendOtpInput {
|
|
61
|
+
to: string;
|
|
62
|
+
code: string;
|
|
63
|
+
/** App/brand name shown inside the message. */
|
|
64
|
+
appName?: string;
|
|
65
|
+
channel?: 'auto' | MessageChannel;
|
|
66
|
+
clientRef?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Override the default body. Receives `{appName, otp}`. When omitted a
|
|
69
|
+
* sensible default is used. Ignored if `template` is provided.
|
|
70
|
+
*/
|
|
71
|
+
body?: (vars: {
|
|
72
|
+
appName: string;
|
|
73
|
+
otp: string;
|
|
74
|
+
}) => string;
|
|
75
|
+
/** DLT/provider template key, when the partner uses server-side rendering. */
|
|
76
|
+
template?: string;
|
|
77
|
+
/** Extra template variables (merged with `{appName, otp}`). */
|
|
78
|
+
variables?: Record<string, string | number>;
|
|
79
|
+
}
|
|
80
|
+
declare class SneekApiError extends Error {
|
|
81
|
+
status: number;
|
|
82
|
+
type: string;
|
|
83
|
+
title: string;
|
|
84
|
+
detail?: string | undefined;
|
|
85
|
+
constructor(status: number, type: string, title: string, detail?: string | undefined);
|
|
86
|
+
}
|
|
87
|
+
declare class Sneek {
|
|
88
|
+
private readonly apiUrl;
|
|
89
|
+
private readonly apiKey;
|
|
90
|
+
private readonly fetchImpl;
|
|
91
|
+
private readonly maxRetries;
|
|
92
|
+
private readonly retryBaseMs;
|
|
93
|
+
readonly messages: MessagesClient;
|
|
94
|
+
constructor(opts: SneekOptions);
|
|
95
|
+
/** @internal — dispatches a bearer-authenticated request with retries. */
|
|
96
|
+
request<T>(method: string, path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<T>;
|
|
97
|
+
private backoff;
|
|
98
|
+
/** Convenience: send an SMS. */
|
|
99
|
+
sendSMS(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
100
|
+
/** Convenience: send a WhatsApp message. */
|
|
101
|
+
sendWhatsApp(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
102
|
+
/** Convenience: send an email. */
|
|
103
|
+
sendEmail(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
104
|
+
/**
|
|
105
|
+
* OTP body-formatting helper. Sneek only *delivers*; the partner generates
|
|
106
|
+
* and verifies the code. This formats `{appName, otp}` into a body (or
|
|
107
|
+
* forwards a template) and sends it.
|
|
108
|
+
*/
|
|
109
|
+
sendOTP(input: SendOtpInput): Promise<SendMessageResult>;
|
|
110
|
+
}
|
|
111
|
+
declare class MessagesClient {
|
|
112
|
+
private readonly sneek;
|
|
113
|
+
constructor(sneek: Sneek);
|
|
114
|
+
send(input: SendMessageInput): Promise<SendMessageResult>;
|
|
115
|
+
get(id: string): Promise<SendMessageResult>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { type MessageChannel, type SendMessageInput, type SendMessageResult, type SendOtpInput, Sneek, SneekApiError, type SneekOptions, Sneek as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sneeksdk — Node.js SDK for the Sneek Send API.
|
|
3
|
+
*
|
|
4
|
+
* const sneek = new Sneek({
|
|
5
|
+
* apiUrl: 'https://api.sneek.in',
|
|
6
|
+
* apiKey: process.env.SNEEK_API_KEY!, // snk_live_… / snk_test_…
|
|
7
|
+
* });
|
|
8
|
+
*
|
|
9
|
+
* await sneek.messages.send({ to: '+91…', body: 'Your OTP is 123456' });
|
|
10
|
+
* await sneek.sendSMS('+91…', 'Your OTP is 123456');
|
|
11
|
+
* await sneek.sendOTP({ to: '+91…', code: '123456', appName: 'ConfHub' });
|
|
12
|
+
*
|
|
13
|
+
* Authenticates with `Authorization: Bearer snk_…` against the server's
|
|
14
|
+
* `BearerApiKeyGuard`. No request signing — the bearer key is the credential.
|
|
15
|
+
*/
|
|
16
|
+
interface SneekOptions {
|
|
17
|
+
/** Base URL of the Sneek API, e.g. https://api.sneek.in */
|
|
18
|
+
apiUrl: string;
|
|
19
|
+
/** Bearer API key: `snk_live_…` or `snk_test_…`. */
|
|
20
|
+
apiKey: string;
|
|
21
|
+
/** Override the global fetch (for tests / non-Node runtimes). */
|
|
22
|
+
fetch?: typeof fetch;
|
|
23
|
+
/** Max retry attempts for transient failures (5xx / network). Default 2. */
|
|
24
|
+
maxRetries?: number;
|
|
25
|
+
/** Base backoff in ms between retries (exponential). Default 200. */
|
|
26
|
+
retryBaseMs?: number;
|
|
27
|
+
}
|
|
28
|
+
type MessageChannel = 'sms' | 'whatsapp' | 'email';
|
|
29
|
+
interface SendMessageInput {
|
|
30
|
+
to: string;
|
|
31
|
+
/** Free-form message body. Required unless `template` is given. */
|
|
32
|
+
body?: string;
|
|
33
|
+
channel?: 'auto' | MessageChannel;
|
|
34
|
+
clientRef?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Declares intent so the right approved provider template is used:
|
|
37
|
+
* `otp` → auth/OTP template, `transactional` → utility/communication.
|
|
38
|
+
* When omitted, Sneek infers it from the body text.
|
|
39
|
+
*/
|
|
40
|
+
type?: 'otp' | 'transactional';
|
|
41
|
+
/** Named/DLT provider template key (server-rendered). */
|
|
42
|
+
template?: string;
|
|
43
|
+
/** Variables substituted into `template`. */
|
|
44
|
+
variables?: Record<string, string | number>;
|
|
45
|
+
/**
|
|
46
|
+
* Replay-protection key. Two sends with the same key (per application)
|
|
47
|
+
* deliver only once — the first result is returned for every retry. Pair
|
|
48
|
+
* with the SDK's automatic 5xx retries to make sends safely repeatable.
|
|
49
|
+
*/
|
|
50
|
+
idempotencyKey?: string;
|
|
51
|
+
}
|
|
52
|
+
interface SendMessageResult {
|
|
53
|
+
id: string;
|
|
54
|
+
status: 'accepted';
|
|
55
|
+
channel: MessageChannel;
|
|
56
|
+
provider: string;
|
|
57
|
+
accepted_at: string;
|
|
58
|
+
client_ref?: string;
|
|
59
|
+
}
|
|
60
|
+
interface SendOtpInput {
|
|
61
|
+
to: string;
|
|
62
|
+
code: string;
|
|
63
|
+
/** App/brand name shown inside the message. */
|
|
64
|
+
appName?: string;
|
|
65
|
+
channel?: 'auto' | MessageChannel;
|
|
66
|
+
clientRef?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Override the default body. Receives `{appName, otp}`. When omitted a
|
|
69
|
+
* sensible default is used. Ignored if `template` is provided.
|
|
70
|
+
*/
|
|
71
|
+
body?: (vars: {
|
|
72
|
+
appName: string;
|
|
73
|
+
otp: string;
|
|
74
|
+
}) => string;
|
|
75
|
+
/** DLT/provider template key, when the partner uses server-side rendering. */
|
|
76
|
+
template?: string;
|
|
77
|
+
/** Extra template variables (merged with `{appName, otp}`). */
|
|
78
|
+
variables?: Record<string, string | number>;
|
|
79
|
+
}
|
|
80
|
+
declare class SneekApiError extends Error {
|
|
81
|
+
status: number;
|
|
82
|
+
type: string;
|
|
83
|
+
title: string;
|
|
84
|
+
detail?: string | undefined;
|
|
85
|
+
constructor(status: number, type: string, title: string, detail?: string | undefined);
|
|
86
|
+
}
|
|
87
|
+
declare class Sneek {
|
|
88
|
+
private readonly apiUrl;
|
|
89
|
+
private readonly apiKey;
|
|
90
|
+
private readonly fetchImpl;
|
|
91
|
+
private readonly maxRetries;
|
|
92
|
+
private readonly retryBaseMs;
|
|
93
|
+
readonly messages: MessagesClient;
|
|
94
|
+
constructor(opts: SneekOptions);
|
|
95
|
+
/** @internal — dispatches a bearer-authenticated request with retries. */
|
|
96
|
+
request<T>(method: string, path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<T>;
|
|
97
|
+
private backoff;
|
|
98
|
+
/** Convenience: send an SMS. */
|
|
99
|
+
sendSMS(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
100
|
+
/** Convenience: send a WhatsApp message. */
|
|
101
|
+
sendWhatsApp(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
102
|
+
/** Convenience: send an email. */
|
|
103
|
+
sendEmail(to: string, body: string, clientRef?: string): Promise<SendMessageResult>;
|
|
104
|
+
/**
|
|
105
|
+
* OTP body-formatting helper. Sneek only *delivers*; the partner generates
|
|
106
|
+
* and verifies the code. This formats `{appName, otp}` into a body (or
|
|
107
|
+
* forwards a template) and sends it.
|
|
108
|
+
*/
|
|
109
|
+
sendOTP(input: SendOtpInput): Promise<SendMessageResult>;
|
|
110
|
+
}
|
|
111
|
+
declare class MessagesClient {
|
|
112
|
+
private readonly sneek;
|
|
113
|
+
constructor(sneek: Sneek);
|
|
114
|
+
send(input: SendMessageInput): Promise<SendMessageResult>;
|
|
115
|
+
get(id: string): Promise<SendMessageResult>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { type MessageChannel, type SendMessageInput, type SendMessageResult, type SendOtpInput, Sneek, SneekApiError, type SneekOptions, Sneek as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Sneek: () => Sneek,
|
|
24
|
+
SneekApiError: () => SneekApiError,
|
|
25
|
+
default: () => index_default
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var SneekApiError = class extends Error {
|
|
29
|
+
constructor(status, type, title, detail) {
|
|
30
|
+
super(`${title}${detail ? `: ${detail}` : ""} [${status}]`);
|
|
31
|
+
this.status = status;
|
|
32
|
+
this.type = type;
|
|
33
|
+
this.title = title;
|
|
34
|
+
this.detail = detail;
|
|
35
|
+
this.name = "SneekApiError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
39
|
+
var DEFAULT_RETRY_BASE_MS = 200;
|
|
40
|
+
var Sneek = class {
|
|
41
|
+
apiUrl;
|
|
42
|
+
apiKey;
|
|
43
|
+
fetchImpl;
|
|
44
|
+
maxRetries;
|
|
45
|
+
retryBaseMs;
|
|
46
|
+
messages;
|
|
47
|
+
constructor(opts) {
|
|
48
|
+
if (!opts.apiUrl) throw new Error("apiUrl is required");
|
|
49
|
+
if (!opts.apiKey) throw new Error("apiKey is required");
|
|
50
|
+
this.apiUrl = opts.apiUrl.replace(/\/+$/, "");
|
|
51
|
+
this.apiKey = opts.apiKey;
|
|
52
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
53
|
+
this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
54
|
+
this.retryBaseMs = opts.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
55
|
+
this.messages = new MessagesClient(this);
|
|
56
|
+
}
|
|
57
|
+
/** @internal — dispatches a bearer-authenticated request with retries. */
|
|
58
|
+
async request(method, path, body, extraHeaders) {
|
|
59
|
+
const raw = body === void 0 ? void 0 : JSON.stringify(body);
|
|
60
|
+
let attempt = 0;
|
|
61
|
+
let lastErr;
|
|
62
|
+
for (; ; ) {
|
|
63
|
+
try {
|
|
64
|
+
const res = await this.fetchImpl(`${this.apiUrl}${path}`, {
|
|
65
|
+
method,
|
|
66
|
+
headers: {
|
|
67
|
+
"content-type": "application/json",
|
|
68
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
69
|
+
...extraHeaders ?? {}
|
|
70
|
+
},
|
|
71
|
+
body: raw
|
|
72
|
+
});
|
|
73
|
+
const text = await res.text();
|
|
74
|
+
const parsed = text ? safeJson(text) : void 0;
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
const p = parsed ?? {};
|
|
77
|
+
const err = new SneekApiError(
|
|
78
|
+
res.status,
|
|
79
|
+
p.type ?? "about:blank",
|
|
80
|
+
p.title ?? res.statusText,
|
|
81
|
+
p.detail
|
|
82
|
+
);
|
|
83
|
+
if (res.status >= 500 && attempt < this.maxRetries) {
|
|
84
|
+
lastErr = err;
|
|
85
|
+
await this.backoff(attempt++);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
return parsed;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
if (err instanceof SneekApiError) throw err;
|
|
93
|
+
if (attempt < this.maxRetries) {
|
|
94
|
+
lastErr = err;
|
|
95
|
+
await this.backoff(attempt++);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
throw lastErr ?? err;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
backoff(attempt) {
|
|
103
|
+
const ms = this.retryBaseMs * Math.pow(2, attempt);
|
|
104
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
105
|
+
}
|
|
106
|
+
/** Convenience: send an SMS. */
|
|
107
|
+
sendSMS(to, body, clientRef) {
|
|
108
|
+
return this.messages.send({ to, body, channel: "sms", clientRef });
|
|
109
|
+
}
|
|
110
|
+
/** Convenience: send a WhatsApp message. */
|
|
111
|
+
sendWhatsApp(to, body, clientRef) {
|
|
112
|
+
return this.messages.send({ to, body, channel: "whatsapp", clientRef });
|
|
113
|
+
}
|
|
114
|
+
/** Convenience: send an email. */
|
|
115
|
+
sendEmail(to, body, clientRef) {
|
|
116
|
+
return this.messages.send({ to, body, channel: "email", clientRef });
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* OTP body-formatting helper. Sneek only *delivers*; the partner generates
|
|
120
|
+
* and verifies the code. This formats `{appName, otp}` into a body (or
|
|
121
|
+
* forwards a template) and sends it.
|
|
122
|
+
*/
|
|
123
|
+
sendOTP(input) {
|
|
124
|
+
const appName = input.appName ?? "your account";
|
|
125
|
+
if (input.template) {
|
|
126
|
+
return this.messages.send({
|
|
127
|
+
to: input.to,
|
|
128
|
+
channel: input.channel ?? "auto",
|
|
129
|
+
clientRef: input.clientRef,
|
|
130
|
+
type: "otp",
|
|
131
|
+
template: input.template,
|
|
132
|
+
variables: {
|
|
133
|
+
appName,
|
|
134
|
+
otp: input.code,
|
|
135
|
+
...input.variables ?? {}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const body = input.body ? input.body({ appName, otp: input.code }) : `Your OTP to login to ${appName} is ${input.code}. Do not share it with anyone.`;
|
|
140
|
+
return this.messages.send({
|
|
141
|
+
to: input.to,
|
|
142
|
+
body,
|
|
143
|
+
channel: input.channel ?? "auto",
|
|
144
|
+
clientRef: input.clientRef,
|
|
145
|
+
type: "otp"
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var MessagesClient = class {
|
|
150
|
+
constructor(sneek) {
|
|
151
|
+
this.sneek = sneek;
|
|
152
|
+
}
|
|
153
|
+
async send(input) {
|
|
154
|
+
if (!input.body && !input.template) {
|
|
155
|
+
throw new Error("send() requires either `body` or `template`");
|
|
156
|
+
}
|
|
157
|
+
const payload = {
|
|
158
|
+
to: input.to,
|
|
159
|
+
channel: input.channel ?? "auto"
|
|
160
|
+
};
|
|
161
|
+
if (input.body !== void 0) payload.body = input.body;
|
|
162
|
+
if (input.type !== void 0) payload.type = input.type;
|
|
163
|
+
if (input.template !== void 0) payload.template = input.template;
|
|
164
|
+
if (input.variables !== void 0) payload.variables = input.variables;
|
|
165
|
+
if (input.clientRef) payload.client_ref = input.clientRef;
|
|
166
|
+
const headers = input.idempotencyKey ? { "idempotency-key": input.idempotencyKey } : void 0;
|
|
167
|
+
return this.sneek.request(
|
|
168
|
+
"POST",
|
|
169
|
+
"/api/messages/send",
|
|
170
|
+
payload,
|
|
171
|
+
headers
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
async get(id) {
|
|
175
|
+
return this.sneek.request(
|
|
176
|
+
"GET",
|
|
177
|
+
`/api/messages/${encodeURIComponent(id)}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
function safeJson(s) {
|
|
182
|
+
try {
|
|
183
|
+
return JSON.parse(s);
|
|
184
|
+
} catch {
|
|
185
|
+
return void 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var index_default = Sneek;
|
|
189
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
190
|
+
0 && (module.exports = {
|
|
191
|
+
Sneek,
|
|
192
|
+
SneekApiError
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * sneeksdk — Node.js SDK for the Sneek Send API.\n *\n * const sneek = new Sneek({\n * apiUrl: 'https://api.sneek.in',\n * apiKey: process.env.SNEEK_API_KEY!, // snk_live_… / snk_test_…\n * });\n *\n * await sneek.messages.send({ to: '+91…', body: 'Your OTP is 123456' });\n * await sneek.sendSMS('+91…', 'Your OTP is 123456');\n * await sneek.sendOTP({ to: '+91…', code: '123456', appName: 'ConfHub' });\n *\n * Authenticates with `Authorization: Bearer snk_…` against the server's\n * `BearerApiKeyGuard`. No request signing — the bearer key is the credential.\n */\n\nexport interface SneekOptions {\n /** Base URL of the Sneek API, e.g. https://api.sneek.in */\n apiUrl: string;\n /** Bearer API key: `snk_live_…` or `snk_test_…`. */\n apiKey: string;\n /** Override the global fetch (for tests / non-Node runtimes). */\n fetch?: typeof fetch;\n /** Max retry attempts for transient failures (5xx / network). Default 2. */\n maxRetries?: number;\n /** Base backoff in ms between retries (exponential). Default 200. */\n retryBaseMs?: number;\n}\n\nexport type MessageChannel = 'sms' | 'whatsapp' | 'email';\n\nexport interface SendMessageInput {\n to: string;\n /** Free-form message body. Required unless `template` is given. */\n body?: string;\n channel?: 'auto' | MessageChannel;\n clientRef?: string;\n /**\n * Declares intent so the right approved provider template is used:\n * `otp` → auth/OTP template, `transactional` → utility/communication.\n * When omitted, Sneek infers it from the body text.\n */\n type?: 'otp' | 'transactional';\n /** Named/DLT provider template key (server-rendered). */\n template?: string;\n /** Variables substituted into `template`. */\n variables?: Record<string, string | number>;\n /**\n * Replay-protection key. Two sends with the same key (per application)\n * deliver only once — the first result is returned for every retry. Pair\n * with the SDK's automatic 5xx retries to make sends safely repeatable.\n */\n idempotencyKey?: string;\n}\n\nexport interface SendMessageResult {\n id: string;\n status: 'accepted';\n channel: MessageChannel;\n provider: string;\n accepted_at: string;\n client_ref?: string;\n}\n\nexport interface SendOtpInput {\n to: string;\n code: string;\n /** App/brand name shown inside the message. */\n appName?: string;\n channel?: 'auto' | MessageChannel;\n clientRef?: string;\n /**\n * Override the default body. Receives `{appName, otp}`. When omitted a\n * sensible default is used. Ignored if `template` is provided.\n */\n body?: (vars: { appName: string; otp: string }) => string;\n /** DLT/provider template key, when the partner uses server-side rendering. */\n template?: string;\n /** Extra template variables (merged with `{appName, otp}`). */\n variables?: Record<string, string | number>;\n}\n\nexport class SneekApiError extends Error {\n constructor(\n public status: number,\n public type: string,\n public title: string,\n public detail?: string,\n ) {\n super(`${title}${detail ? `: ${detail}` : ''} [${status}]`);\n this.name = 'SneekApiError';\n }\n}\n\nconst DEFAULT_MAX_RETRIES = 2;\nconst DEFAULT_RETRY_BASE_MS = 200;\n\nexport class Sneek {\n private readonly apiUrl: string;\n private readonly apiKey: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n readonly messages: MessagesClient;\n\n constructor(opts: SneekOptions) {\n if (!opts.apiUrl) throw new Error('apiUrl is required');\n if (!opts.apiKey) throw new Error('apiKey is required');\n this.apiUrl = opts.apiUrl.replace(/\\/+$/, '');\n this.apiKey = opts.apiKey;\n this.fetchImpl = opts.fetch ?? fetch;\n this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseMs = opts.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;\n this.messages = new MessagesClient(this);\n }\n\n /** @internal — dispatches a bearer-authenticated request with retries. */\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const raw = body === undefined ? undefined : JSON.stringify(body);\n let attempt = 0;\n let lastErr: unknown;\n for (;;) {\n try {\n const res = await this.fetchImpl(`${this.apiUrl}${path}`, {\n method,\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n ...(extraHeaders ?? {}),\n },\n body: raw,\n });\n const text = await res.text();\n const parsed: unknown = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const p = (parsed ?? {}) as {\n type?: string;\n title?: string;\n detail?: string;\n };\n const err = new SneekApiError(\n res.status,\n p.type ?? 'about:blank',\n p.title ?? res.statusText,\n p.detail,\n );\n if (res.status >= 500 && attempt < this.maxRetries) {\n lastErr = err;\n await this.backoff(attempt++);\n continue;\n }\n throw err;\n }\n return parsed as T;\n } catch (err) {\n if (err instanceof SneekApiError) throw err;\n if (attempt < this.maxRetries) {\n lastErr = err;\n await this.backoff(attempt++);\n continue;\n }\n throw lastErr ?? err;\n }\n }\n }\n\n private backoff(attempt: number): Promise<void> {\n const ms = this.retryBaseMs * Math.pow(2, attempt);\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /** Convenience: send an SMS. */\n sendSMS(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'sms', clientRef });\n }\n\n /** Convenience: send a WhatsApp message. */\n sendWhatsApp(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'whatsapp', clientRef });\n }\n\n /** Convenience: send an email. */\n sendEmail(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'email', clientRef });\n }\n\n /**\n * OTP body-formatting helper. Sneek only *delivers*; the partner generates\n * and verifies the code. This formats `{appName, otp}` into a body (or\n * forwards a template) and sends it.\n */\n sendOTP(input: SendOtpInput): Promise<SendMessageResult> {\n const appName = input.appName ?? 'your account';\n if (input.template) {\n return this.messages.send({\n to: input.to,\n channel: input.channel ?? 'auto',\n clientRef: input.clientRef,\n type: 'otp',\n template: input.template,\n variables: {\n appName,\n otp: input.code,\n ...(input.variables ?? {}),\n },\n });\n }\n const body = input.body\n ? input.body({ appName, otp: input.code })\n : `Your OTP to login to ${appName} is ${input.code}. Do not share it with anyone.`;\n return this.messages.send({\n to: input.to,\n body,\n channel: input.channel ?? 'auto',\n clientRef: input.clientRef,\n type: 'otp',\n });\n }\n}\n\nclass MessagesClient {\n constructor(private readonly sneek: Sneek) {}\n\n async send(input: SendMessageInput): Promise<SendMessageResult> {\n if (!input.body && !input.template) {\n throw new Error('send() requires either `body` or `template`');\n }\n const payload: Record<string, unknown> = {\n to: input.to,\n channel: input.channel ?? 'auto',\n };\n if (input.body !== undefined) payload.body = input.body;\n if (input.type !== undefined) payload.type = input.type;\n if (input.template !== undefined) payload.template = input.template;\n if (input.variables !== undefined) payload.variables = input.variables;\n if (input.clientRef) payload.client_ref = input.clientRef;\n const headers = input.idempotencyKey\n ? { 'idempotency-key': input.idempotencyKey }\n : undefined;\n return this.sneek.request<SendMessageResult>(\n 'POST',\n '/api/messages/send',\n payload,\n headers,\n );\n }\n\n async get(id: string): Promise<SendMessageResult> {\n return this.sneek.request<SendMessageResult>(\n 'GET',\n `/api/messages/${encodeURIComponent(id)}`,\n );\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\nexport default Sneek;\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var SneekApiError = class extends Error {
|
|
3
|
+
constructor(status, type, title, detail) {
|
|
4
|
+
super(`${title}${detail ? `: ${detail}` : ""} [${status}]`);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.type = type;
|
|
7
|
+
this.title = title;
|
|
8
|
+
this.detail = detail;
|
|
9
|
+
this.name = "SneekApiError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
13
|
+
var DEFAULT_RETRY_BASE_MS = 200;
|
|
14
|
+
var Sneek = class {
|
|
15
|
+
apiUrl;
|
|
16
|
+
apiKey;
|
|
17
|
+
fetchImpl;
|
|
18
|
+
maxRetries;
|
|
19
|
+
retryBaseMs;
|
|
20
|
+
messages;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
if (!opts.apiUrl) throw new Error("apiUrl is required");
|
|
23
|
+
if (!opts.apiKey) throw new Error("apiKey is required");
|
|
24
|
+
this.apiUrl = opts.apiUrl.replace(/\/+$/, "");
|
|
25
|
+
this.apiKey = opts.apiKey;
|
|
26
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
27
|
+
this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
28
|
+
this.retryBaseMs = opts.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
29
|
+
this.messages = new MessagesClient(this);
|
|
30
|
+
}
|
|
31
|
+
/** @internal — dispatches a bearer-authenticated request with retries. */
|
|
32
|
+
async request(method, path, body, extraHeaders) {
|
|
33
|
+
const raw = body === void 0 ? void 0 : JSON.stringify(body);
|
|
34
|
+
let attempt = 0;
|
|
35
|
+
let lastErr;
|
|
36
|
+
for (; ; ) {
|
|
37
|
+
try {
|
|
38
|
+
const res = await this.fetchImpl(`${this.apiUrl}${path}`, {
|
|
39
|
+
method,
|
|
40
|
+
headers: {
|
|
41
|
+
"content-type": "application/json",
|
|
42
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
43
|
+
...extraHeaders ?? {}
|
|
44
|
+
},
|
|
45
|
+
body: raw
|
|
46
|
+
});
|
|
47
|
+
const text = await res.text();
|
|
48
|
+
const parsed = text ? safeJson(text) : void 0;
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const p = parsed ?? {};
|
|
51
|
+
const err = new SneekApiError(
|
|
52
|
+
res.status,
|
|
53
|
+
p.type ?? "about:blank",
|
|
54
|
+
p.title ?? res.statusText,
|
|
55
|
+
p.detail
|
|
56
|
+
);
|
|
57
|
+
if (res.status >= 500 && attempt < this.maxRetries) {
|
|
58
|
+
lastErr = err;
|
|
59
|
+
await this.backoff(attempt++);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
return parsed;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (err instanceof SneekApiError) throw err;
|
|
67
|
+
if (attempt < this.maxRetries) {
|
|
68
|
+
lastErr = err;
|
|
69
|
+
await this.backoff(attempt++);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
throw lastErr ?? err;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
backoff(attempt) {
|
|
77
|
+
const ms = this.retryBaseMs * Math.pow(2, attempt);
|
|
78
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
/** Convenience: send an SMS. */
|
|
81
|
+
sendSMS(to, body, clientRef) {
|
|
82
|
+
return this.messages.send({ to, body, channel: "sms", clientRef });
|
|
83
|
+
}
|
|
84
|
+
/** Convenience: send a WhatsApp message. */
|
|
85
|
+
sendWhatsApp(to, body, clientRef) {
|
|
86
|
+
return this.messages.send({ to, body, channel: "whatsapp", clientRef });
|
|
87
|
+
}
|
|
88
|
+
/** Convenience: send an email. */
|
|
89
|
+
sendEmail(to, body, clientRef) {
|
|
90
|
+
return this.messages.send({ to, body, channel: "email", clientRef });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* OTP body-formatting helper. Sneek only *delivers*; the partner generates
|
|
94
|
+
* and verifies the code. This formats `{appName, otp}` into a body (or
|
|
95
|
+
* forwards a template) and sends it.
|
|
96
|
+
*/
|
|
97
|
+
sendOTP(input) {
|
|
98
|
+
const appName = input.appName ?? "your account";
|
|
99
|
+
if (input.template) {
|
|
100
|
+
return this.messages.send({
|
|
101
|
+
to: input.to,
|
|
102
|
+
channel: input.channel ?? "auto",
|
|
103
|
+
clientRef: input.clientRef,
|
|
104
|
+
type: "otp",
|
|
105
|
+
template: input.template,
|
|
106
|
+
variables: {
|
|
107
|
+
appName,
|
|
108
|
+
otp: input.code,
|
|
109
|
+
...input.variables ?? {}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const body = input.body ? input.body({ appName, otp: input.code }) : `Your OTP to login to ${appName} is ${input.code}. Do not share it with anyone.`;
|
|
114
|
+
return this.messages.send({
|
|
115
|
+
to: input.to,
|
|
116
|
+
body,
|
|
117
|
+
channel: input.channel ?? "auto",
|
|
118
|
+
clientRef: input.clientRef,
|
|
119
|
+
type: "otp"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var MessagesClient = class {
|
|
124
|
+
constructor(sneek) {
|
|
125
|
+
this.sneek = sneek;
|
|
126
|
+
}
|
|
127
|
+
async send(input) {
|
|
128
|
+
if (!input.body && !input.template) {
|
|
129
|
+
throw new Error("send() requires either `body` or `template`");
|
|
130
|
+
}
|
|
131
|
+
const payload = {
|
|
132
|
+
to: input.to,
|
|
133
|
+
channel: input.channel ?? "auto"
|
|
134
|
+
};
|
|
135
|
+
if (input.body !== void 0) payload.body = input.body;
|
|
136
|
+
if (input.type !== void 0) payload.type = input.type;
|
|
137
|
+
if (input.template !== void 0) payload.template = input.template;
|
|
138
|
+
if (input.variables !== void 0) payload.variables = input.variables;
|
|
139
|
+
if (input.clientRef) payload.client_ref = input.clientRef;
|
|
140
|
+
const headers = input.idempotencyKey ? { "idempotency-key": input.idempotencyKey } : void 0;
|
|
141
|
+
return this.sneek.request(
|
|
142
|
+
"POST",
|
|
143
|
+
"/api/messages/send",
|
|
144
|
+
payload,
|
|
145
|
+
headers
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
async get(id) {
|
|
149
|
+
return this.sneek.request(
|
|
150
|
+
"GET",
|
|
151
|
+
`/api/messages/${encodeURIComponent(id)}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function safeJson(s) {
|
|
156
|
+
try {
|
|
157
|
+
return JSON.parse(s);
|
|
158
|
+
} catch {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
var index_default = Sneek;
|
|
163
|
+
export {
|
|
164
|
+
Sneek,
|
|
165
|
+
SneekApiError,
|
|
166
|
+
index_default as default
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * sneeksdk — Node.js SDK for the Sneek Send API.\n *\n * const sneek = new Sneek({\n * apiUrl: 'https://api.sneek.in',\n * apiKey: process.env.SNEEK_API_KEY!, // snk_live_… / snk_test_…\n * });\n *\n * await sneek.messages.send({ to: '+91…', body: 'Your OTP is 123456' });\n * await sneek.sendSMS('+91…', 'Your OTP is 123456');\n * await sneek.sendOTP({ to: '+91…', code: '123456', appName: 'ConfHub' });\n *\n * Authenticates with `Authorization: Bearer snk_…` against the server's\n * `BearerApiKeyGuard`. No request signing — the bearer key is the credential.\n */\n\nexport interface SneekOptions {\n /** Base URL of the Sneek API, e.g. https://api.sneek.in */\n apiUrl: string;\n /** Bearer API key: `snk_live_…` or `snk_test_…`. */\n apiKey: string;\n /** Override the global fetch (for tests / non-Node runtimes). */\n fetch?: typeof fetch;\n /** Max retry attempts for transient failures (5xx / network). Default 2. */\n maxRetries?: number;\n /** Base backoff in ms between retries (exponential). Default 200. */\n retryBaseMs?: number;\n}\n\nexport type MessageChannel = 'sms' | 'whatsapp' | 'email';\n\nexport interface SendMessageInput {\n to: string;\n /** Free-form message body. Required unless `template` is given. */\n body?: string;\n channel?: 'auto' | MessageChannel;\n clientRef?: string;\n /**\n * Declares intent so the right approved provider template is used:\n * `otp` → auth/OTP template, `transactional` → utility/communication.\n * When omitted, Sneek infers it from the body text.\n */\n type?: 'otp' | 'transactional';\n /** Named/DLT provider template key (server-rendered). */\n template?: string;\n /** Variables substituted into `template`. */\n variables?: Record<string, string | number>;\n /**\n * Replay-protection key. Two sends with the same key (per application)\n * deliver only once — the first result is returned for every retry. Pair\n * with the SDK's automatic 5xx retries to make sends safely repeatable.\n */\n idempotencyKey?: string;\n}\n\nexport interface SendMessageResult {\n id: string;\n status: 'accepted';\n channel: MessageChannel;\n provider: string;\n accepted_at: string;\n client_ref?: string;\n}\n\nexport interface SendOtpInput {\n to: string;\n code: string;\n /** App/brand name shown inside the message. */\n appName?: string;\n channel?: 'auto' | MessageChannel;\n clientRef?: string;\n /**\n * Override the default body. Receives `{appName, otp}`. When omitted a\n * sensible default is used. Ignored if `template` is provided.\n */\n body?: (vars: { appName: string; otp: string }) => string;\n /** DLT/provider template key, when the partner uses server-side rendering. */\n template?: string;\n /** Extra template variables (merged with `{appName, otp}`). */\n variables?: Record<string, string | number>;\n}\n\nexport class SneekApiError extends Error {\n constructor(\n public status: number,\n public type: string,\n public title: string,\n public detail?: string,\n ) {\n super(`${title}${detail ? `: ${detail}` : ''} [${status}]`);\n this.name = 'SneekApiError';\n }\n}\n\nconst DEFAULT_MAX_RETRIES = 2;\nconst DEFAULT_RETRY_BASE_MS = 200;\n\nexport class Sneek {\n private readonly apiUrl: string;\n private readonly apiKey: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n\n readonly messages: MessagesClient;\n\n constructor(opts: SneekOptions) {\n if (!opts.apiUrl) throw new Error('apiUrl is required');\n if (!opts.apiKey) throw new Error('apiKey is required');\n this.apiUrl = opts.apiUrl.replace(/\\/+$/, '');\n this.apiKey = opts.apiKey;\n this.fetchImpl = opts.fetch ?? fetch;\n this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseMs = opts.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;\n this.messages = new MessagesClient(this);\n }\n\n /** @internal — dispatches a bearer-authenticated request with retries. */\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const raw = body === undefined ? undefined : JSON.stringify(body);\n let attempt = 0;\n let lastErr: unknown;\n for (;;) {\n try {\n const res = await this.fetchImpl(`${this.apiUrl}${path}`, {\n method,\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.apiKey}`,\n ...(extraHeaders ?? {}),\n },\n body: raw,\n });\n const text = await res.text();\n const parsed: unknown = text ? safeJson(text) : undefined;\n if (!res.ok) {\n const p = (parsed ?? {}) as {\n type?: string;\n title?: string;\n detail?: string;\n };\n const err = new SneekApiError(\n res.status,\n p.type ?? 'about:blank',\n p.title ?? res.statusText,\n p.detail,\n );\n if (res.status >= 500 && attempt < this.maxRetries) {\n lastErr = err;\n await this.backoff(attempt++);\n continue;\n }\n throw err;\n }\n return parsed as T;\n } catch (err) {\n if (err instanceof SneekApiError) throw err;\n if (attempt < this.maxRetries) {\n lastErr = err;\n await this.backoff(attempt++);\n continue;\n }\n throw lastErr ?? err;\n }\n }\n }\n\n private backoff(attempt: number): Promise<void> {\n const ms = this.retryBaseMs * Math.pow(2, attempt);\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /** Convenience: send an SMS. */\n sendSMS(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'sms', clientRef });\n }\n\n /** Convenience: send a WhatsApp message. */\n sendWhatsApp(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'whatsapp', clientRef });\n }\n\n /** Convenience: send an email. */\n sendEmail(to: string, body: string, clientRef?: string) {\n return this.messages.send({ to, body, channel: 'email', clientRef });\n }\n\n /**\n * OTP body-formatting helper. Sneek only *delivers*; the partner generates\n * and verifies the code. This formats `{appName, otp}` into a body (or\n * forwards a template) and sends it.\n */\n sendOTP(input: SendOtpInput): Promise<SendMessageResult> {\n const appName = input.appName ?? 'your account';\n if (input.template) {\n return this.messages.send({\n to: input.to,\n channel: input.channel ?? 'auto',\n clientRef: input.clientRef,\n type: 'otp',\n template: input.template,\n variables: {\n appName,\n otp: input.code,\n ...(input.variables ?? {}),\n },\n });\n }\n const body = input.body\n ? input.body({ appName, otp: input.code })\n : `Your OTP to login to ${appName} is ${input.code}. Do not share it with anyone.`;\n return this.messages.send({\n to: input.to,\n body,\n channel: input.channel ?? 'auto',\n clientRef: input.clientRef,\n type: 'otp',\n });\n }\n}\n\nclass MessagesClient {\n constructor(private readonly sneek: Sneek) {}\n\n async send(input: SendMessageInput): Promise<SendMessageResult> {\n if (!input.body && !input.template) {\n throw new Error('send() requires either `body` or `template`');\n }\n const payload: Record<string, unknown> = {\n to: input.to,\n channel: input.channel ?? 'auto',\n };\n if (input.body !== undefined) payload.body = input.body;\n if (input.type !== undefined) payload.type = input.type;\n if (input.template !== undefined) payload.template = input.template;\n if (input.variables !== undefined) payload.variables = input.variables;\n if (input.clientRef) payload.client_ref = input.clientRef;\n const headers = input.idempotencyKey\n ? { 'idempotency-key': input.idempotencyKey }\n : undefined;\n return this.sneek.request<SendMessageResult>(\n 'POST',\n '/api/messages/send',\n payload,\n headers,\n );\n }\n\n async get(id: string): Promise<SendMessageResult> {\n return this.sneek.request<SendMessageResult>(\n 'GET',\n `/api/messages/${encodeURIComponent(id)}`,\n );\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\nexport default Sneek;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sneeksdk",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Node.js SDK for the Sneek Messaging API",
|
|
8
|
+
"homepage": "https://sneek.in/docs",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://sneek.in/docs"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"module": "dist/index.mjs",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.mjs",
|
|
19
|
+
"require": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"type-check": "tsc --noEmit",
|
|
28
|
+
"test": "node --test --import tsx test/*.test.ts"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"sneek",
|
|
32
|
+
"messaging",
|
|
33
|
+
"sms",
|
|
34
|
+
"whatsapp",
|
|
35
|
+
"email",
|
|
36
|
+
"otp"
|
|
37
|
+
],
|
|
38
|
+
"author": "Abblor Tech Pvt Ltd <abblorltd@gmail.com> (https://sneek.in)",
|
|
39
|
+
"license": "UNLICENSED",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.11.0",
|
|
42
|
+
"tsup": "^8.0.0",
|
|
43
|
+
"tsx": "^4.7.0",
|
|
44
|
+
"typescript": "^5.3.3"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
}
|
|
49
|
+
}
|