@narcisbodea/smstunnel-sdk 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/index.js ADDED
@@ -0,0 +1,247 @@
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 src_exports = {};
22
+ __export(src_exports, {
23
+ EN_LABELS: () => EN_LABELS,
24
+ RO_LABELS: () => RO_LABELS,
25
+ SmsTunnelClient: () => SmsTunnelClient
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+
29
+ // src/shared/labels.ts
30
+ var EN_LABELS = {
31
+ title: "SMS Configuration (SMSTunnel)",
32
+ description: "Connect an Android phone with the SMSTunnel app to send SMS directly from the application.",
33
+ serverUrlLabel: "SMSTunnel Server",
34
+ serverUrlPlaceholder: "https://smstunnel.io",
35
+ saveButton: "Save",
36
+ pairedStatus: "Connected",
37
+ deviceLabel: "Device",
38
+ unpairButton: "Disconnect",
39
+ unpairConfirm: "Are you sure you want to disconnect the SMS device?",
40
+ notPairedStatus: "Not connected",
41
+ notPairedDescription: "Scan the QR code with the SMSTunnel app on your Android phone.",
42
+ connectButton: "Connect phone",
43
+ scanQrPrompt: "Scan this QR code with the SMSTunnel app on your phone:",
44
+ waitingForPairing: "Waiting for connection...",
45
+ cancelButton: "Cancel",
46
+ qrExpired: "QR code has expired. Generate a new one.",
47
+ testSmsTitle: "Send test SMS",
48
+ testPhonePlaceholder: "Phone number (e.g., +1234567890)",
49
+ testMessagePlaceholder: "Message",
50
+ sendTestButton: "Send",
51
+ smsSentSuccess: "SMS sent successfully!",
52
+ smsError: "Error"
53
+ };
54
+ var RO_LABELS = {
55
+ title: "Configurare SMS (SMSTunnel)",
56
+ description: "Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.",
57
+ serverUrlLabel: "Server SMSTunnel",
58
+ serverUrlPlaceholder: "https://smstunnel.io",
59
+ saveButton: "Salveaza",
60
+ pairedStatus: "Conectat",
61
+ deviceLabel: "Dispozitiv",
62
+ unpairButton: "Deconecteaza",
63
+ unpairConfirm: "Sigur doresti sa deconectezi dispozitivul SMS?",
64
+ notPairedStatus: "Neconectat",
65
+ notPairedDescription: "Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.",
66
+ connectButton: "Conecteaza telefon",
67
+ scanQrPrompt: "Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:",
68
+ waitingForPairing: "Se asteapta conectarea...",
69
+ cancelButton: "Anuleaza",
70
+ qrExpired: "Codul QR a expirat. Genereaza unul nou.",
71
+ testSmsTitle: "Trimite SMS de test",
72
+ testPhonePlaceholder: "Nr. telefon (ex: 0741234567)",
73
+ testMessagePlaceholder: "Mesaj",
74
+ sendTestButton: "Trimite",
75
+ smsSentSuccess: "SMS trimis cu succes!",
76
+ smsError: "Eroare"
77
+ };
78
+
79
+ // src/client.ts
80
+ var SmsTunnelClient = class {
81
+ constructor(options) {
82
+ this.pollTimer = null;
83
+ this.baseUrl = options.apiBaseUrl.replace(/\/$/, "");
84
+ this.prefix = options.routePrefix || "smstunnel";
85
+ this.pollInterval = options.pollInterval || 3e3;
86
+ this.getAuthHeaders = options.getAuthHeaders || (() => ({}));
87
+ }
88
+ // ─── Pairing ───────────────────────────────────────
89
+ /** Get current pairing status */
90
+ async getStatus() {
91
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/status`, {
92
+ headers: this.getAuthHeaders()
93
+ });
94
+ return res.json();
95
+ }
96
+ /** Create a pairing token (generates QR data) */
97
+ async createToken() {
98
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/create-token`, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ ...this.getAuthHeaders()
103
+ }
104
+ });
105
+ return res.json();
106
+ }
107
+ /** Check pairing status for a token (no auth needed) */
108
+ async getPairingStatus(token) {
109
+ const res = await fetch(
110
+ `${this.baseUrl}/${this.prefix}/pairing-status/${token}`
111
+ );
112
+ return res.json();
113
+ }
114
+ /**
115
+ * Start polling for pairing completion.
116
+ * Returns a cleanup function to stop polling.
117
+ */
118
+ startPolling(token, callback) {
119
+ this.stopPolling();
120
+ this.pollTimer = setInterval(async () => {
121
+ try {
122
+ const data = await this.getPairingStatus(token);
123
+ if (data.status === "completed") {
124
+ this.stopPolling();
125
+ callback("completed", data);
126
+ } else if (data.status === "expired") {
127
+ this.stopPolling();
128
+ callback("expired", data);
129
+ }
130
+ } catch (err) {
131
+ }
132
+ }, this.pollInterval);
133
+ return () => this.stopPolling();
134
+ }
135
+ /** Stop polling */
136
+ stopPolling() {
137
+ if (this.pollTimer) {
138
+ clearInterval(this.pollTimer);
139
+ this.pollTimer = null;
140
+ }
141
+ }
142
+ /** Unpair the connected device */
143
+ async unpair() {
144
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/unpair`, {
145
+ method: "POST",
146
+ headers: this.getAuthHeaders()
147
+ });
148
+ return res.json();
149
+ }
150
+ /** List active pairings (proxied through backend) */
151
+ async getPairings() {
152
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/pairings`, {
153
+ headers: this.getAuthHeaders()
154
+ });
155
+ return res.json();
156
+ }
157
+ // ─── SMS ───────────────────────────────────────────
158
+ /** Send a single SMS */
159
+ async sendSms(to, message) {
160
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send`, {
161
+ method: "POST",
162
+ headers: {
163
+ "Content-Type": "application/json",
164
+ ...this.getAuthHeaders()
165
+ },
166
+ body: JSON.stringify({ to, message })
167
+ });
168
+ return res.json();
169
+ }
170
+ /** Send a 2FA SMS */
171
+ async send2fa(to, code, template) {
172
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send-2fa`, {
173
+ method: "POST",
174
+ headers: {
175
+ "Content-Type": "application/json",
176
+ ...this.getAuthHeaders()
177
+ },
178
+ body: JSON.stringify({ to, code, template })
179
+ });
180
+ return res.json();
181
+ }
182
+ /** Send bulk SMS */
183
+ async sendBulk(messages) {
184
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send-bulk`, {
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json",
188
+ ...this.getAuthHeaders()
189
+ },
190
+ body: JSON.stringify({ messages })
191
+ });
192
+ return res.json();
193
+ }
194
+ /** Get SMS delivery status */
195
+ async getSmsStatus(messageId) {
196
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/sms-status/${messageId}`, {
197
+ headers: this.getAuthHeaders()
198
+ });
199
+ return res.json();
200
+ }
201
+ /** Get received SMS (inbox) */
202
+ async getReceivedSms() {
203
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/received`, {
204
+ headers: this.getAuthHeaders()
205
+ });
206
+ return res.json();
207
+ }
208
+ // ─── Devices & Account ─────────────────────────────
209
+ /** List paired devices */
210
+ async getDevices() {
211
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/devices`, {
212
+ headers: this.getAuthHeaders()
213
+ });
214
+ return res.json();
215
+ }
216
+ /** Get account usage stats */
217
+ async getUsage() {
218
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/usage`, {
219
+ headers: this.getAuthHeaders()
220
+ });
221
+ return res.json();
222
+ }
223
+ // ─── Config ────────────────────────────────────────
224
+ /** Update server URL configuration */
225
+ async updateServerUrl(serverUrl) {
226
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/update-config`, {
227
+ method: "POST",
228
+ headers: {
229
+ "Content-Type": "application/json",
230
+ ...this.getAuthHeaders()
231
+ },
232
+ body: JSON.stringify({ serverUrl })
233
+ });
234
+ return res.json();
235
+ }
236
+ /** Destroy - cleanup polling */
237
+ destroy() {
238
+ this.stopPolling();
239
+ }
240
+ };
241
+ // Annotate the CommonJS export names for ESM import in node:
242
+ 0 && (module.exports = {
243
+ EN_LABELS,
244
+ RO_LABELS,
245
+ SmsTunnelClient
246
+ });
247
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/shared/labels.ts","../src/client.ts"],"sourcesContent":["// Common types shared between server and all frontends\r\nexport type {\r\n SmsTunnelConfig,\r\n SmsTunnelStorageAdapter,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n} from './server/smstunnel.interfaces';\r\n\r\n// Shared labels (i18n)\r\nexport type { SmsTunnelLabels } from './shared/labels';\r\nexport { EN_LABELS, RO_LABELS } from './shared/labels';\r\n\r\n// Framework-agnostic client (Angular, Svelte, Astro, Solid, Vanilla JS)\r\nexport { SmsTunnelClient } from './client';\r\nexport type {\r\n SmsTunnelClientOptions,\r\n SmsTunnelStatusResponse,\r\n CreateTokenResponse,\r\n PairingStatusResponse,\r\n SendSmsResponse,\r\n SmsStatusResponse,\r\n SendBulkSmsRequest,\r\n SendBulkSmsResponse,\r\n Send2faRequest,\r\n ReceivedSmsResponse,\r\n DevicesResponse,\r\n AccountUsageResponse,\r\n PairingInfo,\r\n PairingEventCallback,\r\n} from './client';\r\n","export interface SmsTunnelLabels {\r\n title: string;\r\n description: string;\r\n serverUrlLabel: string;\r\n serverUrlPlaceholder: string;\r\n saveButton: string;\r\n pairedStatus: string;\r\n deviceLabel: string;\r\n unpairButton: string;\r\n unpairConfirm: string;\r\n notPairedStatus: string;\r\n notPairedDescription: string;\r\n connectButton: string;\r\n scanQrPrompt: string;\r\n waitingForPairing: string;\r\n cancelButton: string;\r\n qrExpired: string;\r\n testSmsTitle: string;\r\n testPhonePlaceholder: string;\r\n testMessagePlaceholder: string;\r\n sendTestButton: string;\r\n smsSentSuccess: string;\r\n smsError: string;\r\n}\r\n\r\nexport const EN_LABELS: SmsTunnelLabels = {\r\n title: 'SMS Configuration (SMSTunnel)',\r\n description: 'Connect an Android phone with the SMSTunnel app to send SMS directly from the application.',\r\n serverUrlLabel: 'SMSTunnel Server',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Save',\r\n pairedStatus: 'Connected',\r\n deviceLabel: 'Device',\r\n unpairButton: 'Disconnect',\r\n unpairConfirm: 'Are you sure you want to disconnect the SMS device?',\r\n notPairedStatus: 'Not connected',\r\n notPairedDescription: 'Scan the QR code with the SMSTunnel app on your Android phone.',\r\n connectButton: 'Connect phone',\r\n scanQrPrompt: 'Scan this QR code with the SMSTunnel app on your phone:',\r\n waitingForPairing: 'Waiting for connection...',\r\n cancelButton: 'Cancel',\r\n qrExpired: 'QR code has expired. Generate a new one.',\r\n testSmsTitle: 'Send test SMS',\r\n testPhonePlaceholder: 'Phone number (e.g., +1234567890)',\r\n testMessagePlaceholder: 'Message',\r\n sendTestButton: 'Send',\r\n smsSentSuccess: 'SMS sent successfully!',\r\n smsError: 'Error',\r\n};\r\n\r\nexport const RO_LABELS: SmsTunnelLabels = {\r\n title: 'Configurare SMS (SMSTunnel)',\r\n description: 'Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.',\r\n serverUrlLabel: 'Server SMSTunnel',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Salveaza',\r\n pairedStatus: 'Conectat',\r\n deviceLabel: 'Dispozitiv',\r\n unpairButton: 'Deconecteaza',\r\n unpairConfirm: 'Sigur doresti sa deconectezi dispozitivul SMS?',\r\n notPairedStatus: 'Neconectat',\r\n notPairedDescription: 'Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.',\r\n connectButton: 'Conecteaza telefon',\r\n scanQrPrompt: 'Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:',\r\n waitingForPairing: 'Se asteapta conectarea...',\r\n cancelButton: 'Anuleaza',\r\n qrExpired: 'Codul QR a expirat. Genereaza unul nou.',\r\n testSmsTitle: 'Trimite SMS de test',\r\n testPhonePlaceholder: 'Nr. telefon (ex: 0741234567)',\r\n testMessagePlaceholder: 'Mesaj',\r\n sendTestButton: 'Trimite',\r\n smsSentSuccess: 'SMS trimis cu succes!',\r\n smsError: 'Eroare',\r\n};\r\n","/**\r\n * Framework-agnostic SMSTunnel client.\r\n *\r\n * Works with Angular, Svelte, Astro, Solid, Vanilla JS, or any framework.\r\n * For React use `smstunnel-sdk/react`, for Vue use `smstunnel-sdk/vue`.\r\n */\r\nexport interface SmsTunnelClientOptions {\r\n apiBaseUrl: string;\r\n getAuthHeaders?: () => Record<string, string>;\r\n routePrefix?: string;\r\n pollInterval?: number;\r\n}\r\n\r\n// --- Status ---\r\n\r\nexport interface SmsTunnelStatusResponse {\r\n paired: boolean;\r\n serverUrl?: string;\r\n deviceName?: string;\r\n}\r\n\r\n// --- Pairing ---\r\n\r\nexport interface CreateTokenResponse {\r\n success: boolean;\r\n token?: string;\r\n qrData?: string;\r\n expiresAt?: string;\r\n pollUrl?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface PairingStatusResponse {\r\n status: 'pending' | 'completed' | 'expired' | 'error';\r\n source?: string;\r\n displayName?: string;\r\n pairedDeviceId?: string;\r\n pairingId?: string;\r\n error?: string;\r\n}\r\n\r\n// --- SMS ---\r\n\r\nexport interface SendSmsResponse {\r\n success: boolean;\r\n messageId?: string;\r\n queued?: boolean;\r\n data?: {\r\n messageId: string;\r\n branded: boolean;\r\n queued: boolean;\r\n remaining: number;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface SmsStatusResponse {\r\n success: boolean;\r\n data?: {\r\n messageId: string;\r\n status: 'sent' | 'pending' | 'failed' | 'delivered';\r\n recipient: string;\r\n sentAt?: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface SendBulkSmsRequest {\r\n messages: Array<{ to: string; message: string }>;\r\n}\r\n\r\nexport interface SendBulkSmsResponse {\r\n success: boolean;\r\n results?: Array<{ to: string; messageId?: string; error?: string }>;\r\n error?: string;\r\n}\r\n\r\nexport interface Send2faRequest {\r\n to: string;\r\n code: string;\r\n template?: string;\r\n}\r\n\r\nexport interface ReceivedSmsResponse {\r\n success: boolean;\r\n data?: Array<{\r\n from: string;\r\n message: string;\r\n receivedAt: string;\r\n }>;\r\n error?: string;\r\n}\r\n\r\n// --- Devices ---\r\n\r\nexport interface DevicesResponse {\r\n success: boolean;\r\n data?: Array<{\r\n id: string;\r\n clientId: string;\r\n name: string;\r\n brand?: string;\r\n model?: string;\r\n connected?: boolean;\r\n }>;\r\n error?: string;\r\n}\r\n\r\n// --- Account ---\r\n\r\nexport interface AccountUsageResponse {\r\n success: boolean;\r\n data?: {\r\n messagesSent: number;\r\n messagesReceived: number;\r\n messagesSentToday: number;\r\n messagesSentMonth: number;\r\n dailyLimit?: number;\r\n monthlyLimit?: number;\r\n };\r\n error?: string;\r\n}\r\n\r\n// --- Pairings list ---\r\n\r\nexport interface PairingInfo {\r\n _id: string;\r\n deviceId: string;\r\n type: string;\r\n name: string;\r\n status: 'active' | 'paused' | 'revoked';\r\n messagesSent: number;\r\n createdAt: string;\r\n source: string;\r\n}\r\n\r\n// --- Callback ---\r\n\r\nexport type PairingEventCallback = (event: 'completed' | 'expired' | 'error', data?: any) => void;\r\n\r\nexport class SmsTunnelClient {\r\n private readonly baseUrl: string;\r\n private readonly prefix: string;\r\n private readonly pollInterval: number;\r\n private readonly getAuthHeaders: () => Record<string, string>;\r\n private pollTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n constructor(options: SmsTunnelClientOptions) {\r\n this.baseUrl = options.apiBaseUrl.replace(/\\/$/, '');\r\n this.prefix = options.routePrefix || 'smstunnel';\r\n this.pollInterval = options.pollInterval || 3000;\r\n this.getAuthHeaders = options.getAuthHeaders || (() => ({}));\r\n }\r\n\r\n // ─── Pairing ───────────────────────────────────────\r\n\r\n /** Get current pairing status */\r\n async getStatus(): Promise<SmsTunnelStatusResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/status`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Create a pairing token (generates QR data) */\r\n async createToken(): Promise<CreateTokenResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Check pairing status for a token (no auth needed) */\r\n async getPairingStatus(token: string): Promise<PairingStatusResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/pairing-status/${token}`,\r\n );\r\n return res.json();\r\n }\r\n\r\n /**\r\n * Start polling for pairing completion.\r\n * Returns a cleanup function to stop polling.\r\n */\r\n startPolling(token: string, callback: PairingEventCallback): () => void {\r\n this.stopPolling();\r\n\r\n this.pollTimer = setInterval(async () => {\r\n try {\r\n const data = await this.getPairingStatus(token);\r\n if (data.status === 'completed') {\r\n this.stopPolling();\r\n callback('completed', data);\r\n } else if (data.status === 'expired') {\r\n this.stopPolling();\r\n callback('expired', data);\r\n }\r\n } catch (err) {\r\n // ignore polling errors\r\n }\r\n }, this.pollInterval);\r\n\r\n return () => this.stopPolling();\r\n }\r\n\r\n /** Stop polling */\r\n stopPolling(): void {\r\n if (this.pollTimer) {\r\n clearInterval(this.pollTimer);\r\n this.pollTimer = null;\r\n }\r\n }\r\n\r\n /** Unpair the connected device */\r\n async unpair(): Promise<{ success: boolean }> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/unpair`, {\r\n method: 'POST',\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** List active pairings (proxied through backend) */\r\n async getPairings(): Promise<PairingInfo[]> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/pairings`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── SMS ───────────────────────────────────────────\r\n\r\n /** Send a single SMS */\r\n async sendSms(to: string, message: string): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send a 2FA SMS */\r\n async send2fa(to: string, code: string, template?: string): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-2fa`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ to, code, template }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send bulk SMS */\r\n async sendBulk(messages: Array<{ to: string; message: string }>): Promise<SendBulkSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-bulk`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ messages }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get SMS delivery status */\r\n async getSmsStatus(messageId: string): Promise<SmsStatusResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/sms-status/${messageId}`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get received SMS (inbox) */\r\n async getReceivedSms(): Promise<ReceivedSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/received`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /** List paired devices */\r\n async getDevices(): Promise<DevicesResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/devices`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get account usage stats */\r\n async getUsage(): Promise<AccountUsageResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/usage`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── Config ────────────────────────────────────────\r\n\r\n /** Update server URL configuration */\r\n async updateServerUrl(serverUrl: string): Promise<{ success: boolean }> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Destroy - cleanup polling */\r\n destroy(): void {\r\n this.stopPolling();\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,218 @@
1
+ // src/shared/labels.ts
2
+ var EN_LABELS = {
3
+ title: "SMS Configuration (SMSTunnel)",
4
+ description: "Connect an Android phone with the SMSTunnel app to send SMS directly from the application.",
5
+ serverUrlLabel: "SMSTunnel Server",
6
+ serverUrlPlaceholder: "https://smstunnel.io",
7
+ saveButton: "Save",
8
+ pairedStatus: "Connected",
9
+ deviceLabel: "Device",
10
+ unpairButton: "Disconnect",
11
+ unpairConfirm: "Are you sure you want to disconnect the SMS device?",
12
+ notPairedStatus: "Not connected",
13
+ notPairedDescription: "Scan the QR code with the SMSTunnel app on your Android phone.",
14
+ connectButton: "Connect phone",
15
+ scanQrPrompt: "Scan this QR code with the SMSTunnel app on your phone:",
16
+ waitingForPairing: "Waiting for connection...",
17
+ cancelButton: "Cancel",
18
+ qrExpired: "QR code has expired. Generate a new one.",
19
+ testSmsTitle: "Send test SMS",
20
+ testPhonePlaceholder: "Phone number (e.g., +1234567890)",
21
+ testMessagePlaceholder: "Message",
22
+ sendTestButton: "Send",
23
+ smsSentSuccess: "SMS sent successfully!",
24
+ smsError: "Error"
25
+ };
26
+ var RO_LABELS = {
27
+ title: "Configurare SMS (SMSTunnel)",
28
+ description: "Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.",
29
+ serverUrlLabel: "Server SMSTunnel",
30
+ serverUrlPlaceholder: "https://smstunnel.io",
31
+ saveButton: "Salveaza",
32
+ pairedStatus: "Conectat",
33
+ deviceLabel: "Dispozitiv",
34
+ unpairButton: "Deconecteaza",
35
+ unpairConfirm: "Sigur doresti sa deconectezi dispozitivul SMS?",
36
+ notPairedStatus: "Neconectat",
37
+ notPairedDescription: "Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.",
38
+ connectButton: "Conecteaza telefon",
39
+ scanQrPrompt: "Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:",
40
+ waitingForPairing: "Se asteapta conectarea...",
41
+ cancelButton: "Anuleaza",
42
+ qrExpired: "Codul QR a expirat. Genereaza unul nou.",
43
+ testSmsTitle: "Trimite SMS de test",
44
+ testPhonePlaceholder: "Nr. telefon (ex: 0741234567)",
45
+ testMessagePlaceholder: "Mesaj",
46
+ sendTestButton: "Trimite",
47
+ smsSentSuccess: "SMS trimis cu succes!",
48
+ smsError: "Eroare"
49
+ };
50
+
51
+ // src/client.ts
52
+ var SmsTunnelClient = class {
53
+ constructor(options) {
54
+ this.pollTimer = null;
55
+ this.baseUrl = options.apiBaseUrl.replace(/\/$/, "");
56
+ this.prefix = options.routePrefix || "smstunnel";
57
+ this.pollInterval = options.pollInterval || 3e3;
58
+ this.getAuthHeaders = options.getAuthHeaders || (() => ({}));
59
+ }
60
+ // ─── Pairing ───────────────────────────────────────
61
+ /** Get current pairing status */
62
+ async getStatus() {
63
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/status`, {
64
+ headers: this.getAuthHeaders()
65
+ });
66
+ return res.json();
67
+ }
68
+ /** Create a pairing token (generates QR data) */
69
+ async createToken() {
70
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/create-token`, {
71
+ method: "POST",
72
+ headers: {
73
+ "Content-Type": "application/json",
74
+ ...this.getAuthHeaders()
75
+ }
76
+ });
77
+ return res.json();
78
+ }
79
+ /** Check pairing status for a token (no auth needed) */
80
+ async getPairingStatus(token) {
81
+ const res = await fetch(
82
+ `${this.baseUrl}/${this.prefix}/pairing-status/${token}`
83
+ );
84
+ return res.json();
85
+ }
86
+ /**
87
+ * Start polling for pairing completion.
88
+ * Returns a cleanup function to stop polling.
89
+ */
90
+ startPolling(token, callback) {
91
+ this.stopPolling();
92
+ this.pollTimer = setInterval(async () => {
93
+ try {
94
+ const data = await this.getPairingStatus(token);
95
+ if (data.status === "completed") {
96
+ this.stopPolling();
97
+ callback("completed", data);
98
+ } else if (data.status === "expired") {
99
+ this.stopPolling();
100
+ callback("expired", data);
101
+ }
102
+ } catch (err) {
103
+ }
104
+ }, this.pollInterval);
105
+ return () => this.stopPolling();
106
+ }
107
+ /** Stop polling */
108
+ stopPolling() {
109
+ if (this.pollTimer) {
110
+ clearInterval(this.pollTimer);
111
+ this.pollTimer = null;
112
+ }
113
+ }
114
+ /** Unpair the connected device */
115
+ async unpair() {
116
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/unpair`, {
117
+ method: "POST",
118
+ headers: this.getAuthHeaders()
119
+ });
120
+ return res.json();
121
+ }
122
+ /** List active pairings (proxied through backend) */
123
+ async getPairings() {
124
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/pairings`, {
125
+ headers: this.getAuthHeaders()
126
+ });
127
+ return res.json();
128
+ }
129
+ // ─── SMS ───────────────────────────────────────────
130
+ /** Send a single SMS */
131
+ async sendSms(to, message) {
132
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send`, {
133
+ method: "POST",
134
+ headers: {
135
+ "Content-Type": "application/json",
136
+ ...this.getAuthHeaders()
137
+ },
138
+ body: JSON.stringify({ to, message })
139
+ });
140
+ return res.json();
141
+ }
142
+ /** Send a 2FA SMS */
143
+ async send2fa(to, code, template) {
144
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send-2fa`, {
145
+ method: "POST",
146
+ headers: {
147
+ "Content-Type": "application/json",
148
+ ...this.getAuthHeaders()
149
+ },
150
+ body: JSON.stringify({ to, code, template })
151
+ });
152
+ return res.json();
153
+ }
154
+ /** Send bulk SMS */
155
+ async sendBulk(messages) {
156
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/send-bulk`, {
157
+ method: "POST",
158
+ headers: {
159
+ "Content-Type": "application/json",
160
+ ...this.getAuthHeaders()
161
+ },
162
+ body: JSON.stringify({ messages })
163
+ });
164
+ return res.json();
165
+ }
166
+ /** Get SMS delivery status */
167
+ async getSmsStatus(messageId) {
168
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/sms-status/${messageId}`, {
169
+ headers: this.getAuthHeaders()
170
+ });
171
+ return res.json();
172
+ }
173
+ /** Get received SMS (inbox) */
174
+ async getReceivedSms() {
175
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/received`, {
176
+ headers: this.getAuthHeaders()
177
+ });
178
+ return res.json();
179
+ }
180
+ // ─── Devices & Account ─────────────────────────────
181
+ /** List paired devices */
182
+ async getDevices() {
183
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/devices`, {
184
+ headers: this.getAuthHeaders()
185
+ });
186
+ return res.json();
187
+ }
188
+ /** Get account usage stats */
189
+ async getUsage() {
190
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/usage`, {
191
+ headers: this.getAuthHeaders()
192
+ });
193
+ return res.json();
194
+ }
195
+ // ─── Config ────────────────────────────────────────
196
+ /** Update server URL configuration */
197
+ async updateServerUrl(serverUrl) {
198
+ const res = await fetch(`${this.baseUrl}/${this.prefix}/update-config`, {
199
+ method: "POST",
200
+ headers: {
201
+ "Content-Type": "application/json",
202
+ ...this.getAuthHeaders()
203
+ },
204
+ body: JSON.stringify({ serverUrl })
205
+ });
206
+ return res.json();
207
+ }
208
+ /** Destroy - cleanup polling */
209
+ destroy() {
210
+ this.stopPolling();
211
+ }
212
+ };
213
+ export {
214
+ EN_LABELS,
215
+ RO_LABELS,
216
+ SmsTunnelClient
217
+ };
218
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/labels.ts","../src/client.ts"],"sourcesContent":["export interface SmsTunnelLabels {\r\n title: string;\r\n description: string;\r\n serverUrlLabel: string;\r\n serverUrlPlaceholder: string;\r\n saveButton: string;\r\n pairedStatus: string;\r\n deviceLabel: string;\r\n unpairButton: string;\r\n unpairConfirm: string;\r\n notPairedStatus: string;\r\n notPairedDescription: string;\r\n connectButton: string;\r\n scanQrPrompt: string;\r\n waitingForPairing: string;\r\n cancelButton: string;\r\n qrExpired: string;\r\n testSmsTitle: string;\r\n testPhonePlaceholder: string;\r\n testMessagePlaceholder: string;\r\n sendTestButton: string;\r\n smsSentSuccess: string;\r\n smsError: string;\r\n}\r\n\r\nexport const EN_LABELS: SmsTunnelLabels = {\r\n title: 'SMS Configuration (SMSTunnel)',\r\n description: 'Connect an Android phone with the SMSTunnel app to send SMS directly from the application.',\r\n serverUrlLabel: 'SMSTunnel Server',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Save',\r\n pairedStatus: 'Connected',\r\n deviceLabel: 'Device',\r\n unpairButton: 'Disconnect',\r\n unpairConfirm: 'Are you sure you want to disconnect the SMS device?',\r\n notPairedStatus: 'Not connected',\r\n notPairedDescription: 'Scan the QR code with the SMSTunnel app on your Android phone.',\r\n connectButton: 'Connect phone',\r\n scanQrPrompt: 'Scan this QR code with the SMSTunnel app on your phone:',\r\n waitingForPairing: 'Waiting for connection...',\r\n cancelButton: 'Cancel',\r\n qrExpired: 'QR code has expired. Generate a new one.',\r\n testSmsTitle: 'Send test SMS',\r\n testPhonePlaceholder: 'Phone number (e.g., +1234567890)',\r\n testMessagePlaceholder: 'Message',\r\n sendTestButton: 'Send',\r\n smsSentSuccess: 'SMS sent successfully!',\r\n smsError: 'Error',\r\n};\r\n\r\nexport const RO_LABELS: SmsTunnelLabels = {\r\n title: 'Configurare SMS (SMSTunnel)',\r\n description: 'Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.',\r\n serverUrlLabel: 'Server SMSTunnel',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Salveaza',\r\n pairedStatus: 'Conectat',\r\n deviceLabel: 'Dispozitiv',\r\n unpairButton: 'Deconecteaza',\r\n unpairConfirm: 'Sigur doresti sa deconectezi dispozitivul SMS?',\r\n notPairedStatus: 'Neconectat',\r\n notPairedDescription: 'Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.',\r\n connectButton: 'Conecteaza telefon',\r\n scanQrPrompt: 'Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:',\r\n waitingForPairing: 'Se asteapta conectarea...',\r\n cancelButton: 'Anuleaza',\r\n qrExpired: 'Codul QR a expirat. Genereaza unul nou.',\r\n testSmsTitle: 'Trimite SMS de test',\r\n testPhonePlaceholder: 'Nr. telefon (ex: 0741234567)',\r\n testMessagePlaceholder: 'Mesaj',\r\n sendTestButton: 'Trimite',\r\n smsSentSuccess: 'SMS trimis cu succes!',\r\n smsError: 'Eroare',\r\n};\r\n","/**\r\n * Framework-agnostic SMSTunnel client.\r\n *\r\n * Works with Angular, Svelte, Astro, Solid, Vanilla JS, or any framework.\r\n * For React use `smstunnel-sdk/react`, for Vue use `smstunnel-sdk/vue`.\r\n */\r\nexport interface SmsTunnelClientOptions {\r\n apiBaseUrl: string;\r\n getAuthHeaders?: () => Record<string, string>;\r\n routePrefix?: string;\r\n pollInterval?: number;\r\n}\r\n\r\n// --- Status ---\r\n\r\nexport interface SmsTunnelStatusResponse {\r\n paired: boolean;\r\n serverUrl?: string;\r\n deviceName?: string;\r\n}\r\n\r\n// --- Pairing ---\r\n\r\nexport interface CreateTokenResponse {\r\n success: boolean;\r\n token?: string;\r\n qrData?: string;\r\n expiresAt?: string;\r\n pollUrl?: string;\r\n error?: string;\r\n}\r\n\r\nexport interface PairingStatusResponse {\r\n status: 'pending' | 'completed' | 'expired' | 'error';\r\n source?: string;\r\n displayName?: string;\r\n pairedDeviceId?: string;\r\n pairingId?: string;\r\n error?: string;\r\n}\r\n\r\n// --- SMS ---\r\n\r\nexport interface SendSmsResponse {\r\n success: boolean;\r\n messageId?: string;\r\n queued?: boolean;\r\n data?: {\r\n messageId: string;\r\n branded: boolean;\r\n queued: boolean;\r\n remaining: number;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface SmsStatusResponse {\r\n success: boolean;\r\n data?: {\r\n messageId: string;\r\n status: 'sent' | 'pending' | 'failed' | 'delivered';\r\n recipient: string;\r\n sentAt?: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface SendBulkSmsRequest {\r\n messages: Array<{ to: string; message: string }>;\r\n}\r\n\r\nexport interface SendBulkSmsResponse {\r\n success: boolean;\r\n results?: Array<{ to: string; messageId?: string; error?: string }>;\r\n error?: string;\r\n}\r\n\r\nexport interface Send2faRequest {\r\n to: string;\r\n code: string;\r\n template?: string;\r\n}\r\n\r\nexport interface ReceivedSmsResponse {\r\n success: boolean;\r\n data?: Array<{\r\n from: string;\r\n message: string;\r\n receivedAt: string;\r\n }>;\r\n error?: string;\r\n}\r\n\r\n// --- Devices ---\r\n\r\nexport interface DevicesResponse {\r\n success: boolean;\r\n data?: Array<{\r\n id: string;\r\n clientId: string;\r\n name: string;\r\n brand?: string;\r\n model?: string;\r\n connected?: boolean;\r\n }>;\r\n error?: string;\r\n}\r\n\r\n// --- Account ---\r\n\r\nexport interface AccountUsageResponse {\r\n success: boolean;\r\n data?: {\r\n messagesSent: number;\r\n messagesReceived: number;\r\n messagesSentToday: number;\r\n messagesSentMonth: number;\r\n dailyLimit?: number;\r\n monthlyLimit?: number;\r\n };\r\n error?: string;\r\n}\r\n\r\n// --- Pairings list ---\r\n\r\nexport interface PairingInfo {\r\n _id: string;\r\n deviceId: string;\r\n type: string;\r\n name: string;\r\n status: 'active' | 'paused' | 'revoked';\r\n messagesSent: number;\r\n createdAt: string;\r\n source: string;\r\n}\r\n\r\n// --- Callback ---\r\n\r\nexport type PairingEventCallback = (event: 'completed' | 'expired' | 'error', data?: any) => void;\r\n\r\nexport class SmsTunnelClient {\r\n private readonly baseUrl: string;\r\n private readonly prefix: string;\r\n private readonly pollInterval: number;\r\n private readonly getAuthHeaders: () => Record<string, string>;\r\n private pollTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n constructor(options: SmsTunnelClientOptions) {\r\n this.baseUrl = options.apiBaseUrl.replace(/\\/$/, '');\r\n this.prefix = options.routePrefix || 'smstunnel';\r\n this.pollInterval = options.pollInterval || 3000;\r\n this.getAuthHeaders = options.getAuthHeaders || (() => ({}));\r\n }\r\n\r\n // ─── Pairing ───────────────────────────────────────\r\n\r\n /** Get current pairing status */\r\n async getStatus(): Promise<SmsTunnelStatusResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/status`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Create a pairing token (generates QR data) */\r\n async createToken(): Promise<CreateTokenResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Check pairing status for a token (no auth needed) */\r\n async getPairingStatus(token: string): Promise<PairingStatusResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/pairing-status/${token}`,\r\n );\r\n return res.json();\r\n }\r\n\r\n /**\r\n * Start polling for pairing completion.\r\n * Returns a cleanup function to stop polling.\r\n */\r\n startPolling(token: string, callback: PairingEventCallback): () => void {\r\n this.stopPolling();\r\n\r\n this.pollTimer = setInterval(async () => {\r\n try {\r\n const data = await this.getPairingStatus(token);\r\n if (data.status === 'completed') {\r\n this.stopPolling();\r\n callback('completed', data);\r\n } else if (data.status === 'expired') {\r\n this.stopPolling();\r\n callback('expired', data);\r\n }\r\n } catch (err) {\r\n // ignore polling errors\r\n }\r\n }, this.pollInterval);\r\n\r\n return () => this.stopPolling();\r\n }\r\n\r\n /** Stop polling */\r\n stopPolling(): void {\r\n if (this.pollTimer) {\r\n clearInterval(this.pollTimer);\r\n this.pollTimer = null;\r\n }\r\n }\r\n\r\n /** Unpair the connected device */\r\n async unpair(): Promise<{ success: boolean }> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/unpair`, {\r\n method: 'POST',\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** List active pairings (proxied through backend) */\r\n async getPairings(): Promise<PairingInfo[]> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/pairings`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── SMS ───────────────────────────────────────────\r\n\r\n /** Send a single SMS */\r\n async sendSms(to: string, message: string): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send a 2FA SMS */\r\n async send2fa(to: string, code: string, template?: string): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-2fa`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ to, code, template }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send bulk SMS */\r\n async sendBulk(messages: Array<{ to: string; message: string }>): Promise<SendBulkSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-bulk`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ messages }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get SMS delivery status */\r\n async getSmsStatus(messageId: string): Promise<SmsStatusResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/sms-status/${messageId}`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get received SMS (inbox) */\r\n async getReceivedSms(): Promise<ReceivedSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/received`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /** List paired devices */\r\n async getDevices(): Promise<DevicesResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/devices`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Get account usage stats */\r\n async getUsage(): Promise<AccountUsageResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/usage`, {\r\n headers: this.getAuthHeaders(),\r\n });\r\n return res.json();\r\n }\r\n\r\n // ─── Config ────────────────────────────────────────\r\n\r\n /** Update server URL configuration */\r\n async updateServerUrl(serverUrl: string): Promise<{ success: boolean }> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Destroy - cleanup polling */\r\n destroy(): void {\r\n this.stopPolling();\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,93 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface SmsTunnelLabels {
4
+ title: string;
5
+ description: string;
6
+ serverUrlLabel: string;
7
+ serverUrlPlaceholder: string;
8
+ saveButton: string;
9
+ pairedStatus: string;
10
+ deviceLabel: string;
11
+ unpairButton: string;
12
+ unpairConfirm: string;
13
+ notPairedStatus: string;
14
+ notPairedDescription: string;
15
+ connectButton: string;
16
+ scanQrPrompt: string;
17
+ waitingForPairing: string;
18
+ cancelButton: string;
19
+ qrExpired: string;
20
+ testSmsTitle: string;
21
+ testPhonePlaceholder: string;
22
+ testMessagePlaceholder: string;
23
+ sendTestButton: string;
24
+ smsSentSuccess: string;
25
+ smsError: string;
26
+ }
27
+ declare const EN_LABELS: SmsTunnelLabels;
28
+ declare const RO_LABELS: SmsTunnelLabels;
29
+
30
+ interface SmsTunnelPairingProps {
31
+ apiBaseUrl: string;
32
+ getAuthHeaders?: () => Record<string, string>;
33
+ labels?: SmsTunnelLabels;
34
+ onPaired?: (device: {
35
+ deviceName: string;
36
+ }) => void;
37
+ onUnpaired?: () => void;
38
+ showTestSms?: boolean;
39
+ showServerUrlInput?: boolean;
40
+ qrSize?: number;
41
+ routePrefix?: string;
42
+ className?: string;
43
+ }
44
+ interface UseSmsTunnelOptions {
45
+ apiBaseUrl: string;
46
+ getAuthHeaders?: () => Record<string, string>;
47
+ routePrefix?: string;
48
+ pollInterval?: number;
49
+ }
50
+ interface SmsTunnelState {
51
+ status: 'loading' | 'unpaired' | 'paired';
52
+ serverUrl: string;
53
+ deviceName: string;
54
+ showQr: boolean;
55
+ qrData: string;
56
+ generating: boolean;
57
+ polling: boolean;
58
+ error: string;
59
+ }
60
+
61
+ declare function SmsTunnelPairing({ apiBaseUrl, getAuthHeaders, labels, onPaired, onUnpaired, showTestSms, showServerUrlInput, qrSize, routePrefix, className, }: SmsTunnelPairingProps): react_jsx_runtime.JSX.Element;
62
+
63
+ interface QrCodeCanvasProps {
64
+ value: string;
65
+ size?: number;
66
+ }
67
+ declare function QrCodeCanvas({ value, size }: QrCodeCanvasProps): react_jsx_runtime.JSX.Element;
68
+
69
+ declare function useSmsTunnel(options: UseSmsTunnelOptions): {
70
+ generateQr: () => Promise<void>;
71
+ cancelPairing: () => void;
72
+ unpair: () => Promise<void>;
73
+ sendTestSms: (to: string, message: string) => Promise<{
74
+ success: boolean;
75
+ messageId?: string;
76
+ error?: string;
77
+ } | {
78
+ success: boolean;
79
+ error: any;
80
+ }>;
81
+ updateServerUrl: (serverUrl: string) => Promise<void>;
82
+ refetch: () => Promise<void>;
83
+ status: "loading" | "unpaired" | "paired";
84
+ serverUrl: string;
85
+ deviceName: string;
86
+ showQr: boolean;
87
+ qrData: string;
88
+ generating: boolean;
89
+ polling: boolean;
90
+ error: string;
91
+ };
92
+
93
+ export { EN_LABELS, QrCodeCanvas, type QrCodeCanvasProps, RO_LABELS, type SmsTunnelLabels, SmsTunnelPairing, type SmsTunnelPairingProps, type SmsTunnelState, type UseSmsTunnelOptions, useSmsTunnel };