@narcisbodea/smstunnel-sdk 1.1.4 → 1.1.6

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.
@@ -1 +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// --- E2E Encryption ---\r\n\r\nexport interface DeviceE2EStatusResponse {\r\n success: boolean;\r\n data?: {\r\n encryptionEnabled: boolean;\r\n hasPublicKey: boolean;\r\n publicKeyFingerprint?: string;\r\n keyCreatedAt?: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface DevicePublicKeyResponse {\r\n success: boolean;\r\n data?: {\r\n deviceId: string;\r\n deviceName: string;\r\n publicKey: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface VerifyDeviceKeyResponse {\r\n valid: boolean;\r\n currentFingerprint?: string;\r\n needsRePairing: boolean;\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 // ─── E2E Encryption ────────────────────────────────\r\n\r\n /** Get E2E encryption status for a device */\r\n async getDeviceE2EStatus(deviceId: string): Promise<DeviceE2EStatusResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/e2e-status/${deviceId}`,\r\n { headers: this.getAuthHeaders() },\r\n );\r\n return res.json();\r\n }\r\n\r\n /** Get device public key for E2E encryption */\r\n async getDevicePublicKey(deviceId: string): Promise<DevicePublicKeyResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/public-key/${deviceId}`,\r\n { headers: this.getAuthHeaders() },\r\n );\r\n return res.json();\r\n }\r\n\r\n /** Verify that a stored fingerprint matches the current device key */\r\n async verifyDeviceKey(\r\n deviceId: string,\r\n fingerprint: string,\r\n ): Promise<VerifyDeviceKeyResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/verify-key`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send an encrypted SMS (payload already encrypted with device public key) */\r\n async sendEncryptedSms(\r\n encryptedPayload: string,\r\n deviceId: string,\r\n is2FA: boolean = false,\r\n ): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\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":[]}
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 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// --- E2E Encryption ---\r\n\r\nexport interface DeviceE2EStatusResponse {\r\n success: boolean;\r\n data?: {\r\n encryptionEnabled: boolean;\r\n hasPublicKey: boolean;\r\n publicKeyFingerprint?: string;\r\n keyCreatedAt?: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface DevicePublicKeyResponse {\r\n success: boolean;\r\n data?: {\r\n deviceId: string;\r\n deviceName: string;\r\n publicKey: string;\r\n };\r\n error?: string;\r\n}\r\n\r\nexport interface VerifyDeviceKeyResponse {\r\n valid: boolean;\r\n currentFingerprint?: string;\r\n needsRePairing: boolean;\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 = 'smstunnel';\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.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 // ─── E2E Encryption ────────────────────────────────\r\n\r\n /** Get E2E encryption status for a device */\r\n async getDeviceE2EStatus(deviceId: string): Promise<DeviceE2EStatusResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/e2e-status/${deviceId}`,\r\n { headers: this.getAuthHeaders() },\r\n );\r\n return res.json();\r\n }\r\n\r\n /** Get device public key for E2E encryption */\r\n async getDevicePublicKey(deviceId: string): Promise<DevicePublicKeyResponse> {\r\n const res = await fetch(\r\n `${this.baseUrl}/${this.prefix}/public-key/${deviceId}`,\r\n { headers: this.getAuthHeaders() },\r\n );\r\n return res.json();\r\n }\r\n\r\n /** Verify that a stored fingerprint matches the current device key */\r\n async verifyDeviceKey(\r\n deviceId: string,\r\n fingerprint: string,\r\n ): Promise<VerifyDeviceKeyResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/verify-key`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return res.json();\r\n }\r\n\r\n /** Send an encrypted SMS (payload already encrypted with device public key) */\r\n async sendEncryptedSms(\r\n encryptedPayload: string,\r\n deviceId: string,\r\n is2FA: boolean = false,\r\n ): Promise<SendSmsResponse> {\r\n const res = await fetch(`${this.baseUrl}/${this.prefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...this.getAuthHeaders(),\r\n },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\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":[]}
@@ -38,13 +38,11 @@ interface SmsTunnelPairingProps {
38
38
  showTestSms?: boolean;
39
39
  showServerUrlInput?: boolean;
40
40
  qrSize?: number;
41
- routePrefix?: string;
42
41
  className?: string;
43
42
  }
44
43
  interface UseSmsTunnelOptions {
45
44
  apiBaseUrl: string;
46
45
  getAuthHeaders?: () => Record<string, string>;
47
- routePrefix?: string;
48
46
  pollInterval?: number;
49
47
  }
50
48
  interface SmsTunnelState {
@@ -58,7 +56,7 @@ interface SmsTunnelState {
58
56
  error: string;
59
57
  }
60
58
 
61
- declare function SmsTunnelPairing({ apiBaseUrl, getAuthHeaders, labels, onPaired, onUnpaired, showTestSms, showServerUrlInput, qrSize, routePrefix, className, }: SmsTunnelPairingProps): react_jsx_runtime.JSX.Element;
59
+ declare function SmsTunnelPairing({ apiBaseUrl, getAuthHeaders, labels, onPaired, onUnpaired, showTestSms, showServerUrlInput, qrSize, className, }: SmsTunnelPairingProps): react_jsx_runtime.JSX.Element;
62
60
 
63
61
  interface QrCodeCanvasProps {
64
62
  value: string;
@@ -38,13 +38,11 @@ interface SmsTunnelPairingProps {
38
38
  showTestSms?: boolean;
39
39
  showServerUrlInput?: boolean;
40
40
  qrSize?: number;
41
- routePrefix?: string;
42
41
  className?: string;
43
42
  }
44
43
  interface UseSmsTunnelOptions {
45
44
  apiBaseUrl: string;
46
45
  getAuthHeaders?: () => Record<string, string>;
47
- routePrefix?: string;
48
46
  pollInterval?: number;
49
47
  }
50
48
  interface SmsTunnelState {
@@ -58,7 +56,7 @@ interface SmsTunnelState {
58
56
  error: string;
59
57
  }
60
58
 
61
- declare function SmsTunnelPairing({ apiBaseUrl, getAuthHeaders, labels, onPaired, onUnpaired, showTestSms, showServerUrlInput, qrSize, routePrefix, className, }: SmsTunnelPairingProps): react_jsx_runtime.JSX.Element;
59
+ declare function SmsTunnelPairing({ apiBaseUrl, getAuthHeaders, labels, onPaired, onUnpaired, showTestSms, showServerUrlInput, qrSize, className, }: SmsTunnelPairingProps): react_jsx_runtime.JSX.Element;
62
60
 
63
61
  interface QrCodeCanvasProps {
64
62
  value: string;
@@ -44,7 +44,8 @@ var import_react3 = require("react");
44
44
  // src/react/useSmsTunnel.ts
45
45
  var import_react = require("react");
46
46
  function useSmsTunnel(options) {
47
- const { apiBaseUrl, getAuthHeaders, routePrefix = "smstunnel", pollInterval = 3e3 } = options;
47
+ const { apiBaseUrl, getAuthHeaders, pollInterval = 3e3 } = options;
48
+ const routePrefix = "smstunnel";
48
49
  const [state, setState] = (0, import_react.useState)({
49
50
  status: "loading",
50
51
  serverUrl: "",
@@ -529,11 +530,10 @@ function SmsTunnelPairing({
529
530
  showTestSms = true,
530
531
  showServerUrlInput = true,
531
532
  qrSize = 220,
532
- routePrefix = "smstunnel",
533
533
  className
534
534
  }) {
535
535
  injectKeyframes();
536
- const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders, routePrefix });
536
+ const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders });
537
537
  const [localServerUrl, setLocalServerUrl] = (0, import_react3.useState)("");
538
538
  const [savingUrl, setSavingUrl] = (0, import_react3.useState)(false);
539
539
  const [testPhone, setTestPhone] = (0, import_react3.useState)("");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts","../../src/react/SmsTunnelPairing.tsx","../../src/react/useSmsTunnel.ts","../../src/react/QrCodeCanvas.tsx","../../src/shared/labels.ts"],"sourcesContent":["export { SmsTunnelPairing } from './SmsTunnelPairing';\r\nexport { QrCodeCanvas } from './QrCodeCanvas';\r\nexport { useSmsTunnel } from './useSmsTunnel';\r\nexport { EN_LABELS, RO_LABELS } from './types';\r\nexport type {\r\n SmsTunnelLabels,\r\n SmsTunnelPairingProps,\r\n UseSmsTunnelOptions,\r\n SmsTunnelState,\r\n} from './types';\r\nexport type { QrCodeCanvasProps } from './QrCodeCanvas';\r\n","import { useState } from 'react';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS } from './types';\r\nimport type { SmsTunnelPairingProps } from './types';\r\n\r\n// Inline styles - no external CSS dependency\r\nconst styles = {\r\n container: {\r\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n color: '#1f2937',\r\n } as React.CSSProperties,\r\n card: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '12px',\r\n padding: '24px',\r\n backgroundColor: '#ffffff',\r\n } as React.CSSProperties,\r\n title: {\r\n fontSize: '18px',\r\n fontWeight: 600,\r\n color: '#111827',\r\n marginBottom: '4px',\r\n } as React.CSSProperties,\r\n subtitle: {\r\n fontSize: '13px',\r\n color: '#6b7280',\r\n marginBottom: '20px',\r\n } as React.CSSProperties,\r\n section: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n label: {\r\n display: 'block',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n color: '#374151',\r\n marginBottom: '6px',\r\n } as React.CSSProperties,\r\n inputRow: {\r\n display: 'flex',\r\n gap: '8px',\r\n } as React.CSSProperties,\r\n input: {\r\n flex: 1,\r\n border: '1px solid #d1d5db',\r\n borderRadius: '8px',\r\n padding: '6px 12px',\r\n fontSize: '13px',\r\n outline: 'none',\r\n } as React.CSSProperties,\r\n button: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#4f46e5',\r\n color: '#ffffff',\r\n transition: 'opacity 0.2s',\r\n } as React.CSSProperties,\r\n buttonSecondary: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: '1px solid #d1d5db',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#ffffff',\r\n color: '#374151',\r\n } as React.CSSProperties,\r\n buttonDanger: {\r\n padding: '6px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n cursor: 'pointer',\r\n backgroundColor: 'transparent',\r\n color: '#9ca3af',\r\n transition: 'color 0.2s',\r\n } as React.CSSProperties,\r\n pairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #bbf7d0',\r\n backgroundColor: '#f0fdf4',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n pairedIcon: {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: '#dcfce7',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#16a34a',\r\n fontSize: '18px',\r\n flexShrink: 0,\r\n } as React.CSSProperties,\r\n unpairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #fde68a',\r\n backgroundColor: '#fefce8',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n qrContainer: {\r\n textAlign: 'center' as const,\r\n padding: '16px',\r\n } as React.CSSProperties,\r\n qrFrame: {\r\n display: 'inline-block',\r\n border: '2px dashed #a5b4fc',\r\n borderRadius: '12px',\r\n padding: '16px',\r\n backgroundColor: '#ffffff',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n spinner: {\r\n display: 'inline-block',\r\n width: '12px',\r\n height: '12px',\r\n border: '2px solid #d1d5db',\r\n borderTopColor: '#4f46e5',\r\n borderRadius: '50%',\r\n animation: 'smstunnel-spin 0.8s linear infinite',\r\n marginRight: '8px',\r\n verticalAlign: 'middle',\r\n } as React.CSSProperties,\r\n error: {\r\n color: '#dc2626',\r\n fontSize: '13px',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n success: {\r\n color: '#16a34a',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n errorSmall: {\r\n color: '#dc2626',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n};\r\n\r\n// Inject keyframes once\r\nlet stylesInjected = false;\r\nfunction injectKeyframes() {\r\n if (stylesInjected || typeof document === 'undefined') return;\r\n const style = document.createElement('style');\r\n style.textContent = `@keyframes smstunnel-spin { to { transform: rotate(360deg); } }`;\r\n document.head.appendChild(style);\r\n stylesInjected = true;\r\n}\r\n\r\nexport function SmsTunnelPairing({\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n labels = EN_LABELS,\r\n onPaired,\r\n onUnpaired,\r\n showTestSms = true,\r\n showServerUrlInput = true,\r\n qrSize = 220,\r\n routePrefix = 'smstunnel',\r\n className,\r\n}: SmsTunnelPairingProps) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders, routePrefix });\r\n\r\n const [localServerUrl, setLocalServerUrl] = useState('');\r\n const [savingUrl, setSavingUrl] = useState(false);\r\n const [testPhone, setTestPhone] = useState('');\r\n const [testMsg, setTestMsg] = useState('Test SMS');\r\n const [testResult, setTestResult] = useState<{\r\n success: boolean;\r\n error?: string;\r\n messageId?: string;\r\n } | null>(null);\r\n const [sendingTest, setSendingTest] = useState(false);\r\n\r\n // Sync server URL once loaded\r\n if (tunnel.serverUrl && !localServerUrl) {\r\n setLocalServerUrl(tunnel.serverUrl);\r\n }\r\n\r\n const handleSaveUrl = async () => {\r\n if (!localServerUrl.trim()) return;\r\n setSavingUrl(true);\r\n await tunnel.updateServerUrl(localServerUrl.trim().replace(/\\/$/, ''));\r\n setSavingUrl(false);\r\n };\r\n\r\n const handleUnpair = async () => {\r\n if (!confirm(labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n onUnpaired?.();\r\n };\r\n\r\n const handleGenerateQr = async () => {\r\n await tunnel.generateQr();\r\n };\r\n\r\n // Watch for pairing completion\r\n const prevStatus = tunnel.status;\r\n if (prevStatus === 'paired' && tunnel.deviceName) {\r\n // Fire onPaired if status changed\r\n }\r\n\r\n const handleTestSms = async () => {\r\n if (!testPhone.trim()) return;\r\n setSendingTest(true);\r\n setTestResult(null);\r\n const result = await tunnel.sendTestSms(testPhone.trim(), testMsg);\r\n setTestResult(result);\r\n setSendingTest(false);\r\n };\r\n\r\n if (tunnel.status === 'loading') {\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={{ textAlign: 'center', padding: '24px' }}>\r\n <span style={styles.spinner} />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={styles.title}>{labels.title}</div>\r\n <div style={styles.subtitle}>{labels.description}</div>\r\n\r\n {/* Server URL */}\r\n {showServerUrlInput && (\r\n <div style={styles.section}>\r\n <label style={styles.label}>{labels.serverUrlLabel}</label>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={localServerUrl}\r\n onChange={(e) => setLocalServerUrl(e.target.value)}\r\n placeholder={labels.serverUrlPlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: savingUrl || !localServerUrl.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleSaveUrl}\r\n disabled={savingUrl || !localServerUrl.trim()}\r\n >\r\n {labels.saveButton}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {tunnel.error && <div style={styles.error}>{tunnel.error}</div>}\r\n\r\n {tunnel.status === 'paired' ? (\r\n <div>\r\n {/* Paired banner */}\r\n <div style={styles.pairedBanner}>\r\n <div style={styles.pairedIcon}>&#10003;</div>\r\n <div style={{ flex: 1 }}>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#166534' }}>\r\n {labels.pairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#16a34a' }}>\r\n {labels.deviceLabel}: {tunnel.deviceName || 'Android Phone'}\r\n </div>\r\n </div>\r\n <button\r\n onClick={handleUnpair}\r\n style={styles.buttonDanger}\r\n title={labels.unpairButton}\r\n >\r\n &#10005;\r\n </button>\r\n </div>\r\n\r\n {/* Test SMS */}\r\n {showTestSms && (\r\n <div style={styles.section}>\r\n <div\r\n style={{\r\n fontSize: '13px',\r\n fontWeight: 600,\r\n color: '#374151',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n {labels.testSmsTitle}\r\n </div>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={testPhone}\r\n onChange={(e) => setTestPhone(e.target.value)}\r\n placeholder={labels.testPhonePlaceholder}\r\n style={styles.input}\r\n />\r\n <input\r\n value={testMsg}\r\n onChange={(e) => setTestMsg(e.target.value)}\r\n placeholder={labels.testMessagePlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: sendingTest || !testPhone.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleTestSms}\r\n disabled={sendingTest || !testPhone.trim()}\r\n >\r\n {sendingTest ? (\r\n <span style={styles.spinner} />\r\n ) : (\r\n labels.sendTestButton\r\n )}\r\n </button>\r\n </div>\r\n {testResult && (\r\n <div style={testResult.success ? styles.success : styles.errorSmall}>\r\n {testResult.success\r\n ? `${labels.smsSentSuccess} (ID: ${testResult.messageId})`\r\n : `${labels.smsError}: ${testResult.error}`}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div>\r\n {/* Not paired banner */}\r\n <div style={styles.unpairedBanner}>\r\n <div style={{ fontSize: '24px' }}>&#128241;</div>\r\n <div>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#854d0e' }}>\r\n {labels.notPairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#a16207' }}>\r\n {labels.notPairedDescription}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {!tunnel.showQr ? (\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: tunnel.generating ? 0.5 : 1,\r\n }}\r\n onClick={handleGenerateQr}\r\n disabled={tunnel.generating}\r\n >\r\n {tunnel.generating && <span style={styles.spinner} />}\r\n {labels.connectButton}\r\n </button>\r\n ) : (\r\n <div style={styles.qrContainer}>\r\n <div style={{ fontSize: '13px', color: '#4b5563', marginBottom: '12px' }}>\r\n {labels.scanQrPrompt}\r\n </div>\r\n <div style={styles.qrFrame}>\r\n <QrCodeCanvas value={tunnel.qrData} size={qrSize} />\r\n </div>\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '8px',\r\n fontSize: '12px',\r\n color: '#9ca3af',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n <span style={styles.spinner} />\r\n {labels.waitingForPairing}\r\n </div>\r\n <button style={styles.buttonSecondary} onClick={tunnel.cancelPairing}>\r\n {labels.cancelButton}\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react';\r\nimport type { SmsTunnelState, UseSmsTunnelOptions } from './types';\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const { apiBaseUrl, getAuthHeaders, routePrefix = 'smstunnel', pollInterval = 3000 } = options;\r\n\r\n const [state, setState] = useState<SmsTunnelState>({\r\n status: 'loading',\r\n serverUrl: '',\r\n deviceName: '',\r\n showQr: false,\r\n qrData: '',\r\n generating: false,\r\n polling: false,\r\n error: '',\r\n });\r\n\r\n const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n const pairingTokenRef = useRef<string>('');\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const authHeaders = useCallback((): Record<string, string> => {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }, [getAuthHeaders]);\r\n\r\n const fetchStatus = useCallback(async () => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/status`, {\r\n headers: authHeaders(),\r\n });\r\n const data = await res.json();\r\n setState((prev) => ({\r\n ...prev,\r\n status: data.paired ? 'paired' : 'unpaired',\r\n serverUrl: data.serverUrl || '',\r\n deviceName: data.deviceName || '',\r\n }));\r\n } catch {\r\n setState((prev) => ({ ...prev, status: 'unpaired' }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n useEffect(() => {\r\n fetchStatus();\r\n }, [fetchStatus]);\r\n\r\n // Polling effect\r\n useEffect(() => {\r\n if (!state.polling || !pairingTokenRef.current) return;\r\n\r\n pollRef.current = setInterval(async () => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingTokenRef.current}`,\r\n );\r\n const data = await res.json();\r\n\r\n if (data.status === 'completed') {\r\n setState((prev) => ({ ...prev, polling: false, showQr: false }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n // Wait for callback to arrive, then refetch\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n setState((prev) => ({\r\n ...prev,\r\n polling: false,\r\n showQr: false,\r\n error: 'QR code expired',\r\n }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n }\r\n } catch {\r\n // ignore polling errors\r\n }\r\n }, pollInterval);\r\n\r\n return () => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n };\r\n }, [state.polling, baseUrl, routePrefix, pollInterval, fetchStatus]);\r\n\r\n const generateQr = useCallback(async () => {\r\n setState((prev) => ({ ...prev, generating: true, error: '' }));\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n });\r\n const data = await res.json();\r\n\r\n if (!data.success) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: data.error || 'Failed to create QR code',\r\n }));\r\n return;\r\n }\r\n\r\n pairingTokenRef.current = data.token;\r\n setState((prev) => ({\r\n ...prev,\r\n qrData: data.qrData,\r\n showQr: true,\r\n polling: true,\r\n generating: false,\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: `Error: ${err.message}`,\r\n }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const cancelPairing = useCallback(() => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n pairingTokenRef.current = '';\r\n setState((prev) => ({\r\n ...prev,\r\n showQr: false,\r\n polling: false,\r\n qrData: '',\r\n }));\r\n }, []);\r\n\r\n const unpair = useCallback(async () => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n setState((prev) => ({\r\n ...prev,\r\n status: 'unpaired',\r\n deviceName: '',\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Unpair failed: ${err.message}` }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const sendTestSms = useCallback(\r\n async (to: string, message: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return (await res.json()) as { success: boolean; messageId?: string; error?: string };\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const updateServerUrl = useCallback(\r\n async (serverUrl: string) => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n setState((prev) => ({ ...prev, serverUrl }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Update failed: ${err.message}` }));\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const refetch = useCallback(() => fetchStatus(), [fetchStatus]);\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n const getDeviceE2EStatus = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const getDevicePublicKey = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/public-key/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const verifyDeviceKey = useCallback(\r\n async (deviceId: string, fingerprint: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { valid: false, needsRePairing: true, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const sendEncryptedSms = useCallback(\r\n async (encryptedPayload: string, deviceId: string, is2FA: boolean = false) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n return {\r\n ...state,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch,\r\n // E2E\r\n getDeviceE2EStatus,\r\n getDevicePublicKey,\r\n verifyDeviceKey,\r\n sendEncryptedSms,\r\n };\r\n}\r\n","import { useEffect, useRef } from 'react';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport interface QrCodeCanvasProps {\r\n value: string;\r\n size?: number;\r\n}\r\n\r\nexport function QrCodeCanvas({ value, size = 200 }: QrCodeCanvasProps) {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n\r\n useEffect(() => {\r\n if (!canvasRef.current || !value) return;\r\n\r\n const canvas = canvasRef.current;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const qr = qrcode(0, 'M');\r\n qr.addData(value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = size / moduleCount;\r\n\r\n canvas.width = size;\r\n canvas.height = size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, size, size);\r\n ctx.fillStyle = '#000000';\r\n\r\n for (let row = 0; row < moduleCount; row++) {\r\n for (let col = 0; col < moduleCount; col++) {\r\n if (qr.isDark(row, col)) {\r\n ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);\r\n }\r\n }\r\n }\r\n }, [value, size]);\r\n\r\n return <canvas ref={canvasRef} width={size} height={size} />;\r\n}\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"],"mappings":"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","names":["import_react","import_react","qrcode","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/SmsTunnelPairing.tsx","../../src/react/useSmsTunnel.ts","../../src/react/QrCodeCanvas.tsx","../../src/shared/labels.ts"],"sourcesContent":["export { SmsTunnelPairing } from './SmsTunnelPairing';\r\nexport { QrCodeCanvas } from './QrCodeCanvas';\r\nexport { useSmsTunnel } from './useSmsTunnel';\r\nexport { EN_LABELS, RO_LABELS } from './types';\r\nexport type {\r\n SmsTunnelLabels,\r\n SmsTunnelPairingProps,\r\n UseSmsTunnelOptions,\r\n SmsTunnelState,\r\n} from './types';\r\nexport type { QrCodeCanvasProps } from './QrCodeCanvas';\r\n","import { useState } from 'react';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS } from './types';\r\nimport type { SmsTunnelPairingProps } from './types';\r\n\r\n// Inline styles - no external CSS dependency\r\nconst styles = {\r\n container: {\r\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n color: '#1f2937',\r\n } as React.CSSProperties,\r\n card: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '12px',\r\n padding: '24px',\r\n backgroundColor: '#ffffff',\r\n } as React.CSSProperties,\r\n title: {\r\n fontSize: '18px',\r\n fontWeight: 600,\r\n color: '#111827',\r\n marginBottom: '4px',\r\n } as React.CSSProperties,\r\n subtitle: {\r\n fontSize: '13px',\r\n color: '#6b7280',\r\n marginBottom: '20px',\r\n } as React.CSSProperties,\r\n section: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n label: {\r\n display: 'block',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n color: '#374151',\r\n marginBottom: '6px',\r\n } as React.CSSProperties,\r\n inputRow: {\r\n display: 'flex',\r\n gap: '8px',\r\n } as React.CSSProperties,\r\n input: {\r\n flex: 1,\r\n border: '1px solid #d1d5db',\r\n borderRadius: '8px',\r\n padding: '6px 12px',\r\n fontSize: '13px',\r\n outline: 'none',\r\n } as React.CSSProperties,\r\n button: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#4f46e5',\r\n color: '#ffffff',\r\n transition: 'opacity 0.2s',\r\n } as React.CSSProperties,\r\n buttonSecondary: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: '1px solid #d1d5db',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#ffffff',\r\n color: '#374151',\r\n } as React.CSSProperties,\r\n buttonDanger: {\r\n padding: '6px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n cursor: 'pointer',\r\n backgroundColor: 'transparent',\r\n color: '#9ca3af',\r\n transition: 'color 0.2s',\r\n } as React.CSSProperties,\r\n pairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #bbf7d0',\r\n backgroundColor: '#f0fdf4',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n pairedIcon: {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: '#dcfce7',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#16a34a',\r\n fontSize: '18px',\r\n flexShrink: 0,\r\n } as React.CSSProperties,\r\n unpairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #fde68a',\r\n backgroundColor: '#fefce8',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n qrContainer: {\r\n textAlign: 'center' as const,\r\n padding: '16px',\r\n } as React.CSSProperties,\r\n qrFrame: {\r\n display: 'inline-block',\r\n border: '2px dashed #a5b4fc',\r\n borderRadius: '12px',\r\n padding: '16px',\r\n backgroundColor: '#ffffff',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n spinner: {\r\n display: 'inline-block',\r\n width: '12px',\r\n height: '12px',\r\n border: '2px solid #d1d5db',\r\n borderTopColor: '#4f46e5',\r\n borderRadius: '50%',\r\n animation: 'smstunnel-spin 0.8s linear infinite',\r\n marginRight: '8px',\r\n verticalAlign: 'middle',\r\n } as React.CSSProperties,\r\n error: {\r\n color: '#dc2626',\r\n fontSize: '13px',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n success: {\r\n color: '#16a34a',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n errorSmall: {\r\n color: '#dc2626',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n};\r\n\r\n// Inject keyframes once\r\nlet stylesInjected = false;\r\nfunction injectKeyframes() {\r\n if (stylesInjected || typeof document === 'undefined') return;\r\n const style = document.createElement('style');\r\n style.textContent = `@keyframes smstunnel-spin { to { transform: rotate(360deg); } }`;\r\n document.head.appendChild(style);\r\n stylesInjected = true;\r\n}\r\n\r\nexport function SmsTunnelPairing({\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n labels = EN_LABELS,\r\n onPaired,\r\n onUnpaired,\r\n showTestSms = true,\r\n showServerUrlInput = true,\r\n qrSize = 220,\r\n className,\r\n}: SmsTunnelPairingProps) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders });\r\n\r\n const [localServerUrl, setLocalServerUrl] = useState('');\r\n const [savingUrl, setSavingUrl] = useState(false);\r\n const [testPhone, setTestPhone] = useState('');\r\n const [testMsg, setTestMsg] = useState('Test SMS');\r\n const [testResult, setTestResult] = useState<{\r\n success: boolean;\r\n error?: string;\r\n messageId?: string;\r\n } | null>(null);\r\n const [sendingTest, setSendingTest] = useState(false);\r\n\r\n // Sync server URL once loaded\r\n if (tunnel.serverUrl && !localServerUrl) {\r\n setLocalServerUrl(tunnel.serverUrl);\r\n }\r\n\r\n const handleSaveUrl = async () => {\r\n if (!localServerUrl.trim()) return;\r\n setSavingUrl(true);\r\n await tunnel.updateServerUrl(localServerUrl.trim().replace(/\\/$/, ''));\r\n setSavingUrl(false);\r\n };\r\n\r\n const handleUnpair = async () => {\r\n if (!confirm(labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n onUnpaired?.();\r\n };\r\n\r\n const handleGenerateQr = async () => {\r\n await tunnel.generateQr();\r\n };\r\n\r\n // Watch for pairing completion\r\n const prevStatus = tunnel.status;\r\n if (prevStatus === 'paired' && tunnel.deviceName) {\r\n // Fire onPaired if status changed\r\n }\r\n\r\n const handleTestSms = async () => {\r\n if (!testPhone.trim()) return;\r\n setSendingTest(true);\r\n setTestResult(null);\r\n const result = await tunnel.sendTestSms(testPhone.trim(), testMsg);\r\n setTestResult(result);\r\n setSendingTest(false);\r\n };\r\n\r\n if (tunnel.status === 'loading') {\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={{ textAlign: 'center', padding: '24px' }}>\r\n <span style={styles.spinner} />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={styles.title}>{labels.title}</div>\r\n <div style={styles.subtitle}>{labels.description}</div>\r\n\r\n {/* Server URL */}\r\n {showServerUrlInput && (\r\n <div style={styles.section}>\r\n <label style={styles.label}>{labels.serverUrlLabel}</label>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={localServerUrl}\r\n onChange={(e) => setLocalServerUrl(e.target.value)}\r\n placeholder={labels.serverUrlPlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: savingUrl || !localServerUrl.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleSaveUrl}\r\n disabled={savingUrl || !localServerUrl.trim()}\r\n >\r\n {labels.saveButton}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {tunnel.error && <div style={styles.error}>{tunnel.error}</div>}\r\n\r\n {tunnel.status === 'paired' ? (\r\n <div>\r\n {/* Paired banner */}\r\n <div style={styles.pairedBanner}>\r\n <div style={styles.pairedIcon}>&#10003;</div>\r\n <div style={{ flex: 1 }}>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#166534' }}>\r\n {labels.pairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#16a34a' }}>\r\n {labels.deviceLabel}: {tunnel.deviceName || 'Android Phone'}\r\n </div>\r\n </div>\r\n <button\r\n onClick={handleUnpair}\r\n style={styles.buttonDanger}\r\n title={labels.unpairButton}\r\n >\r\n &#10005;\r\n </button>\r\n </div>\r\n\r\n {/* Test SMS */}\r\n {showTestSms && (\r\n <div style={styles.section}>\r\n <div\r\n style={{\r\n fontSize: '13px',\r\n fontWeight: 600,\r\n color: '#374151',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n {labels.testSmsTitle}\r\n </div>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={testPhone}\r\n onChange={(e) => setTestPhone(e.target.value)}\r\n placeholder={labels.testPhonePlaceholder}\r\n style={styles.input}\r\n />\r\n <input\r\n value={testMsg}\r\n onChange={(e) => setTestMsg(e.target.value)}\r\n placeholder={labels.testMessagePlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: sendingTest || !testPhone.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleTestSms}\r\n disabled={sendingTest || !testPhone.trim()}\r\n >\r\n {sendingTest ? (\r\n <span style={styles.spinner} />\r\n ) : (\r\n labels.sendTestButton\r\n )}\r\n </button>\r\n </div>\r\n {testResult && (\r\n <div style={testResult.success ? styles.success : styles.errorSmall}>\r\n {testResult.success\r\n ? `${labels.smsSentSuccess} (ID: ${testResult.messageId})`\r\n : `${labels.smsError}: ${testResult.error}`}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div>\r\n {/* Not paired banner */}\r\n <div style={styles.unpairedBanner}>\r\n <div style={{ fontSize: '24px' }}>&#128241;</div>\r\n <div>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#854d0e' }}>\r\n {labels.notPairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#a16207' }}>\r\n {labels.notPairedDescription}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {!tunnel.showQr ? (\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: tunnel.generating ? 0.5 : 1,\r\n }}\r\n onClick={handleGenerateQr}\r\n disabled={tunnel.generating}\r\n >\r\n {tunnel.generating && <span style={styles.spinner} />}\r\n {labels.connectButton}\r\n </button>\r\n ) : (\r\n <div style={styles.qrContainer}>\r\n <div style={{ fontSize: '13px', color: '#4b5563', marginBottom: '12px' }}>\r\n {labels.scanQrPrompt}\r\n </div>\r\n <div style={styles.qrFrame}>\r\n <QrCodeCanvas value={tunnel.qrData} size={qrSize} />\r\n </div>\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '8px',\r\n fontSize: '12px',\r\n color: '#9ca3af',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n <span style={styles.spinner} />\r\n {labels.waitingForPairing}\r\n </div>\r\n <button style={styles.buttonSecondary} onClick={tunnel.cancelPairing}>\r\n {labels.cancelButton}\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react';\r\nimport type { SmsTunnelState, UseSmsTunnelOptions } from './types';\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const { apiBaseUrl, getAuthHeaders, pollInterval = 3000 } = options;\r\n const routePrefix = 'smstunnel';\r\n\r\n const [state, setState] = useState<SmsTunnelState>({\r\n status: 'loading',\r\n serverUrl: '',\r\n deviceName: '',\r\n showQr: false,\r\n qrData: '',\r\n generating: false,\r\n polling: false,\r\n error: '',\r\n });\r\n\r\n const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n const pairingTokenRef = useRef<string>('');\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const authHeaders = useCallback((): Record<string, string> => {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }, [getAuthHeaders]);\r\n\r\n const fetchStatus = useCallback(async () => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/status`, {\r\n headers: authHeaders(),\r\n });\r\n const data = await res.json();\r\n setState((prev) => ({\r\n ...prev,\r\n status: data.paired ? 'paired' : 'unpaired',\r\n serverUrl: data.serverUrl || '',\r\n deviceName: data.deviceName || '',\r\n }));\r\n } catch {\r\n setState((prev) => ({ ...prev, status: 'unpaired' }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n useEffect(() => {\r\n fetchStatus();\r\n }, [fetchStatus]);\r\n\r\n // Polling effect\r\n useEffect(() => {\r\n if (!state.polling || !pairingTokenRef.current) return;\r\n\r\n pollRef.current = setInterval(async () => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingTokenRef.current}`,\r\n );\r\n const data = await res.json();\r\n\r\n if (data.status === 'completed') {\r\n setState((prev) => ({ ...prev, polling: false, showQr: false }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n // Wait for callback to arrive, then refetch\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n setState((prev) => ({\r\n ...prev,\r\n polling: false,\r\n showQr: false,\r\n error: 'QR code expired',\r\n }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n }\r\n } catch {\r\n // ignore polling errors\r\n }\r\n }, pollInterval);\r\n\r\n return () => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n };\r\n }, [state.polling, baseUrl, routePrefix, pollInterval, fetchStatus]);\r\n\r\n const generateQr = useCallback(async () => {\r\n setState((prev) => ({ ...prev, generating: true, error: '' }));\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n });\r\n const data = await res.json();\r\n\r\n if (!data.success) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: data.error || 'Failed to create QR code',\r\n }));\r\n return;\r\n }\r\n\r\n pairingTokenRef.current = data.token;\r\n setState((prev) => ({\r\n ...prev,\r\n qrData: data.qrData,\r\n showQr: true,\r\n polling: true,\r\n generating: false,\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: `Error: ${err.message}`,\r\n }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const cancelPairing = useCallback(() => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n pairingTokenRef.current = '';\r\n setState((prev) => ({\r\n ...prev,\r\n showQr: false,\r\n polling: false,\r\n qrData: '',\r\n }));\r\n }, []);\r\n\r\n const unpair = useCallback(async () => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n setState((prev) => ({\r\n ...prev,\r\n status: 'unpaired',\r\n deviceName: '',\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Unpair failed: ${err.message}` }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const sendTestSms = useCallback(\r\n async (to: string, message: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return (await res.json()) as { success: boolean; messageId?: string; error?: string };\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const updateServerUrl = useCallback(\r\n async (serverUrl: string) => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n setState((prev) => ({ ...prev, serverUrl }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Update failed: ${err.message}` }));\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const refetch = useCallback(() => fetchStatus(), [fetchStatus]);\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n const getDeviceE2EStatus = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const getDevicePublicKey = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/public-key/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const verifyDeviceKey = useCallback(\r\n async (deviceId: string, fingerprint: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { valid: false, needsRePairing: true, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const sendEncryptedSms = useCallback(\r\n async (encryptedPayload: string, deviceId: string, is2FA: boolean = false) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n return {\r\n ...state,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch,\r\n // E2E\r\n getDeviceE2EStatus,\r\n getDevicePublicKey,\r\n verifyDeviceKey,\r\n sendEncryptedSms,\r\n };\r\n}\r\n","import { useEffect, useRef } from 'react';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport interface QrCodeCanvasProps {\r\n value: string;\r\n size?: number;\r\n}\r\n\r\nexport function QrCodeCanvas({ value, size = 200 }: QrCodeCanvasProps) {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n\r\n useEffect(() => {\r\n if (!canvasRef.current || !value) return;\r\n\r\n const canvas = canvasRef.current;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const qr = qrcode(0, 'M');\r\n qr.addData(value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = size / moduleCount;\r\n\r\n canvas.width = size;\r\n canvas.height = size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, size, size);\r\n ctx.fillStyle = '#000000';\r\n\r\n for (let row = 0; row < moduleCount; row++) {\r\n for (let col = 0; col < moduleCount; col++) {\r\n if (qr.isDark(row, col)) {\r\n ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);\r\n }\r\n }\r\n }\r\n }, [value, size]);\r\n\r\n return <canvas ref={canvasRef} width={size} height={size} />;\r\n}\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"],"mappings":"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","names":["import_react","import_react","qrcode","import_jsx_runtime"]}
@@ -4,7 +4,8 @@ import { useState as useState2 } from "react";
4
4
  // src/react/useSmsTunnel.ts
5
5
  import { useState, useEffect, useRef, useCallback } from "react";
6
6
  function useSmsTunnel(options) {
7
- const { apiBaseUrl, getAuthHeaders, routePrefix = "smstunnel", pollInterval = 3e3 } = options;
7
+ const { apiBaseUrl, getAuthHeaders, pollInterval = 3e3 } = options;
8
+ const routePrefix = "smstunnel";
8
9
  const [state, setState] = useState({
9
10
  status: "loading",
10
11
  serverUrl: "",
@@ -489,11 +490,10 @@ function SmsTunnelPairing({
489
490
  showTestSms = true,
490
491
  showServerUrlInput = true,
491
492
  qrSize = 220,
492
- routePrefix = "smstunnel",
493
493
  className
494
494
  }) {
495
495
  injectKeyframes();
496
- const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders, routePrefix });
496
+ const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders });
497
497
  const [localServerUrl, setLocalServerUrl] = useState2("");
498
498
  const [savingUrl, setSavingUrl] = useState2(false);
499
499
  const [testPhone, setTestPhone] = useState2("");