@sneekin/sdk 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/client.d.ts +24 -0
- package/dist/core/derive-keys.d.ts +6 -0
- package/dist/core/generated/api.d.ts +3490 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/index.cjs +223 -0
- package/dist/index.d.ts +2 -118
- package/dist/index.js +127 -129
- package/dist/messages/index.cjs +150 -0
- package/dist/messages/index.d.ts +1 -0
- package/dist/messages/index.js +121 -0
- package/dist/pay/index.cjs +223 -0
- package/dist/pay/index.d.ts +2 -0
- package/dist/pay/index.js +192 -0
- package/dist/pay/payments.d.ts +90 -0
- package/dist/verify/index.cjs +150 -0
- package/dist/verify/index.d.ts +1 -0
- package/dist/verify/index.js +121 -0
- package/package.json +67 -47
- package/README.md +0 -134
- package/dist/index.d.mts +0 -118
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -168
- package/dist/index.mjs.map +0 -1
package/dist/index.d.mts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @sneekin/sdk — 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.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @sneekin/sdk — 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
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
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
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @sneekin/sdk — 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":[]}
|