uz-sms-glmv 0.1.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/index.cjs ADDED
@@ -0,0 +1,480 @@
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
+ SmsClient: () => SmsClient,
24
+ SmsConfigError: () => SmsConfigError,
25
+ SmsError: () => SmsError,
26
+ SmsProviderError: () => SmsProviderError,
27
+ createSmsClient: () => createSmsClient,
28
+ normalizePhone: () => normalizePhone
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/errors.ts
33
+ var SmsError = class extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = "SmsError";
37
+ }
38
+ };
39
+ var SmsConfigError = class extends SmsError {
40
+ constructor(message) {
41
+ super(message);
42
+ this.name = "SmsConfigError";
43
+ }
44
+ };
45
+ var SmsProviderError = class extends SmsError {
46
+ /** Provider that produced the error. */
47
+ provider;
48
+ /** HTTP status, when the failure originated from an HTTP response. */
49
+ status;
50
+ /** Raw provider payload, for debugging. */
51
+ raw;
52
+ constructor(provider, message, opts = {}) {
53
+ super(`[${provider}] ${message}`);
54
+ this.name = "SmsProviderError";
55
+ this.provider = provider;
56
+ this.status = opts.status;
57
+ this.raw = opts.raw;
58
+ }
59
+ };
60
+
61
+ // src/internal/http.ts
62
+ async function httpFetch(provider, url, init, timeoutMs) {
63
+ const controller = new AbortController();
64
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
65
+ try {
66
+ return await fetch(url, { ...init, signal: controller.signal });
67
+ } catch (err) {
68
+ if (err instanceof Error && err.name === "AbortError") {
69
+ throw new SmsProviderError(provider, `Request timed out after ${timeoutMs}ms`);
70
+ }
71
+ throw new SmsProviderError(
72
+ provider,
73
+ `Network error: ${err instanceof Error ? err.message : String(err)}`,
74
+ { raw: err }
75
+ );
76
+ } finally {
77
+ clearTimeout(timer);
78
+ }
79
+ }
80
+ async function parseBody(res) {
81
+ const text = await res.text();
82
+ if (!text) return null;
83
+ try {
84
+ return JSON.parse(text);
85
+ } catch {
86
+ return text;
87
+ }
88
+ }
89
+
90
+ // src/internal/phone.ts
91
+ function normalizePhone(input) {
92
+ const digits = input.replace(/\D+/g, "");
93
+ let national;
94
+ if (digits.length === 12 && digits.startsWith("998")) {
95
+ national = digits.slice(3);
96
+ } else if (digits.length === 9) {
97
+ national = digits;
98
+ } else {
99
+ throw new SmsError(
100
+ `Invalid Uzbek phone number: "${input}" (expected 998XXXXXXXXX or 9-digit national form)`
101
+ );
102
+ }
103
+ return `998${national}`;
104
+ }
105
+
106
+ // src/providers/eskiz.ts
107
+ var DEFAULT_BASE_URL = "https://notify.eskiz.uz/api";
108
+ var EskizProvider = class {
109
+ constructor(config, timeoutMs) {
110
+ this.config = config;
111
+ this.timeoutMs = timeoutMs;
112
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
113
+ }
114
+ config;
115
+ timeoutMs;
116
+ name = "eskiz";
117
+ baseUrl;
118
+ token;
119
+ async send(params) {
120
+ const body = new URLSearchParams({
121
+ mobile_phone: normalizePhone(params.to),
122
+ message: params.text,
123
+ from: params.from ?? this.config.from ?? "4546"
124
+ });
125
+ if (params.callbackUrl) body.set("callback_url", params.callbackUrl);
126
+ const raw = await this.request("/message/sms/send", body);
127
+ const data = raw;
128
+ return {
129
+ provider: this.name,
130
+ messageId: data.id != null ? String(data.id) : void 0,
131
+ status: data.status === "waiting" ? "queued" : "sent",
132
+ raw
133
+ };
134
+ }
135
+ async getBalance() {
136
+ const token = await this.auth();
137
+ const res = await httpFetch(
138
+ this.name,
139
+ `${this.baseUrl}/user/get-limit`,
140
+ { method: "GET", headers: { Authorization: `Bearer ${token}` } },
141
+ this.timeoutMs
142
+ );
143
+ const raw = await parseBody(res);
144
+ if (!res.ok) {
145
+ throw new SmsProviderError(this.name, "Failed to fetch balance", {
146
+ status: res.status,
147
+ raw
148
+ });
149
+ }
150
+ const balance = raw.data?.balance;
151
+ return typeof balance === "number" ? balance : Number(balance ?? 0);
152
+ }
153
+ /** POST a form body to an authenticated endpoint, retrying once on a 401. */
154
+ async request(path, body) {
155
+ for (let attempt = 0; attempt < 2; attempt++) {
156
+ const token = await this.auth();
157
+ const res = await httpFetch(
158
+ this.name,
159
+ `${this.baseUrl}${path}`,
160
+ {
161
+ method: "POST",
162
+ headers: { Authorization: `Bearer ${token}` },
163
+ body
164
+ },
165
+ this.timeoutMs
166
+ );
167
+ const raw = await parseBody(res);
168
+ if (res.status === 401 && attempt === 0) {
169
+ this.token = void 0;
170
+ continue;
171
+ }
172
+ if (!res.ok) {
173
+ throw new SmsProviderError(this.name, this.describe(raw), {
174
+ status: res.status,
175
+ raw
176
+ });
177
+ }
178
+ return raw;
179
+ }
180
+ throw new SmsProviderError(this.name, "Request failed after token refresh");
181
+ }
182
+ /** Return a cached bearer token, minting a fresh one if needed. */
183
+ async auth() {
184
+ if (this.token) return this.token;
185
+ const body = new URLSearchParams({
186
+ email: this.config.email,
187
+ password: this.config.password
188
+ });
189
+ const res = await httpFetch(
190
+ this.name,
191
+ `${this.baseUrl}/auth/login`,
192
+ { method: "POST", body },
193
+ this.timeoutMs
194
+ );
195
+ const raw = await parseBody(res);
196
+ const token = raw.data?.token;
197
+ if (!res.ok || !token) {
198
+ throw new SmsProviderError(this.name, "Authentication failed", {
199
+ status: res.status,
200
+ raw
201
+ });
202
+ }
203
+ this.token = token;
204
+ return token;
205
+ }
206
+ describe(raw) {
207
+ if (raw && typeof raw === "object" && "message" in raw) {
208
+ return String(raw.message);
209
+ }
210
+ return "Provider rejected the request";
211
+ }
212
+ };
213
+
214
+ // src/providers/octotelecom.ts
215
+ var DEFAULT_BASE_URL2 = "https://api.octotelecom.uz";
216
+ var OctotelecomProvider = class {
217
+ constructor(config, timeoutMs) {
218
+ this.config = config;
219
+ this.timeoutMs = timeoutMs;
220
+ const base = (config.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
221
+ this.endpoint = `${base}/${config.clientId}/json2/simple`;
222
+ const creds = `${config.username}:${config.password}`;
223
+ this.authHeader = `Basic ${Buffer.from(creds).toString("base64")}`;
224
+ }
225
+ config;
226
+ timeoutMs;
227
+ name = "octotelecom";
228
+ endpoint;
229
+ authHeader;
230
+ async send(params) {
231
+ const extraId = `uz-sms-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
232
+ const callbackUrl = params.callbackUrl ?? this.config.callbackUrl;
233
+ const payload = {
234
+ // Octotelecom expects the number without a leading "+".
235
+ phone_number: normalizePhone(params.to),
236
+ extra_id: extraId,
237
+ tag: this.config.tag ?? "uz-sms-glmv",
238
+ channels: ["sms"],
239
+ channel_options: {
240
+ sms: {
241
+ text: params.text,
242
+ alpha_name: params.from ?? this.config.from,
243
+ ttl: this.config.ttl ?? 300
244
+ }
245
+ }
246
+ };
247
+ if (callbackUrl) payload.callback_url = callbackUrl;
248
+ const res = await httpFetch(
249
+ this.name,
250
+ this.endpoint,
251
+ {
252
+ method: "POST",
253
+ headers: {
254
+ Authorization: this.authHeader,
255
+ "Content-Type": "application/json"
256
+ },
257
+ body: JSON.stringify(payload)
258
+ },
259
+ this.timeoutMs
260
+ );
261
+ const raw = await parseBody(res);
262
+ const data = raw;
263
+ if (!res.ok || data?.error_text) {
264
+ throw new SmsProviderError(
265
+ this.name,
266
+ data?.error_text ?? "Provider rejected the request",
267
+ { status: res.status, raw }
268
+ );
269
+ }
270
+ return {
271
+ provider: this.name,
272
+ messageId: data?.message_id != null ? String(data.message_id) : extraId,
273
+ status: "sent",
274
+ raw
275
+ };
276
+ }
277
+ };
278
+
279
+ // src/providers/playmobile.ts
280
+ var DEFAULT_BASE_URL3 = "https://send.smsxabar.uz/broker-api";
281
+ var PlayMobileProvider = class {
282
+ constructor(config, timeoutMs) {
283
+ this.config = config;
284
+ this.timeoutMs = timeoutMs;
285
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
286
+ const creds = `${config.login}:${config.password}`;
287
+ this.authHeader = `Basic ${Buffer.from(creds).toString("base64")}`;
288
+ }
289
+ config;
290
+ timeoutMs;
291
+ name = "playmobile";
292
+ baseUrl;
293
+ authHeader;
294
+ async send(params) {
295
+ const messageId = `uz-sms-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
296
+ const payload = {
297
+ messages: [
298
+ {
299
+ recipient: normalizePhone(params.to),
300
+ "message-id": messageId,
301
+ sms: {
302
+ originator: params.from ?? this.config.from,
303
+ content: { text: params.text }
304
+ }
305
+ }
306
+ ]
307
+ };
308
+ const res = await httpFetch(
309
+ this.name,
310
+ `${this.baseUrl}/send`,
311
+ {
312
+ method: "POST",
313
+ headers: {
314
+ Authorization: this.authHeader,
315
+ "Content-Type": "application/json"
316
+ },
317
+ body: JSON.stringify(payload)
318
+ },
319
+ this.timeoutMs
320
+ );
321
+ const raw = await parseBody(res);
322
+ if (!res.ok) {
323
+ throw new SmsProviderError(this.name, this.describe(raw), {
324
+ status: res.status,
325
+ raw
326
+ });
327
+ }
328
+ return {
329
+ provider: this.name,
330
+ messageId,
331
+ // Play Mobile accepts the batch and delivers asynchronously.
332
+ status: "queued",
333
+ raw
334
+ };
335
+ }
336
+ describe(raw) {
337
+ if (raw && typeof raw === "object" && "error" in raw) {
338
+ return String(raw.error);
339
+ }
340
+ if (typeof raw === "string" && raw) return raw;
341
+ return "Provider rejected the request";
342
+ }
343
+ };
344
+
345
+ // src/providers/smsuz.ts
346
+ var DEFAULT_BASE_URL4 = "https://api.sms.uz/sms/send";
347
+ var DEFAULT_PARAMS = {
348
+ login: "login",
349
+ password: "password",
350
+ phone: "phone",
351
+ text: "text",
352
+ from: "from"
353
+ };
354
+ var SmsUzProvider = class {
355
+ constructor(config, timeoutMs) {
356
+ this.config = config;
357
+ this.timeoutMs = timeoutMs;
358
+ this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL4;
359
+ this.fields = { ...DEFAULT_PARAMS, ...config.params };
360
+ }
361
+ config;
362
+ timeoutMs;
363
+ name = "smsuz";
364
+ baseUrl;
365
+ fields;
366
+ async send(params) {
367
+ const url = new URL(this.baseUrl);
368
+ url.searchParams.set(this.fields.login, this.config.login);
369
+ url.searchParams.set(this.fields.password, this.config.password);
370
+ url.searchParams.set(this.fields.phone, normalizePhone(params.to));
371
+ url.searchParams.set(this.fields.text, params.text);
372
+ const from = params.from ?? this.config.from;
373
+ if (from) url.searchParams.set(this.fields.from, from);
374
+ const res = await httpFetch(
375
+ this.name,
376
+ url.toString(),
377
+ { method: "GET" },
378
+ this.timeoutMs
379
+ );
380
+ const raw = await parseBody(res);
381
+ if (!res.ok) {
382
+ throw new SmsProviderError(this.name, this.describe(raw), {
383
+ status: res.status,
384
+ raw
385
+ });
386
+ }
387
+ return {
388
+ provider: this.name,
389
+ messageId: this.extractId(raw),
390
+ status: "sent",
391
+ raw
392
+ };
393
+ }
394
+ extractId(raw) {
395
+ if (raw && typeof raw === "object") {
396
+ const id = raw.id ?? raw.message_id;
397
+ if (id != null) return String(id);
398
+ }
399
+ return void 0;
400
+ }
401
+ describe(raw) {
402
+ if (raw && typeof raw === "object" && "error" in raw) {
403
+ return String(raw.error);
404
+ }
405
+ if (typeof raw === "string" && raw) return raw;
406
+ return "Provider rejected the request";
407
+ }
408
+ };
409
+
410
+ // src/index.ts
411
+ var DEFAULT_TIMEOUT_MS = 15e3;
412
+ var SmsClient = class {
413
+ provider;
414
+ constructor(config) {
415
+ this.provider = buildProvider(config);
416
+ }
417
+ /** Provider this client sends through. */
418
+ get providerName() {
419
+ return this.provider.name;
420
+ }
421
+ /** Send one SMS. */
422
+ send(params) {
423
+ return this.provider.send(params);
424
+ }
425
+ /** Account balance in UZS. Throws if the active provider has no balance API. */
426
+ getBalance() {
427
+ if (!this.provider.getBalance) {
428
+ throw new SmsConfigError(
429
+ `Provider "${this.provider.name}" does not support balance lookup`
430
+ );
431
+ }
432
+ return this.provider.getBalance();
433
+ }
434
+ };
435
+ function createSmsClient(config) {
436
+ return new SmsClient(config);
437
+ }
438
+ function buildProvider(config) {
439
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
440
+ switch (config.provider) {
441
+ case "eskiz":
442
+ if (!config.eskiz) {
443
+ throw new SmsConfigError('Missing "eskiz" config for provider "eskiz"');
444
+ }
445
+ return new EskizProvider(config.eskiz, timeoutMs);
446
+ case "playmobile":
447
+ if (!config.playmobile) {
448
+ throw new SmsConfigError(
449
+ 'Missing "playmobile" config for provider "playmobile"'
450
+ );
451
+ }
452
+ return new PlayMobileProvider(config.playmobile, timeoutMs);
453
+ case "smsuz":
454
+ if (!config.smsuz) {
455
+ throw new SmsConfigError('Missing "smsuz" config for provider "smsuz"');
456
+ }
457
+ return new SmsUzProvider(config.smsuz, timeoutMs);
458
+ case "octotelecom":
459
+ if (!config.octotelecom) {
460
+ throw new SmsConfigError(
461
+ 'Missing "octotelecom" config for provider "octotelecom"'
462
+ );
463
+ }
464
+ return new OctotelecomProvider(config.octotelecom, timeoutMs);
465
+ default: {
466
+ const exhaustive = config.provider;
467
+ throw new SmsConfigError(`Unknown provider: ${String(exhaustive)}`);
468
+ }
469
+ }
470
+ }
471
+ // Annotate the CommonJS export names for ESM import in node:
472
+ 0 && (module.exports = {
473
+ SmsClient,
474
+ SmsConfigError,
475
+ SmsError,
476
+ SmsProviderError,
477
+ createSmsClient,
478
+ normalizePhone
479
+ });
480
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/internal/http.ts","../src/internal/phone.ts","../src/providers/eskiz.ts","../src/providers/octotelecom.ts","../src/providers/playmobile.ts","../src/providers/smsuz.ts"],"sourcesContent":["import { SmsConfigError } from \"./errors.js\";\nimport { EskizProvider } from \"./providers/eskiz.js\";\nimport { OctotelecomProvider } from \"./providers/octotelecom.js\";\nimport { PlayMobileProvider } from \"./providers/playmobile.js\";\nimport { SmsUzProvider } from \"./providers/smsuz.js\";\nimport type {\n SendSmsParams,\n SendSmsResult,\n SmsClientConfig,\n SmsProvider,\n} from \"./types.js\";\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/** A configured SMS client bound to exactly one provider. */\nexport class SmsClient {\n private readonly provider: SmsProvider;\n\n constructor(config: SmsClientConfig) {\n this.provider = buildProvider(config);\n }\n\n /** Provider this client sends through. */\n get providerName() {\n return this.provider.name;\n }\n\n /** Send one SMS. */\n send(params: SendSmsParams): Promise<SendSmsResult> {\n return this.provider.send(params);\n }\n\n /** Account balance in UZS. Throws if the active provider has no balance API. */\n getBalance(): Promise<number> {\n if (!this.provider.getBalance) {\n throw new SmsConfigError(\n `Provider \"${this.provider.name}\" does not support balance lookup`,\n );\n }\n return this.provider.getBalance();\n }\n}\n\n/** Construct an {@link SmsClient} from config. */\nexport function createSmsClient(config: SmsClientConfig): SmsClient {\n return new SmsClient(config);\n}\n\nfunction buildProvider(config: SmsClientConfig): SmsProvider {\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n switch (config.provider) {\n case \"eskiz\":\n if (!config.eskiz) {\n throw new SmsConfigError('Missing \"eskiz\" config for provider \"eskiz\"');\n }\n return new EskizProvider(config.eskiz, timeoutMs);\n\n case \"playmobile\":\n if (!config.playmobile) {\n throw new SmsConfigError(\n 'Missing \"playmobile\" config for provider \"playmobile\"',\n );\n }\n return new PlayMobileProvider(config.playmobile, timeoutMs);\n\n case \"smsuz\":\n if (!config.smsuz) {\n throw new SmsConfigError('Missing \"smsuz\" config for provider \"smsuz\"');\n }\n return new SmsUzProvider(config.smsuz, timeoutMs);\n\n case \"octotelecom\":\n if (!config.octotelecom) {\n throw new SmsConfigError(\n 'Missing \"octotelecom\" config for provider \"octotelecom\"',\n );\n }\n return new OctotelecomProvider(config.octotelecom, timeoutMs);\n\n default: {\n const exhaustive: never = config.provider;\n throw new SmsConfigError(`Unknown provider: ${String(exhaustive)}`);\n }\n }\n}\n\nexport { normalizePhone } from \"./internal/phone.js\";\nexport {\n SmsError,\n SmsConfigError,\n SmsProviderError,\n} from \"./errors.js\";\nexport type {\n ProviderName,\n SendSmsParams,\n SendSmsResult,\n SmsProvider,\n EskizConfig,\n PlayMobileConfig,\n SmsUzConfig,\n OctotelecomConfig,\n SmsClientConfig,\n} from \"./types.js\";\n","import type { ProviderName } from \"./types.js\";\n\n/** Base error for every failure raised by this package. */\nexport class SmsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SmsError\";\n }\n}\n\n/** Bad client/provider configuration (missing credentials, unknown provider). */\nexport class SmsConfigError extends SmsError {\n constructor(message: string) {\n super(message);\n this.name = \"SmsConfigError\";\n }\n}\n\n/** A provider rejected the request or returned a non-OK response. */\nexport class SmsProviderError extends SmsError {\n /** Provider that produced the error. */\n readonly provider: ProviderName;\n /** HTTP status, when the failure originated from an HTTP response. */\n readonly status?: number;\n /** Raw provider payload, for debugging. */\n readonly raw?: unknown;\n\n constructor(\n provider: ProviderName,\n message: string,\n opts: { status?: number; raw?: unknown } = {},\n ) {\n super(`[${provider}] ${message}`);\n this.name = \"SmsProviderError\";\n this.provider = provider;\n this.status = opts.status;\n this.raw = opts.raw;\n }\n}\n","import { SmsProviderError } from \"../errors.js\";\nimport type { ProviderName } from \"../types.js\";\n\n/** `fetch` wrapper that enforces a timeout and tags failures with the provider. */\nexport async function httpFetch(\n provider: ProviderName,\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new SmsProviderError(provider, `Request timed out after ${timeoutMs}ms`);\n }\n throw new SmsProviderError(\n provider,\n `Network error: ${err instanceof Error ? err.message : String(err)}`,\n { raw: err },\n );\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Parse a response body as JSON, falling back to text on malformed payloads. */\nexport async function parseBody(res: Response): Promise<unknown> {\n const text = await res.text();\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","import { SmsError } from \"../errors.js\";\n\n/**\n * Normalize an Uzbek phone number to bare `998XXXXXXXXX` (12 digits).\n *\n * Accepts `+998 90 123 45 67`, `998901234567`, `90 123-45-67`, etc.\n * A 9-digit national number is assumed to be Uzbek and prefixed with `998`.\n */\nexport function normalizePhone(input: string): string {\n const digits = input.replace(/\\D+/g, \"\");\n\n let national: string;\n if (digits.length === 12 && digits.startsWith(\"998\")) {\n national = digits.slice(3);\n } else if (digits.length === 9) {\n national = digits;\n } else {\n throw new SmsError(\n `Invalid Uzbek phone number: \"${input}\" (expected 998XXXXXXXXX or 9-digit national form)`,\n );\n }\n\n return `998${national}`;\n}\n","import { SmsProviderError } from \"../errors.js\";\nimport { httpFetch, parseBody } from \"../internal/http.js\";\nimport { normalizePhone } from \"../internal/phone.js\";\nimport type {\n EskizConfig,\n SendSmsParams,\n SendSmsResult,\n SmsProvider,\n} from \"../types.js\";\n\nconst DEFAULT_BASE_URL = \"https://notify.eskiz.uz/api\";\n\n/** Adapter for Eskiz (notify.eskiz.uz). Auth is a bearer token minted on demand. */\nexport class EskizProvider implements SmsProvider {\n readonly name = \"eskiz\" as const;\n\n private readonly baseUrl: string;\n private token?: string;\n\n constructor(\n private readonly config: EskizConfig,\n private readonly timeoutMs: number,\n ) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n }\n\n async send(params: SendSmsParams): Promise<SendSmsResult> {\n const body = new URLSearchParams({\n mobile_phone: normalizePhone(params.to),\n message: params.text,\n from: params.from ?? this.config.from ?? \"4546\",\n });\n if (params.callbackUrl) body.set(\"callback_url\", params.callbackUrl);\n\n const raw = await this.request(\"/message/sms/send\", body);\n const data = raw as { id?: string | number; status?: string };\n\n return {\n provider: this.name,\n messageId: data.id != null ? String(data.id) : undefined,\n status: data.status === \"waiting\" ? \"queued\" : \"sent\",\n raw,\n };\n }\n\n async getBalance(): Promise<number> {\n const token = await this.auth();\n const res = await httpFetch(\n this.name,\n `${this.baseUrl}/user/get-limit`,\n { method: \"GET\", headers: { Authorization: `Bearer ${token}` } },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n if (!res.ok) {\n throw new SmsProviderError(this.name, \"Failed to fetch balance\", {\n status: res.status,\n raw,\n });\n }\n const balance = (raw as { data?: { balance?: number } }).data?.balance;\n return typeof balance === \"number\" ? balance : Number(balance ?? 0);\n }\n\n /** POST a form body to an authenticated endpoint, retrying once on a 401. */\n private async request(path: string, body: URLSearchParams): Promise<unknown> {\n for (let attempt = 0; attempt < 2; attempt++) {\n const token = await this.auth();\n const res = await httpFetch(\n this.name,\n `${this.baseUrl}${path}`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}` },\n body,\n },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n\n if (res.status === 401 && attempt === 0) {\n this.token = undefined;\n continue;\n }\n if (!res.ok) {\n throw new SmsProviderError(this.name, this.describe(raw), {\n status: res.status,\n raw,\n });\n }\n return raw;\n }\n // Unreachable: the loop either returns or throws.\n throw new SmsProviderError(this.name, \"Request failed after token refresh\");\n }\n\n /** Return a cached bearer token, minting a fresh one if needed. */\n private async auth(): Promise<string> {\n if (this.token) return this.token;\n\n const body = new URLSearchParams({\n email: this.config.email,\n password: this.config.password,\n });\n const res = await httpFetch(\n this.name,\n `${this.baseUrl}/auth/login`,\n { method: \"POST\", body },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n const token = (raw as { data?: { token?: string } }).data?.token;\n\n if (!res.ok || !token) {\n throw new SmsProviderError(this.name, \"Authentication failed\", {\n status: res.status,\n raw,\n });\n }\n this.token = token;\n return token;\n }\n\n private describe(raw: unknown): string {\n if (raw && typeof raw === \"object\" && \"message\" in raw) {\n return String((raw as { message: unknown }).message);\n }\n return \"Provider rejected the request\";\n }\n}\n","import { SmsProviderError } from \"../errors.js\";\nimport { httpFetch, parseBody } from \"../internal/http.js\";\nimport { normalizePhone } from \"../internal/phone.js\";\nimport type {\n OctotelecomConfig,\n SendSmsParams,\n SendSmsResult,\n SmsProvider,\n} from \"../types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.octotelecom.uz\";\n\n/** Adapter for Octotelecom (JSONv2 API). Auth is HTTP Basic on every request. */\nexport class OctotelecomProvider implements SmsProvider {\n readonly name = \"octotelecom\" as const;\n\n private readonly endpoint: string;\n private readonly authHeader: string;\n\n constructor(\n private readonly config: OctotelecomConfig,\n private readonly timeoutMs: number,\n ) {\n const base = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.endpoint = `${base}/${config.clientId}/json2/simple`;\n const creds = `${config.username}:${config.password}`;\n this.authHeader = `Basic ${Buffer.from(creds).toString(\"base64\")}`;\n }\n\n async send(params: SendSmsParams): Promise<SendSmsResult> {\n const extraId = `uz-sms-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const callbackUrl = params.callbackUrl ?? this.config.callbackUrl;\n\n const payload: Record<string, unknown> = {\n // Octotelecom expects the number without a leading \"+\".\n phone_number: normalizePhone(params.to),\n extra_id: extraId,\n tag: this.config.tag ?? \"uz-sms-glmv\",\n channels: [\"sms\"],\n channel_options: {\n sms: {\n text: params.text,\n alpha_name: params.from ?? this.config.from,\n ttl: this.config.ttl ?? 300,\n },\n },\n };\n if (callbackUrl) payload.callback_url = callbackUrl;\n\n const res = await httpFetch(\n this.name,\n this.endpoint,\n {\n method: \"POST\",\n headers: {\n Authorization: this.authHeader,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n const data = raw as { message_id?: string | number; error_text?: string };\n\n // Octotelecom can return a 200 with an `error_text` instead of an HTTP error.\n if (!res.ok || data?.error_text) {\n throw new SmsProviderError(\n this.name,\n data?.error_text ?? \"Provider rejected the request\",\n { status: res.status, raw },\n );\n }\n\n return {\n provider: this.name,\n messageId: data?.message_id != null ? String(data.message_id) : extraId,\n status: \"sent\",\n raw,\n };\n }\n}\n","import { SmsProviderError } from \"../errors.js\";\nimport { httpFetch, parseBody } from \"../internal/http.js\";\nimport { normalizePhone } from \"../internal/phone.js\";\nimport type {\n PlayMobileConfig,\n SendSmsParams,\n SendSmsResult,\n SmsProvider,\n} from \"../types.js\";\n\nconst DEFAULT_BASE_URL = \"https://send.smsxabar.uz/broker-api\";\n\n/** Adapter for Play Mobile (broker-api). Auth is HTTP Basic on every request. */\nexport class PlayMobileProvider implements SmsProvider {\n readonly name = \"playmobile\" as const;\n\n private readonly baseUrl: string;\n private readonly authHeader: string;\n\n constructor(\n private readonly config: PlayMobileConfig,\n private readonly timeoutMs: number,\n ) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const creds = `${config.login}:${config.password}`;\n this.authHeader = `Basic ${Buffer.from(creds).toString(\"base64\")}`;\n }\n\n async send(params: SendSmsParams): Promise<SendSmsResult> {\n const messageId = `uz-sms-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const payload = {\n messages: [\n {\n recipient: normalizePhone(params.to),\n \"message-id\": messageId,\n sms: {\n originator: params.from ?? this.config.from,\n content: { text: params.text },\n },\n },\n ],\n };\n\n const res = await httpFetch(\n this.name,\n `${this.baseUrl}/send`,\n {\n method: \"POST\",\n headers: {\n Authorization: this.authHeader,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n\n if (!res.ok) {\n throw new SmsProviderError(this.name, this.describe(raw), {\n status: res.status,\n raw,\n });\n }\n\n return {\n provider: this.name,\n messageId,\n // Play Mobile accepts the batch and delivers asynchronously.\n status: \"queued\",\n raw,\n };\n }\n\n private describe(raw: unknown): string {\n if (raw && typeof raw === \"object\" && \"error\" in raw) {\n return String((raw as { error: unknown }).error);\n }\n if (typeof raw === \"string\" && raw) return raw;\n return \"Provider rejected the request\";\n }\n}\n","import { SmsProviderError } from \"../errors.js\";\nimport { httpFetch, parseBody } from \"../internal/http.js\";\nimport { normalizePhone } from \"../internal/phone.js\";\nimport type {\n SendSmsParams,\n SendSmsResult,\n SmsProvider,\n SmsUzConfig,\n} from \"../types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.sms.uz/sms/send\";\n\n/** Default query-param names; override via `SmsUzConfig.params` per account. */\nconst DEFAULT_PARAMS = {\n login: \"login\",\n password: \"password\",\n phone: \"phone\",\n text: \"text\",\n from: \"from\",\n} as const;\n\n/** Adapter for SMS.uz — a simple HTTP GET gateway. */\nexport class SmsUzProvider implements SmsProvider {\n readonly name = \"smsuz\" as const;\n\n private readonly baseUrl: string;\n private readonly fields: Record<keyof typeof DEFAULT_PARAMS, string>;\n\n constructor(\n private readonly config: SmsUzConfig,\n private readonly timeoutMs: number,\n ) {\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.fields = { ...DEFAULT_PARAMS, ...config.params };\n }\n\n async send(params: SendSmsParams): Promise<SendSmsResult> {\n const url = new URL(this.baseUrl);\n url.searchParams.set(this.fields.login, this.config.login);\n url.searchParams.set(this.fields.password, this.config.password);\n url.searchParams.set(this.fields.phone, normalizePhone(params.to));\n url.searchParams.set(this.fields.text, params.text);\n\n const from = params.from ?? this.config.from;\n if (from) url.searchParams.set(this.fields.from, from);\n\n const res = await httpFetch(\n this.name,\n url.toString(),\n { method: \"GET\" },\n this.timeoutMs,\n );\n const raw = await parseBody(res);\n\n if (!res.ok) {\n throw new SmsProviderError(this.name, this.describe(raw), {\n status: res.status,\n raw,\n });\n }\n\n return {\n provider: this.name,\n messageId: this.extractId(raw),\n status: \"sent\",\n raw,\n };\n }\n\n private extractId(raw: unknown): string | undefined {\n if (raw && typeof raw === \"object\") {\n const id = (raw as { id?: unknown; message_id?: unknown }).id ??\n (raw as { message_id?: unknown }).message_id;\n if (id != null) return String(id);\n }\n return undefined;\n }\n\n private describe(raw: unknown): string {\n if (raw && typeof raw === \"object\" && \"error\" in raw) {\n return String((raw as { error: unknown }).error);\n }\n if (typeof raw === \"string\" && raw) return raw;\n return \"Provider rejected the request\";\n }\n}\n"],"mappings":"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","names":["DEFAULT_BASE_URL","DEFAULT_BASE_URL","DEFAULT_BASE_URL"]}
@@ -0,0 +1,150 @@
1
+ /** Supported provider identifiers. */
2
+ type ProviderName = "eskiz" | "playmobile" | "smsuz" | "octotelecom";
3
+ /** Parameters for a single outgoing SMS. */
4
+ interface SendSmsParams {
5
+ /** Recipient phone number. Accepts +998..., 998..., or spaced forms — normalized internally to 998XXXXXXXXX. */
6
+ to: string;
7
+ /** Message body. */
8
+ text: string;
9
+ /** Sender / alpha-name. Falls back to the provider's configured `from`. */
10
+ from?: string;
11
+ /** Optional delivery-report callback URL (supported by Eskiz). */
12
+ callbackUrl?: string;
13
+ }
14
+ /** Normalized result returned by every provider. */
15
+ interface SendSmsResult {
16
+ /** Provider that handled the message. */
17
+ provider: ProviderName;
18
+ /** Provider-side message id, when available. */
19
+ messageId?: string;
20
+ /** Coarse delivery status reported at send time. */
21
+ status: "sent" | "queued" | "failed";
22
+ /** Untouched provider response, for debugging / advanced use. */
23
+ raw: unknown;
24
+ }
25
+ /** Adapter contract every provider implements. */
26
+ interface SmsProvider {
27
+ readonly name: ProviderName;
28
+ send(params: SendSmsParams): Promise<SendSmsResult>;
29
+ /** Account balance in the provider's billing unit (UZS). Optional. */
30
+ getBalance?(): Promise<number>;
31
+ }
32
+ /** Eskiz (notify.eskiz.uz) credentials & options. */
33
+ interface EskizConfig {
34
+ email: string;
35
+ password: string;
36
+ /** Default sender. Eskiz test sender is `4546`. */
37
+ from?: string;
38
+ /** Override API base. Default: https://notify.eskiz.uz/api */
39
+ baseUrl?: string;
40
+ }
41
+ /** Play Mobile (broker-api) credentials & options. */
42
+ interface PlayMobileConfig {
43
+ login: string;
44
+ password: string;
45
+ /** Originator / alpha-name registered with Play Mobile. */
46
+ from: string;
47
+ /** Override API base. Default: https://send.smsxabar.uz/broker-api */
48
+ baseUrl?: string;
49
+ }
50
+ /**
51
+ * SMS.uz credentials & options.
52
+ *
53
+ * SMS.uz exposes a simple HTTP GET gateway whose exact field names vary per
54
+ * account. Defaults below match the common `login/password/phone/text` shape;
55
+ * override `params` if your account uses different names.
56
+ */
57
+ interface SmsUzConfig {
58
+ login: string;
59
+ password: string;
60
+ from?: string;
61
+ /** Override API base. Default: https://api.sms.uz/sms/send */
62
+ baseUrl?: string;
63
+ /** Query-param name mapping, if your gateway differs from the defaults. */
64
+ params?: {
65
+ login?: string;
66
+ password?: string;
67
+ phone?: string;
68
+ text?: string;
69
+ from?: string;
70
+ };
71
+ }
72
+ /**
73
+ * Octotelecom credentials & options.
74
+ *
75
+ * Octotelecom exposes a JSONv2 endpoint at
76
+ * `{baseUrl}/{clientId}/json2/simple` with HTTP Basic auth.
77
+ */
78
+ interface OctotelecomConfig {
79
+ /** Client id — a path segment in the JSONv2 endpoint. */
80
+ clientId: string;
81
+ username: string;
82
+ password: string;
83
+ /** Default alpha-name / sender. */
84
+ from?: string;
85
+ /** Default delivery-report callback URL. Overridable per message. */
86
+ callbackUrl?: string;
87
+ /** Tag attached to every message, for provider-side reporting. Default: `uz-sms`. */
88
+ tag?: string;
89
+ /** Message time-to-live in seconds. Default: 300. */
90
+ ttl?: number;
91
+ /** Override API base. Default: https://api.octotelecom.uz */
92
+ baseUrl?: string;
93
+ }
94
+ /** Top-level client config. Exactly one provider is active per client. */
95
+ interface SmsClientConfig {
96
+ /** Which provider this client sends through. */
97
+ provider: ProviderName;
98
+ /** Request timeout in ms. Default: 15000. */
99
+ timeoutMs?: number;
100
+ eskiz?: EskizConfig;
101
+ playmobile?: PlayMobileConfig;
102
+ smsuz?: SmsUzConfig;
103
+ octotelecom?: OctotelecomConfig;
104
+ }
105
+
106
+ /**
107
+ * Normalize an Uzbek phone number to bare `998XXXXXXXXX` (12 digits).
108
+ *
109
+ * Accepts `+998 90 123 45 67`, `998901234567`, `90 123-45-67`, etc.
110
+ * A 9-digit national number is assumed to be Uzbek and prefixed with `998`.
111
+ */
112
+ declare function normalizePhone(input: string): string;
113
+
114
+ /** Base error for every failure raised by this package. */
115
+ declare class SmsError extends Error {
116
+ constructor(message: string);
117
+ }
118
+ /** Bad client/provider configuration (missing credentials, unknown provider). */
119
+ declare class SmsConfigError extends SmsError {
120
+ constructor(message: string);
121
+ }
122
+ /** A provider rejected the request or returned a non-OK response. */
123
+ declare class SmsProviderError extends SmsError {
124
+ /** Provider that produced the error. */
125
+ readonly provider: ProviderName;
126
+ /** HTTP status, when the failure originated from an HTTP response. */
127
+ readonly status?: number;
128
+ /** Raw provider payload, for debugging. */
129
+ readonly raw?: unknown;
130
+ constructor(provider: ProviderName, message: string, opts?: {
131
+ status?: number;
132
+ raw?: unknown;
133
+ });
134
+ }
135
+
136
+ /** A configured SMS client bound to exactly one provider. */
137
+ declare class SmsClient {
138
+ private readonly provider;
139
+ constructor(config: SmsClientConfig);
140
+ /** Provider this client sends through. */
141
+ get providerName(): ProviderName;
142
+ /** Send one SMS. */
143
+ send(params: SendSmsParams): Promise<SendSmsResult>;
144
+ /** Account balance in UZS. Throws if the active provider has no balance API. */
145
+ getBalance(): Promise<number>;
146
+ }
147
+ /** Construct an {@link SmsClient} from config. */
148
+ declare function createSmsClient(config: SmsClientConfig): SmsClient;
149
+
150
+ export { type EskizConfig, type OctotelecomConfig, type PlayMobileConfig, type ProviderName, type SendSmsParams, type SendSmsResult, SmsClient, type SmsClientConfig, SmsConfigError, SmsError, type SmsProvider, SmsProviderError, type SmsUzConfig, createSmsClient, normalizePhone };