UNPKG

expo-realtime-maps-navigation

Version:

JavaScript-pure React Native navigation package with Google Places + HERE Routing APIs, dual maps support, and complete customization - No native modules required!

291 lines (290 loc) 11.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityUtils = exports.useSecureStorage = exports.HereSecureStorage = exports.secureStorage = void 0; const react_native_1 = require("react-native"); class SecureStorageManager { constructor() { this.keychain = null; this.isAvailable = false; this.initialize(); } static getInstance() { if (!SecureStorageManager.instance) { SecureStorageManager.instance = new SecureStorageManager(); } return SecureStorageManager.instance; } async initialize() { try { if (react_native_1.Platform.OS === 'ios' || react_native_1.Platform.OS === 'android') { const Keychain = await Promise.resolve().then(() => __importStar(require('react-native-keychain'))); this.keychain = Keychain.default || Keychain; this.isAvailable = true; } else { this.isAvailable = typeof window !== 'undefined' && !!window.localStorage; } } catch (error) { console.warn('SecureStorage: Keychain not available, falling back to basic storage'); this.isAvailable = false; } } async isSecureStorageAvailable() { if (!this.isAvailable) { await this.initialize(); } return this.isAvailable; } async storeCredentials(service, credentials, options) { try { const entry = { value: JSON.stringify(credentials), timestamp: Date.now(), expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, }; if (this.keychain) { const result = await this.keychain.setInternetCredentials(service, credentials.apiKey, JSON.stringify(entry), { accessControl: this.keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE, authenticationType: this.keychain.AUTHENTICATION_TYPE.DEVICE_PASSCODE_OR_BIOMETRICS, accessGroup: options?.accessGroup, ...options, }); return result !== false; } else if (react_native_1.Platform.OS === 'web') { const encrypted = await this.encryptForWeb(JSON.stringify(entry)); localStorage.setItem(`secure_${service}`, encrypted); return true; } else { throw new Error('Secure storage not available'); } } catch (error) { console.error('SecureStorage: Failed to store credentials:', error); return false; } } async getCredentials(service, options) { try { let storedData = null; if (this.keychain) { const result = await this.keychain.getInternetCredentials(service, { authenticationPrompt: { title: 'Access HERE Navigation', subtitle: 'Authenticate to access navigation services', description: 'This app uses HERE SDK for navigation', fallbackLabel: 'Use passcode', cancelLabel: 'Cancel', ...options, }, }); if (result && result.password) { storedData = result.password; } } else if (react_native_1.Platform.OS === 'web') { const encrypted = localStorage.getItem(`secure_${service}`); if (encrypted) { storedData = await this.decryptForWeb(encrypted); } } if (!storedData) { return null; } const entry = JSON.parse(storedData); if (entry.expiresAt && Date.now() > entry.expiresAt) { await this.removeCredentials(service); return null; } return JSON.parse(entry.value); } catch (error) { console.error('SecureStorage: Failed to get credentials:', error); return null; } } async removeCredentials(service) { try { if (this.keychain) { await this.keychain.resetInternetCredentials(service); return true; } else if (react_native_1.Platform.OS === 'web') { localStorage.removeItem(`secure_${service}`); return true; } return false; } catch (error) { console.error('SecureStorage: Failed to remove credentials:', error); return false; } } async updateCredentials(service, credentials, options) { try { const existing = await this.getCredentials(service, options); if (!existing) { throw new Error('No existing credentials found'); } const updated = { ...existing, ...credentials, }; return await this.storeCredentials(service, updated, options); } catch (error) { console.error('SecureStorage: Failed to update credentials:', error); return false; } } async rotateApiKey(service, newApiKey, options) { return await this.updateCredentials(service, { apiKey: newApiKey }, options); } async validateCredentials(service) { try { const credentials = await this.getCredentials(service); if (!credentials || !credentials.apiKey) { return false; } if (credentials.apiKey.length < 10) { return false; } return true; } catch (error) { console.error('SecureStorage: Failed to validate credentials:', error); return false; } } async encryptForWeb(data) { try { const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); if (crypto.subtle) { const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, [ 'encrypt', 'decrypt', ]); const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, dataBuffer); return btoa(String.fromCharCode(...new Uint8Array(encrypted))); } else { return btoa(data); } } catch (error) { console.warn('Web encryption failed, using base64:', error); return btoa(data); } } async decryptForWeb(encryptedData) { try { return atob(encryptedData); } catch (error) { console.error('Web decryption failed:', error); throw error; } } } exports.secureStorage = SecureStorageManager.getInstance(); exports.HereSecureStorage = { async storeHereCredentials(credentials, options) { return exports.secureStorage.storeCredentials('com.here.sdk', credentials, options); }, async getHereCredentials(options) { return exports.secureStorage.getCredentials('com.here.sdk', options); }, async removeHereCredentials() { return exports.secureStorage.removeCredentials('com.here.sdk'); }, async updateHereCredentials(credentials, options) { return exports.secureStorage.updateCredentials('com.here.sdk', credentials, options); }, async rotateHereApiKey(newApiKey, options) { return exports.secureStorage.rotateApiKey('com.here.sdk', newApiKey, options); }, async validateHereCredentials() { return exports.secureStorage.validateCredentials('com.here.sdk'); }, async isHereCredentialsStored() { const credentials = await exports.secureStorage.getCredentials('com.here.sdk'); return credentials !== null; }, }; const useSecureStorage = () => { return { secureStorage: exports.secureStorage, HereSecureStorage: exports.HereSecureStorage, isAvailable: exports.secureStorage.isSecureStorageAvailable(), }; }; exports.useSecureStorage = useSecureStorage; exports.SecurityUtils = { generateSecureToken(length = 32) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; if (typeof crypto !== 'undefined' && crypto.getRandomValues) { const array = new Uint32Array(length); crypto.getRandomValues(array); for (let i = 0; i < length; i++) { result += chars[array[i] % chars.length]; } } else { for (let i = 0; i < length; i++) { result += chars[Math.floor(Math.random() * chars.length)]; } } return result; }, validateApiKeyFormat(apiKey) { return apiKey.length >= 10 && /^[A-Za-z0-9_-]+$/.test(apiKey); }, maskSensitiveData(data, visibleChars = 4) { if (data.length <= visibleChars * 2) { return '*'.repeat(data.length); } const start = data.substring(0, visibleChars); const end = data.substring(data.length - visibleChars); const middle = '*'.repeat(data.length - visibleChars * 2); return `${start}${middle}${end}`; }, }; exports.default = exports.secureStorage;