@ateam-ai-mobile/expo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/createBridge.ts"],"sourcesContent":["/**\n * Native Bridge Factory\n *\n * Creates a Bridge instance that wraps Expo native modules.\n * Each bridge is scoped to a specific connector ID for storage isolation.\n */\n\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport * as ExpoCalendar from 'expo-calendar';\nimport * as ExpoContacts from 'expo-contacts';\nimport * as ExpoLocation from 'expo-location';\nimport * as ExpoBattery from 'expo-battery';\nimport * as ExpoNetwork from 'expo-network';\nimport * as ExpoNotifications from 'expo-notifications';\nimport * as ExpoSMS from 'expo-sms';\nimport * as ExpoLinking from 'expo-linking';\nimport { Platform } from 'react-native';\nimport * as Device from 'expo-device';\n\nimport type { Bridge } from '@ateam-ai-mobile/plugin-runtime';\n\nexport function createBridge(connectorId: string, deviceId: string): Bridge {\n const storagePrefix = `connector:${connectorId}:`;\n\n const log = {\n info: (msg: string, data?: any) => {\n console.log(`[${connectorId}] ${msg}`, data || '');\n },\n warn: (msg: string, data?: any) => {\n console.warn(`[${connectorId}] ${msg}`, data || '');\n },\n error: (msg: string, data?: any) => {\n console.error(`[${connectorId}] ${msg}`, data || '');\n },\n };\n\n // ─────────────────────────────────────────────────────────────────────\n // Helper functions\n // ─────────────────────────────────────────────────────────────────────\n\n const parseDateTime = (dateStr: string, timeStr: string): Date => {\n const [year, month, day] = dateStr.split('-').map(Number);\n const [hours, minutes] = timeStr.split(':').map(Number);\n return new Date(year, month - 1, day, hours, minutes);\n };\n\n const formatDate = (date: Date): string => {\n return date.toISOString().split('T')[0];\n };\n\n const getDayOfWeek = (date: Date): string => {\n const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n return days[date.getDay()];\n };\n\n const addDays = (date: Date, days: number): Date => {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n };\n\n const startOfDay = (date: Date): Date => {\n const result = new Date(date);\n result.setHours(0, 0, 0, 0);\n return result;\n };\n\n const endOfDay = (date: Date): Date => {\n const result = new Date(date);\n result.setHours(23, 59, 59, 999);\n return result;\n };\n\n const getCalendarIds = async (): Promise<string[]> => {\n try {\n const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);\n return calendars.map(c => c.id);\n } catch (err) {\n log.warn('Failed to get calendar IDs', err);\n return [];\n }\n };\n\n const getDefaultCalendarId = async (): Promise<string> => {\n try {\n const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);\n return calendars.length > 0 ? calendars[0].id : '';\n } catch (err) {\n log.warn('Failed to get default calendar', err);\n return '';\n }\n };\n\n const formatCalendarEvent = (event: any): any => {\n return {\n id: event.id,\n title: event.title,\n date: event.startDate ? formatDate(new Date(event.startDate)) : '',\n start: event.startDate ? new Date(event.startDate).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }) : '',\n end: event.endDate ? new Date(event.endDate).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }) : '',\n location: event.location || '',\n attendees: event.attendees ? event.attendees.map((a: any) => a.email || a.name) : [],\n notes: event.notes || '',\n };\n };\n\n const formatContact = (contact: any): any => {\n const phone = contact.phoneNumbers?.[0]?.number || null;\n const email = contact.emails?.[0]?.email || null;\n const birthday = contact.birthday\n ? `${contact.birthday.year}-${String(contact.birthday.month).padStart(2, '0')}-${String(contact.birthday.day).padStart(2, '0')}`\n : null;\n\n return {\n id: contact.id,\n name: contact.name,\n phone,\n email,\n relationship: contact.relationships?.[0]?.relationship || contact.contactType || 'contact',\n birthday,\n notes: contact.note || '',\n };\n };\n\n const classifyLocation = (geo: any): string => {\n const name = geo.name?.toLowerCase() || '';\n if (name.includes('home')) return 'home';\n if (name.includes('work') || name.includes('office')) return 'work';\n if (name.includes('school') || name.includes('university')) return 'school';\n if (name.includes('gym') || name.includes('fitness')) return 'fitness';\n if (name.includes('hospital') || name.includes('clinic') || name.includes('medical')) return 'medical';\n return 'other';\n };\n\n const formatPlace = (geo: any): string => {\n if (!geo) return 'Unknown Location';\n const name = geo.name || '';\n const street = geo.street ? `${geo.street}` : '';\n return name ? `${name} — ${street}` : street;\n };\n\n const formatAddress = (geo: any): string => {\n if (!geo) return '';\n const parts = [geo.street, geo.city, geo.region, geo.postalCode].filter(Boolean);\n return parts.join(', ');\n };\n\n const formatNotification = (notif: any): any => {\n return {\n id: notif.request?.identifier || String(Math.random()),\n app: notif.request?.content?.title || 'Unknown',\n title: notif.request?.content?.title || '',\n body: notif.request?.content?.body || '',\n timestamp: new Date().toISOString(),\n sender: null,\n priority: 'medium',\n read: false,\n };\n };\n\n const mapBatteryState = (state: number): 'charging' | 'full' | 'unplugged' => {\n if (state === ExpoBattery.BatteryState.CHARGING) return 'charging';\n if (state === ExpoBattery.BatteryState.FULL) return 'full';\n return 'unplugged';\n };\n\n // ─────────────────────────────────────────────────────────────────────\n // BRIDGE IMPLEMENTATION\n // ─────────────────────────────────────────────────────────────────────\n\n const bridge: Bridge = {\n calendar: {\n async getToday() {\n try {\n const start = startOfDay(new Date());\n const end = endOfDay(new Date());\n const calendarIds = await getCalendarIds();\n const events = await ExpoCalendar.getEventsAsync(calendarIds, start, end);\n return {\n date: formatDate(new Date()),\n day_of_week: getDayOfWeek(new Date()),\n events: events.map(formatCalendarEvent),\n count: events.length,\n };\n } catch (err) {\n log.error('Failed to get today\\'s calendar events', err);\n return { date: formatDate(new Date()), day_of_week: getDayOfWeek(new Date()), events: [], count: 0 };\n }\n },\n\n async getUpcoming(days = 7) {\n try {\n const start = new Date();\n const end = addDays(start, Math.min(days, 14));\n const calendarIds = await getCalendarIds();\n const events = await ExpoCalendar.getEventsAsync(calendarIds, start, end);\n return {\n from: formatDate(start),\n days_ahead: days,\n events: events.map(formatCalendarEvent),\n count: events.length,\n };\n } catch (err) {\n log.error('Failed to get upcoming calendar events', err);\n return { from: formatDate(new Date()), days_ahead: days, events: [], count: 0 };\n }\n },\n\n async create(event) {\n try {\n const calendarId = await getDefaultCalendarId();\n if (!calendarId) throw new Error('No default calendar found');\n\n const eventId = await ExpoCalendar.createEventAsync(calendarId, {\n title: event.title,\n startDate: parseDateTime(event.date, event.start_time),\n endDate: parseDateTime(event.date, event.end_time || event.start_time),\n location: event.location,\n notes: event.notes,\n });\n\n return { event_id: eventId };\n } catch (err) {\n log.error('Failed to create calendar event', err);\n throw err;\n }\n },\n\n async reschedule(params) {\n try {\n const updates: any = {};\n if (params.new_date && params.new_start_time) {\n updates.startDate = parseDateTime(params.new_date, params.new_start_time);\n }\n if (params.new_date && params.new_end_time) {\n updates.endDate = parseDateTime(params.new_date, params.new_end_time);\n }\n await ExpoCalendar.updateEventAsync(params.event_id, updates);\n return { ok: true };\n } catch (err) {\n log.error('Failed to reschedule event', err);\n throw err;\n }\n },\n\n async cancel(event_id) {\n try {\n await ExpoCalendar.deleteEventAsync(event_id);\n return { ok: true };\n } catch (err) {\n log.error('Failed to cancel event', err);\n throw err;\n }\n },\n },\n\n contacts: {\n async search(query) {\n try {\n const { data } = await ExpoContacts.getContactsAsync({\n fields: [\n ExpoContacts.Fields.PhoneNumbers,\n ExpoContacts.Fields.Emails,\n ExpoContacts.Fields.Birthday,\n ExpoContacts.Fields.Note,\n ExpoContacts.Fields.Relationships,\n ],\n });\n\n const q = query.toLowerCase();\n const filtered = data.filter(\n c => c.name?.toLowerCase().includes(q) || c.note?.toLowerCase().includes(q)\n );\n\n return { contacts: filtered.map(formatContact), count: filtered.length };\n } catch (err) {\n log.error('Failed to search contacts', err);\n return { contacts: [], count: 0 };\n }\n },\n\n async get(idOrName) {\n try {\n const { data } = await ExpoContacts.getContactsAsync({\n fields: [\n ExpoContacts.Fields.PhoneNumbers,\n ExpoContacts.Fields.Emails,\n ExpoContacts.Fields.Birthday,\n ExpoContacts.Fields.Note,\n ],\n });\n\n const contact = data.find(c => c.id === idOrName || c.name === idOrName);\n return contact ? formatContact(contact) : null;\n } catch (err) {\n log.error('Failed to get contact', err);\n return null;\n }\n },\n\n async getAll() {\n try {\n const { data } = await ExpoContacts.getContactsAsync({\n fields: [\n ExpoContacts.Fields.PhoneNumbers,\n ExpoContacts.Fields.Emails,\n ExpoContacts.Fields.Birthday,\n ExpoContacts.Fields.Note,\n ExpoContacts.Fields.Relationships,\n ],\n sort: ExpoContacts.SortTypes.LastName,\n });\n\n return { contacts: data.map(formatContact), count: data.length };\n } catch (err) {\n log.error('Failed to get all contacts', err);\n return { contacts: [], count: 0 };\n }\n },\n\n async birthdays(days = 30) {\n try {\n const { data } = await ExpoContacts.getContactsAsync({\n fields: [ExpoContacts.Fields.Birthday],\n });\n\n const today = new Date();\n const futureDate = addDays(today, days);\n\n const birthdays = data\n .filter(c => c.birthday)\n .map(c => {\n const birthday = c.birthday!;\n const thisYearBday = new Date(today.getFullYear(), birthday.month - 1, birthday.day);\n if (thisYearBday < today) {\n thisYearBday.setFullYear(today.getFullYear() + 1);\n }\n\n return {\n name: c.name,\n relationship: c.relationships?.[0]?.relationship || 'contact',\n birthday: `${birthday.year || '????'}-${String(birthday.month).padStart(2, '0')}-${String(birthday.day).padStart(2, '0')}`,\n days_until: Math.floor((thisYearBday.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)),\n is_today: thisYearBday.toDateString() === today.toDateString(),\n };\n })\n .filter(b => b.days_until >= 0 && b.days_until <= days);\n\n return { birthdays, count: birthdays.length };\n } catch (err) {\n log.error('Failed to get birthdays', err);\n return { birthdays: [], count: 0 };\n }\n },\n },\n\n location: {\n async getCurrent() {\n try {\n const location = await ExpoLocation.getCurrentPositionAsync({\n accuracy: ExpoLocation.Accuracy.Balanced,\n });\n\n const [geo] = await ExpoLocation.reverseGeocodeAsync({\n latitude: location.coords.latitude,\n longitude: location.coords.longitude,\n });\n\n return {\n lat: location.coords.latitude,\n lng: location.coords.longitude,\n place: formatPlace(geo),\n address: formatAddress(geo),\n type: classifyLocation(geo),\n timestamp: new Date().toISOString(),\n };\n } catch (err) {\n log.error('Failed to get current location', err);\n throw err;\n }\n },\n },\n\n battery: {\n async getStatus() {\n try {\n const [level, state, lowPower] = await Promise.all([\n ExpoBattery.getBatteryLevelAsync(),\n ExpoBattery.getBatteryStateAsync(),\n ExpoBattery.isLowPowerModeEnabledAsync(),\n ]);\n\n return {\n ok: true as const,\n timestamp: new Date().toISOString(),\n battery: {\n level: Math.round(level * 100),\n state: mapBatteryState(state),\n low_power_mode: lowPower,\n },\n };\n } catch (err) {\n log.error('Failed to get battery status', err);\n throw err;\n }\n },\n },\n\n network: {\n async getStatus() {\n try {\n const state = await ExpoNetwork.getNetworkStateAsync();\n return {\n type: state.type === ExpoNetwork.NetworkStateType.WIFI\n ? 'wifi'\n : state.type === ExpoNetwork.NetworkStateType.CELLULAR\n ? 'cellular'\n : 'none',\n is_connected: state.isConnected ?? false,\n timestamp: new Date().toISOString(),\n };\n } catch (err) {\n log.error('Failed to get network status', err);\n throw err;\n }\n },\n },\n\n notifications: {\n async list() {\n try {\n const presented = await ExpoNotifications.getPresentedNotificationsAsync();\n const notifications = presented.map(formatNotification);\n return {\n notifications,\n total: notifications.length,\n unread: notifications.filter(n => !n.read).length,\n };\n } catch (err) {\n log.error('Failed to get notifications', err);\n return { notifications: [], total: 0, unread: 0 };\n }\n },\n },\n\n sms: {\n async isAvailable() {\n try {\n return await ExpoSMS.isAvailableAsync();\n } catch (err) {\n log.error('Failed to check SMS availability', err);\n return false;\n }\n },\n\n async send(phone, message) {\n try {\n const { result } = await ExpoSMS.sendSMSAsync([phone], message);\n return {\n ok: result === 'sent' || result === 'unknown',\n status: result as 'sent' | 'unknown' | 'cancelled',\n };\n } catch (err) {\n log.error('Failed to send SMS', err);\n throw err;\n }\n },\n },\n\n maps: {\n async navigate(destination, mode = 'driving') {\n try {\n const encoded = encodeURIComponent(destination);\n const url = Platform.OS === 'ios'\n ? `maps://maps.apple.com/?daddr=${encoded}&dirflg=${mode[0]}`\n : `google.navigation:q=${encoded}&mode=${mode}`;\n\n const canOpen = await ExpoLinking.canOpenURL(url);\n if (!canOpen) {\n // Fallback to web\n const fallbackUrl = `https://www.google.com/maps/search/${encoded}`;\n await ExpoLinking.openURL(fallbackUrl);\n } else {\n await ExpoLinking.openURL(url);\n }\n\n return { ok: true, status: 'navigating' };\n } catch (err) {\n log.error('Failed to navigate', err);\n return { ok: false, status: 'error' };\n }\n },\n },\n\n http: {\n async get(url, headers = {}) {\n try {\n const res = await fetch(url, { headers });\n const data = await res.json();\n return {\n ok: res.ok,\n status: res.status,\n data,\n headers: Object.fromEntries(res.headers.entries()),\n };\n } catch (err) {\n log.error('HTTP GET failed', err);\n throw err;\n }\n },\n\n async post(url, body, headers = {}) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(body),\n });\n const data = await res.json();\n return {\n ok: res.ok,\n status: res.status,\n data,\n headers: Object.fromEntries(res.headers.entries()),\n };\n } catch (err) {\n log.error('HTTP POST failed', err);\n throw err;\n }\n },\n\n async put(url, body, headers = {}) {\n try {\n const res = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(body),\n });\n const data = await res.json();\n return {\n ok: res.ok,\n status: res.status,\n data,\n headers: Object.fromEntries(res.headers.entries()),\n };\n } catch (err) {\n log.error('HTTP PUT failed', err);\n throw err;\n }\n },\n\n async delete(url, body, headers = {}) {\n try {\n const opts: any = {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json', ...headers },\n };\n if (body) opts.body = JSON.stringify(body);\n\n const res = await fetch(url, opts);\n const data = await res.json();\n return {\n ok: res.ok,\n status: res.status,\n data,\n headers: Object.fromEntries(res.headers.entries()),\n };\n } catch (err) {\n log.error('HTTP DELETE failed', err);\n throw err;\n }\n },\n },\n\n storage: {\n async get(key) {\n try {\n return await AsyncStorage.getItem(storagePrefix + key);\n } catch (err) {\n log.error(`Failed to get storage key: ${key}`, err);\n return null;\n }\n },\n\n async set(key, value) {\n try {\n await AsyncStorage.setItem(storagePrefix + key, value);\n } catch (err) {\n log.error(`Failed to set storage key: ${key}`, err);\n }\n },\n\n async delete(key) {\n try {\n await AsyncStorage.removeItem(storagePrefix + key);\n } catch (err) {\n log.error(`Failed to delete storage key: ${key}`, err);\n }\n },\n\n async keys() {\n try {\n const allKeys = await AsyncStorage.getAllKeys();\n return allKeys\n .filter(k => k.startsWith(storagePrefix))\n .map(k => k.slice(storagePrefix.length));\n } catch (err) {\n log.error('Failed to list storage keys', err);\n return [];\n }\n },\n },\n\n permissions: {\n async check(capability) {\n try {\n // Map capability to Expo permission API\n switch (capability) {\n case 'calendar':\n const cal = await ExpoCalendar.getCalendarPermissionsAsync();\n return cal.status === 'granted' ? 'granted' : cal.status === 'denied' ? 'denied' : 'not_requested';\n case 'contacts':\n const con = await ExpoContacts.getPermissionsAsync();\n return con.status === 'granted' ? 'granted' : con.status === 'denied' ? 'denied' : 'not_requested';\n case 'location':\n const loc = await ExpoLocation.getForegroundPermissionsAsync();\n return loc.status === 'granted' ? 'granted' : loc.status === 'denied' ? 'denied' : 'not_requested';\n case 'notifications':\n const notif = await ExpoNotifications.getPermissionsAsync();\n return notif.granted ? 'granted' : notif.canAskAgain ? 'not_requested' : 'denied';\n default:\n return 'not_requested';\n }\n } catch (err) {\n log.error(`Failed to check permission: ${capability}`, err);\n return 'denied';\n }\n },\n\n async request(capability) {\n try {\n switch (capability) {\n case 'calendar':\n const cal = await ExpoCalendar.requestCalendarPermissionsAsync();\n return cal.status === 'granted';\n case 'contacts':\n const con = await ExpoContacts.requestPermissionsAsync();\n return con.status === 'granted';\n case 'location':\n const loc = await ExpoLocation.requestForegroundPermissionsAsync();\n return loc.status === 'granted';\n case 'notifications':\n const notif = await ExpoNotifications.requestPermissionsAsync();\n return notif.granted;\n default:\n return false;\n }\n } catch (err) {\n log.error(`Failed to request permission: ${capability}`, err);\n return false;\n }\n },\n\n async getAll() {\n try {\n const [cal, con, loc, notif] = await Promise.allSettled([\n ExpoCalendar.getCalendarPermissionsAsync(),\n ExpoContacts.getPermissionsAsync(),\n ExpoLocation.getForegroundPermissionsAsync(),\n ExpoNotifications.getPermissionsAsync(),\n ]);\n\n return {\n calendar: cal.status === 'fulfilled' ? (cal.value.status === 'granted' ? 'granted' : cal.value.status === 'denied' ? 'denied' : 'not_requested') : 'denied',\n contacts: con.status === 'fulfilled' ? (con.value.status === 'granted' ? 'granted' : con.value.status === 'denied' ? 'denied' : 'not_requested') : 'denied',\n location: loc.status === 'fulfilled' ? (loc.value.status === 'granted' ? 'granted' : loc.value.status === 'denied' ? 'denied' : 'not_requested') : 'denied',\n notifications: notif.status === 'fulfilled' ? (notif.value.granted ? 'granted' : notif.value.canAskAgain ? 'not_requested' : 'denied') : 'denied',\n };\n } catch (err) {\n log.error('Failed to get all permissions', err);\n return { calendar: 'denied', contacts: 'denied', location: 'denied', notifications: 'denied' };\n }\n },\n },\n\n device: {\n getDeviceId() {\n return deviceId;\n },\n\n getPlatform() {\n return {\n os: Platform.OS as 'ios' | 'android',\n version: Platform.Version ? String(Platform.Version) : 'unknown',\n };\n },\n },\n\n log,\n };\n\n return bridge;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,42 @@
1
+ import { AttachmentCache, OutgoingAttachment, AttachmentPreview } from '@ateam-ai-mobile/core';
2
+ import { HapticsAPI, BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/plugin-runtime';
3
+
4
+ declare class ExpoAttachmentCache implements AttachmentCache {
5
+ private index;
6
+ private loaded;
7
+ constructor();
8
+ ready(): Promise<void>;
9
+ save(jobId: string, attachments: OutgoingAttachment[]): Promise<void>;
10
+ get(jobId: string): AttachmentPreview[] | undefined;
11
+ }
12
+
13
+ declare const expoHaptics: HapticsAPI;
14
+
15
+ declare class ExpoBundleStorage implements BundleStorage {
16
+ private dirReady;
17
+ private ensureDir;
18
+ loadIndex(): Promise<Record<string, BundleCacheEntry>>;
19
+ saveIndex(index: Record<string, BundleCacheEntry>): Promise<void>;
20
+ readBundle(localPath: string): Promise<string | null>;
21
+ writeBundle(key: string, content: string): Promise<string>;
22
+ deleteBundle(localPath: string): Promise<void>;
23
+ clearAll(): Promise<void>;
24
+ }
25
+
26
+ /**
27
+ * Wire all Expo providers in one call. Runs before initAdas() is fine —
28
+ * these registries are independent of the core config.
29
+ *
30
+ * Returns the attachment cache so the host can pass it to initAdas():
31
+ * const { attachmentCache } = setupExpoProviders();
32
+ * initAdas({ ..., attachmentCache });
33
+ */
34
+ declare function setupExpoProviders(opts?: {
35
+ /** Extra modules remote bundles may require(), e.g. react-native-webview.
36
+ * Static imports must live in the HOST (it owns the native build). */
37
+ bundleModules?: Record<string, any>;
38
+ }): {
39
+ attachmentCache: ExpoAttachmentCache;
40
+ };
41
+
42
+ export { ExpoAttachmentCache, ExpoBundleStorage, expoHaptics, setupExpoProviders };
@@ -0,0 +1,42 @@
1
+ import { AttachmentCache, OutgoingAttachment, AttachmentPreview } from '@ateam-ai-mobile/core';
2
+ import { HapticsAPI, BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/plugin-runtime';
3
+
4
+ declare class ExpoAttachmentCache implements AttachmentCache {
5
+ private index;
6
+ private loaded;
7
+ constructor();
8
+ ready(): Promise<void>;
9
+ save(jobId: string, attachments: OutgoingAttachment[]): Promise<void>;
10
+ get(jobId: string): AttachmentPreview[] | undefined;
11
+ }
12
+
13
+ declare const expoHaptics: HapticsAPI;
14
+
15
+ declare class ExpoBundleStorage implements BundleStorage {
16
+ private dirReady;
17
+ private ensureDir;
18
+ loadIndex(): Promise<Record<string, BundleCacheEntry>>;
19
+ saveIndex(index: Record<string, BundleCacheEntry>): Promise<void>;
20
+ readBundle(localPath: string): Promise<string | null>;
21
+ writeBundle(key: string, content: string): Promise<string>;
22
+ deleteBundle(localPath: string): Promise<void>;
23
+ clearAll(): Promise<void>;
24
+ }
25
+
26
+ /**
27
+ * Wire all Expo providers in one call. Runs before initAdas() is fine —
28
+ * these registries are independent of the core config.
29
+ *
30
+ * Returns the attachment cache so the host can pass it to initAdas():
31
+ * const { attachmentCache } = setupExpoProviders();
32
+ * initAdas({ ..., attachmentCache });
33
+ */
34
+ declare function setupExpoProviders(opts?: {
35
+ /** Extra modules remote bundles may require(), e.g. react-native-webview.
36
+ * Static imports must live in the HOST (it owns the native build). */
37
+ bundleModules?: Record<string, any>;
38
+ }): {
39
+ attachmentCache: ExpoAttachmentCache;
40
+ };
41
+
42
+ export { ExpoAttachmentCache, ExpoBundleStorage, expoHaptics, setupExpoProviders };
package/dist/index.js ADDED
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ExpoAttachmentCache: () => ExpoAttachmentCache,
34
+ ExpoBundleStorage: () => ExpoBundleStorage,
35
+ expoHaptics: () => expoHaptics,
36
+ setupExpoProviders: () => setupExpoProviders
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+ var ExpoWebBrowser = __toESM(require("expo-web-browser"));
40
+ var ExpoLinking = __toESM(require("expo-linking"));
41
+ var ExpoClipboard = __toESM(require("expo-clipboard"));
42
+ var import_plugin_runtime = require("@ateam-ai-mobile/plugin-runtime");
43
+ var import_ui = require("@ateam-ai-mobile/ui");
44
+
45
+ // src/haptics.ts
46
+ var Haptics = __toESM(require("expo-haptics"));
47
+ var expoHaptics = {
48
+ impact(style) {
49
+ const map = {
50
+ light: Haptics.ImpactFeedbackStyle.Light,
51
+ medium: Haptics.ImpactFeedbackStyle.Medium,
52
+ heavy: Haptics.ImpactFeedbackStyle.Heavy
53
+ };
54
+ Haptics.impactAsync(map[style]).catch(() => {
55
+ });
56
+ },
57
+ notification(type) {
58
+ const map = {
59
+ success: Haptics.NotificationFeedbackType.Success,
60
+ warning: Haptics.NotificationFeedbackType.Warning,
61
+ error: Haptics.NotificationFeedbackType.Error
62
+ };
63
+ Haptics.notificationAsync(map[type]).catch(() => {
64
+ });
65
+ },
66
+ selection() {
67
+ Haptics.selectionAsync().catch(() => {
68
+ });
69
+ }
70
+ };
71
+
72
+ // src/bundleStorage.ts
73
+ var FileSystem = __toESM(require("expo-file-system/legacy"));
74
+ var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
75
+ var CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;
76
+ var CACHE_INDEX_KEY = "adas-plugin-bundles-index-v1";
77
+ var ExpoBundleStorage = class {
78
+ constructor() {
79
+ this.dirReady = false;
80
+ }
81
+ async ensureDir() {
82
+ if (this.dirReady) return;
83
+ try {
84
+ const info = await FileSystem.getInfoAsync(CACHE_DIR);
85
+ if (!info.exists) {
86
+ await FileSystem.makeDirectoryAsync(CACHE_DIR, { intermediates: true });
87
+ }
88
+ } catch (err) {
89
+ console.warn("[ExpoBundleStorage] cache dir error:", err);
90
+ }
91
+ this.dirReady = true;
92
+ }
93
+ async loadIndex() {
94
+ try {
95
+ const raw = await import_async_storage.default.getItem(CACHE_INDEX_KEY);
96
+ return raw ? JSON.parse(raw) : {};
97
+ } catch {
98
+ return {};
99
+ }
100
+ }
101
+ async saveIndex(index) {
102
+ try {
103
+ await import_async_storage.default.setItem(CACHE_INDEX_KEY, JSON.stringify(index));
104
+ } catch (err) {
105
+ console.warn("[ExpoBundleStorage] index save error:", err);
106
+ }
107
+ }
108
+ async readBundle(localPath) {
109
+ try {
110
+ const info = await FileSystem.getInfoAsync(localPath);
111
+ if (!info.exists) return null;
112
+ return await FileSystem.readAsStringAsync(localPath);
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+ async writeBundle(key, content) {
118
+ await this.ensureDir();
119
+ const localPath = `${CACHE_DIR}${key}`;
120
+ await FileSystem.writeAsStringAsync(localPath, content);
121
+ return localPath;
122
+ }
123
+ async deleteBundle(localPath) {
124
+ await FileSystem.deleteAsync(localPath, { idempotent: true }).catch(() => {
125
+ });
126
+ }
127
+ async clearAll() {
128
+ await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {
129
+ });
130
+ await import_async_storage.default.removeItem(CACHE_INDEX_KEY).catch(() => {
131
+ });
132
+ this.dirReady = false;
133
+ }
134
+ };
135
+
136
+ // src/attachmentCache.ts
137
+ var FileSystem2 = __toESM(require("expo-file-system/legacy"));
138
+ var import_async_storage2 = __toESM(require("@react-native-async-storage/async-storage"));
139
+ var INDEX_KEY = "adas-chat-attachment-index-v1";
140
+ var DIR = `${FileSystem2.documentDirectory}chat-attachments/`;
141
+ var MAX_JOBS = 100;
142
+ var ExpoAttachmentCache = class {
143
+ constructor() {
144
+ this.index = {};
145
+ this.loaded = (async () => {
146
+ try {
147
+ const info = await FileSystem2.getInfoAsync(DIR);
148
+ if (!info.exists) await FileSystem2.makeDirectoryAsync(DIR, { intermediates: true });
149
+ const raw = await import_async_storage2.default.getItem(INDEX_KEY);
150
+ if (raw) this.index = JSON.parse(raw);
151
+ } catch {
152
+ }
153
+ })();
154
+ }
155
+ async ready() {
156
+ await this.loaded;
157
+ }
158
+ async save(jobId, attachments) {
159
+ await this.loaded;
160
+ const entries = [];
161
+ for (let i = 0; i < attachments.length; i++) {
162
+ const att = attachments[i];
163
+ const ext = att.mimeType.startsWith("image/") ? att.mimeType.split("/")[1] || "jpg" : "bin";
164
+ const filePath = `${DIR}${jobId}_${i}.${ext}`;
165
+ try {
166
+ await FileSystem2.writeAsStringAsync(filePath, att.data, { encoding: FileSystem2.EncodingType.Base64 });
167
+ entries.push({ mimeType: att.mimeType, name: att.name, filePath });
168
+ } catch (err) {
169
+ console.warn("[ExpoAttachmentCache] write failed:", err);
170
+ }
171
+ }
172
+ if (entries.length === 0) return;
173
+ this.index[jobId] = entries;
174
+ const keys = Object.keys(this.index);
175
+ if (keys.length > MAX_JOBS) {
176
+ for (const k of keys.slice(0, keys.length - MAX_JOBS)) {
177
+ const old = this.index[k];
178
+ if (old) for (const o of old) FileSystem2.deleteAsync(o.filePath, { idempotent: true }).catch(() => {
179
+ });
180
+ delete this.index[k];
181
+ }
182
+ }
183
+ try {
184
+ await import_async_storage2.default.setItem(INDEX_KEY, JSON.stringify(this.index));
185
+ } catch {
186
+ }
187
+ }
188
+ get(jobId) {
189
+ const entries = this.index[jobId];
190
+ if (!entries || entries.length === 0) return void 0;
191
+ return entries.map((e) => ({
192
+ mimeType: e.mimeType,
193
+ name: e.name,
194
+ previewUri: e.filePath
195
+ }));
196
+ }
197
+ };
198
+
199
+ // src/index.ts
200
+ function setupExpoProviders(opts) {
201
+ (0, import_plugin_runtime.setHapticsProvider)(expoHaptics);
202
+ (0, import_plugin_runtime.setBundleStorage)(new ExpoBundleStorage());
203
+ (0, import_ui.setClipboardProvider)({
204
+ setString: (text) => ExpoClipboard.setStringAsync(text).then(() => {
205
+ })
206
+ });
207
+ (0, import_plugin_runtime.registerBundleModules)({
208
+ "expo-web-browser": ExpoWebBrowser,
209
+ "expo-linking": ExpoLinking,
210
+ ...opts?.bundleModules || {}
211
+ });
212
+ return { attachmentCache: new ExpoAttachmentCache() };
213
+ }
214
+ // Annotate the CommonJS export names for ESM import in node:
215
+ 0 && (module.exports = {
216
+ ExpoAttachmentCache,
217
+ ExpoBundleStorage,
218
+ expoHaptics,
219
+ setupExpoProviders
220
+ });
221
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/haptics.ts","../src/bundleStorage.ts","../src/attachmentCache.ts"],"sourcesContent":["/**\n * @ateam-ai-mobile/expo — Expo-backed providers for the ADAS SDK.\n *\n * Bare-RN hosts skip this package entirely (core + plugin-runtime have safe\n * defaults); Expo hosts call setupExpoProviders() once at startup to light\n * up haptics, persistent bundle caching, attachment previews, and the\n * expo modules remote bundles may require.\n */\nimport * as ExpoWebBrowser from 'expo-web-browser';\nimport * as ExpoLinking from 'expo-linking';\nimport * as ExpoClipboard from 'expo-clipboard';\nimport {\n setHapticsProvider,\n setBundleStorage,\n registerBundleModules,\n} from '@ateam-ai-mobile/plugin-runtime';\nimport { setClipboardProvider } from '@ateam-ai-mobile/ui';\n\nimport { expoHaptics } from './haptics';\nimport { ExpoBundleStorage } from './bundleStorage';\nimport { ExpoAttachmentCache } from './attachmentCache';\n\nexport { expoHaptics } from './haptics';\nexport { ExpoBundleStorage } from './bundleStorage';\nexport { ExpoAttachmentCache } from './attachmentCache';\n// NOTE: createBridge (device capabilities bridge) is intentionally NOT\n// exported here — it statically imports the optional expo modules\n// (calendar/contacts/location/…), which would break Metro builds in hosts\n// that don't install them. Install those modules and import it from the\n// subpath: `import { createBridge } from '@ateam-ai-mobile/expo/bridge'`.\n\n/**\n * Wire all Expo providers in one call. Runs before initAdas() is fine —\n * these registries are independent of the core config.\n *\n * Returns the attachment cache so the host can pass it to initAdas():\n * const { attachmentCache } = setupExpoProviders();\n * initAdas({ ..., attachmentCache });\n */\nexport function setupExpoProviders(opts?: {\n /** Extra modules remote bundles may require(), e.g. react-native-webview.\n * Static imports must live in the HOST (it owns the native build). */\n bundleModules?: Record<string, any>;\n}): { attachmentCache: ExpoAttachmentCache } {\n setHapticsProvider(expoHaptics);\n setBundleStorage(new ExpoBundleStorage());\n setClipboardProvider({\n setString: (text: string) => ExpoClipboard.setStringAsync(text).then(() => {}),\n });\n registerBundleModules({\n 'expo-web-browser': ExpoWebBrowser,\n 'expo-linking': ExpoLinking,\n ...(opts?.bundleModules || {}),\n });\n return { attachmentCache: new ExpoAttachmentCache() };\n}\n","/**\n * expo-haptics implementation of the plugin HapticsAPI.\n */\nimport * as Haptics from 'expo-haptics';\nimport type { HapticsAPI } from '@ateam-ai-mobile/plugin-runtime';\n\nexport const expoHaptics: HapticsAPI = {\n impact(style) {\n const map = {\n light: Haptics.ImpactFeedbackStyle.Light,\n medium: Haptics.ImpactFeedbackStyle.Medium,\n heavy: Haptics.ImpactFeedbackStyle.Heavy,\n } as const;\n Haptics.impactAsync(map[style]).catch(() => {});\n },\n notification(type) {\n const map = {\n success: Haptics.NotificationFeedbackType.Success,\n warning: Haptics.NotificationFeedbackType.Warning,\n error: Haptics.NotificationFeedbackType.Error,\n } as const;\n Haptics.notificationAsync(map[type]).catch(() => {});\n },\n selection() {\n Haptics.selectionAsync().catch(() => {});\n },\n};\n","/**\n * Disk-backed BundleStorage: bundle files in the Expo cache directory,\n * index in AsyncStorage. Mirrors the reference app's cache semantics\n * (index version key, files under plugin-bundles/).\n */\nimport * as FileSystem from 'expo-file-system/legacy';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/plugin-runtime';\n\nconst CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;\nconst CACHE_INDEX_KEY = 'adas-plugin-bundles-index-v1';\n\nexport class ExpoBundleStorage implements BundleStorage {\n private dirReady = false;\n\n private async ensureDir(): Promise<void> {\n if (this.dirReady) return;\n try {\n const info = await FileSystem.getInfoAsync(CACHE_DIR);\n if (!info.exists) {\n await FileSystem.makeDirectoryAsync(CACHE_DIR, { intermediates: true });\n }\n } catch (err) {\n console.warn('[ExpoBundleStorage] cache dir error:', err);\n }\n this.dirReady = true;\n }\n\n async loadIndex(): Promise<Record<string, BundleCacheEntry>> {\n try {\n const raw = await AsyncStorage.getItem(CACHE_INDEX_KEY);\n return raw ? JSON.parse(raw) : {};\n } catch {\n return {};\n }\n }\n\n async saveIndex(index: Record<string, BundleCacheEntry>): Promise<void> {\n try {\n await AsyncStorage.setItem(CACHE_INDEX_KEY, JSON.stringify(index));\n } catch (err) {\n console.warn('[ExpoBundleStorage] index save error:', err);\n }\n }\n\n async readBundle(localPath: string): Promise<string | null> {\n try {\n const info = await FileSystem.getInfoAsync(localPath);\n if (!info.exists) return null;\n return await FileSystem.readAsStringAsync(localPath);\n } catch {\n return null; // file gone — caller re-downloads\n }\n }\n\n async writeBundle(key: string, content: string): Promise<string> {\n await this.ensureDir();\n const localPath = `${CACHE_DIR}${key}`;\n await FileSystem.writeAsStringAsync(localPath, content);\n return localPath;\n }\n\n async deleteBundle(localPath: string): Promise<void> {\n await FileSystem.deleteAsync(localPath, { idempotent: true }).catch(() => {});\n }\n\n async clearAll(): Promise<void> {\n await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {});\n await AsyncStorage.removeItem(CACHE_INDEX_KEY).catch(() => {});\n this.dirReady = false;\n }\n}\n","/**\n * Expo implementation of @ateam-ai-mobile/core's AttachmentCache: attachment bytes on\n * disk, index in AsyncStorage. Mirrors the reference app's chat attachment\n * cache (last-100 retention).\n */\nimport * as FileSystem from 'expo-file-system/legacy';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { AttachmentCache, AttachmentPreview, OutgoingAttachment } from '@ateam-ai-mobile/core';\n\nconst INDEX_KEY = 'adas-chat-attachment-index-v1';\nconst DIR = `${FileSystem.documentDirectory}chat-attachments/`;\nconst MAX_JOBS = 100;\n\ninterface CachedAttachment { mimeType: string; name: string; filePath: string }\n\nexport class ExpoAttachmentCache implements AttachmentCache {\n private index: Record<string, CachedAttachment[]> = {};\n private loaded: Promise<void>;\n\n constructor() {\n this.loaded = (async () => {\n try {\n const info = await FileSystem.getInfoAsync(DIR);\n if (!info.exists) await FileSystem.makeDirectoryAsync(DIR, { intermediates: true });\n const raw = await AsyncStorage.getItem(INDEX_KEY);\n if (raw) this.index = JSON.parse(raw);\n } catch {}\n })();\n }\n\n async ready(): Promise<void> {\n await this.loaded;\n }\n\n async save(jobId: string, attachments: OutgoingAttachment[]): Promise<void> {\n await this.loaded;\n const entries: CachedAttachment[] = [];\n for (let i = 0; i < attachments.length; i++) {\n const att = attachments[i];\n const ext = att.mimeType.startsWith('image/') ? att.mimeType.split('/')[1] || 'jpg' : 'bin';\n const filePath = `${DIR}${jobId}_${i}.${ext}`;\n try {\n await FileSystem.writeAsStringAsync(filePath, att.data, { encoding: FileSystem.EncodingType.Base64 });\n entries.push({ mimeType: att.mimeType, name: att.name, filePath });\n } catch (err) {\n console.warn('[ExpoAttachmentCache] write failed:', err);\n }\n }\n if (entries.length === 0) return;\n\n this.index[jobId] = entries;\n // Keep only the most recent MAX_JOBS jobs; delete evicted files.\n const keys = Object.keys(this.index);\n if (keys.length > MAX_JOBS) {\n for (const k of keys.slice(0, keys.length - MAX_JOBS)) {\n const old = this.index[k];\n if (old) for (const o of old) FileSystem.deleteAsync(o.filePath, { idempotent: true }).catch(() => {});\n delete this.index[k];\n }\n }\n try {\n await AsyncStorage.setItem(INDEX_KEY, JSON.stringify(this.index));\n } catch {}\n }\n\n get(jobId: string): AttachmentPreview[] | undefined {\n const entries = this.index[jobId];\n if (!entries || entries.length === 0) return undefined;\n return entries.map(e => ({\n mimeType: e.mimeType,\n name: e.name,\n previewUri: e.filePath,\n }));\n }\n}\n"],"mappings":"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","names":["AsyncStorage","FileSystem","import_async_storage","AsyncStorage"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,187 @@
1
+ // src/index.ts
2
+ import * as ExpoWebBrowser from "expo-web-browser";
3
+ import * as ExpoLinking from "expo-linking";
4
+ import * as ExpoClipboard from "expo-clipboard";
5
+ import {
6
+ setHapticsProvider,
7
+ setBundleStorage,
8
+ registerBundleModules
9
+ } from "@ateam-ai-mobile/plugin-runtime";
10
+ import { setClipboardProvider } from "@ateam-ai-mobile/ui";
11
+
12
+ // src/haptics.ts
13
+ import * as Haptics from "expo-haptics";
14
+ var expoHaptics = {
15
+ impact(style) {
16
+ const map = {
17
+ light: Haptics.ImpactFeedbackStyle.Light,
18
+ medium: Haptics.ImpactFeedbackStyle.Medium,
19
+ heavy: Haptics.ImpactFeedbackStyle.Heavy
20
+ };
21
+ Haptics.impactAsync(map[style]).catch(() => {
22
+ });
23
+ },
24
+ notification(type) {
25
+ const map = {
26
+ success: Haptics.NotificationFeedbackType.Success,
27
+ warning: Haptics.NotificationFeedbackType.Warning,
28
+ error: Haptics.NotificationFeedbackType.Error
29
+ };
30
+ Haptics.notificationAsync(map[type]).catch(() => {
31
+ });
32
+ },
33
+ selection() {
34
+ Haptics.selectionAsync().catch(() => {
35
+ });
36
+ }
37
+ };
38
+
39
+ // src/bundleStorage.ts
40
+ import * as FileSystem from "expo-file-system/legacy";
41
+ import AsyncStorage from "@react-native-async-storage/async-storage";
42
+ var CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;
43
+ var CACHE_INDEX_KEY = "adas-plugin-bundles-index-v1";
44
+ var ExpoBundleStorage = class {
45
+ constructor() {
46
+ this.dirReady = false;
47
+ }
48
+ async ensureDir() {
49
+ if (this.dirReady) return;
50
+ try {
51
+ const info = await FileSystem.getInfoAsync(CACHE_DIR);
52
+ if (!info.exists) {
53
+ await FileSystem.makeDirectoryAsync(CACHE_DIR, { intermediates: true });
54
+ }
55
+ } catch (err) {
56
+ console.warn("[ExpoBundleStorage] cache dir error:", err);
57
+ }
58
+ this.dirReady = true;
59
+ }
60
+ async loadIndex() {
61
+ try {
62
+ const raw = await AsyncStorage.getItem(CACHE_INDEX_KEY);
63
+ return raw ? JSON.parse(raw) : {};
64
+ } catch {
65
+ return {};
66
+ }
67
+ }
68
+ async saveIndex(index) {
69
+ try {
70
+ await AsyncStorage.setItem(CACHE_INDEX_KEY, JSON.stringify(index));
71
+ } catch (err) {
72
+ console.warn("[ExpoBundleStorage] index save error:", err);
73
+ }
74
+ }
75
+ async readBundle(localPath) {
76
+ try {
77
+ const info = await FileSystem.getInfoAsync(localPath);
78
+ if (!info.exists) return null;
79
+ return await FileSystem.readAsStringAsync(localPath);
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
84
+ async writeBundle(key, content) {
85
+ await this.ensureDir();
86
+ const localPath = `${CACHE_DIR}${key}`;
87
+ await FileSystem.writeAsStringAsync(localPath, content);
88
+ return localPath;
89
+ }
90
+ async deleteBundle(localPath) {
91
+ await FileSystem.deleteAsync(localPath, { idempotent: true }).catch(() => {
92
+ });
93
+ }
94
+ async clearAll() {
95
+ await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {
96
+ });
97
+ await AsyncStorage.removeItem(CACHE_INDEX_KEY).catch(() => {
98
+ });
99
+ this.dirReady = false;
100
+ }
101
+ };
102
+
103
+ // src/attachmentCache.ts
104
+ import * as FileSystem2 from "expo-file-system/legacy";
105
+ import AsyncStorage2 from "@react-native-async-storage/async-storage";
106
+ var INDEX_KEY = "adas-chat-attachment-index-v1";
107
+ var DIR = `${FileSystem2.documentDirectory}chat-attachments/`;
108
+ var MAX_JOBS = 100;
109
+ var ExpoAttachmentCache = class {
110
+ constructor() {
111
+ this.index = {};
112
+ this.loaded = (async () => {
113
+ try {
114
+ const info = await FileSystem2.getInfoAsync(DIR);
115
+ if (!info.exists) await FileSystem2.makeDirectoryAsync(DIR, { intermediates: true });
116
+ const raw = await AsyncStorage2.getItem(INDEX_KEY);
117
+ if (raw) this.index = JSON.parse(raw);
118
+ } catch {
119
+ }
120
+ })();
121
+ }
122
+ async ready() {
123
+ await this.loaded;
124
+ }
125
+ async save(jobId, attachments) {
126
+ await this.loaded;
127
+ const entries = [];
128
+ for (let i = 0; i < attachments.length; i++) {
129
+ const att = attachments[i];
130
+ const ext = att.mimeType.startsWith("image/") ? att.mimeType.split("/")[1] || "jpg" : "bin";
131
+ const filePath = `${DIR}${jobId}_${i}.${ext}`;
132
+ try {
133
+ await FileSystem2.writeAsStringAsync(filePath, att.data, { encoding: FileSystem2.EncodingType.Base64 });
134
+ entries.push({ mimeType: att.mimeType, name: att.name, filePath });
135
+ } catch (err) {
136
+ console.warn("[ExpoAttachmentCache] write failed:", err);
137
+ }
138
+ }
139
+ if (entries.length === 0) return;
140
+ this.index[jobId] = entries;
141
+ const keys = Object.keys(this.index);
142
+ if (keys.length > MAX_JOBS) {
143
+ for (const k of keys.slice(0, keys.length - MAX_JOBS)) {
144
+ const old = this.index[k];
145
+ if (old) for (const o of old) FileSystem2.deleteAsync(o.filePath, { idempotent: true }).catch(() => {
146
+ });
147
+ delete this.index[k];
148
+ }
149
+ }
150
+ try {
151
+ await AsyncStorage2.setItem(INDEX_KEY, JSON.stringify(this.index));
152
+ } catch {
153
+ }
154
+ }
155
+ get(jobId) {
156
+ const entries = this.index[jobId];
157
+ if (!entries || entries.length === 0) return void 0;
158
+ return entries.map((e) => ({
159
+ mimeType: e.mimeType,
160
+ name: e.name,
161
+ previewUri: e.filePath
162
+ }));
163
+ }
164
+ };
165
+
166
+ // src/index.ts
167
+ function setupExpoProviders(opts) {
168
+ setHapticsProvider(expoHaptics);
169
+ setBundleStorage(new ExpoBundleStorage());
170
+ setClipboardProvider({
171
+ setString: (text) => ExpoClipboard.setStringAsync(text).then(() => {
172
+ })
173
+ });
174
+ registerBundleModules({
175
+ "expo-web-browser": ExpoWebBrowser,
176
+ "expo-linking": ExpoLinking,
177
+ ...opts?.bundleModules || {}
178
+ });
179
+ return { attachmentCache: new ExpoAttachmentCache() };
180
+ }
181
+ export {
182
+ ExpoAttachmentCache,
183
+ ExpoBundleStorage,
184
+ expoHaptics,
185
+ setupExpoProviders
186
+ };
187
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/haptics.ts","../src/bundleStorage.ts","../src/attachmentCache.ts"],"sourcesContent":["/**\n * @ateam-ai-mobile/expo — Expo-backed providers for the ADAS SDK.\n *\n * Bare-RN hosts skip this package entirely (core + plugin-runtime have safe\n * defaults); Expo hosts call setupExpoProviders() once at startup to light\n * up haptics, persistent bundle caching, attachment previews, and the\n * expo modules remote bundles may require.\n */\nimport * as ExpoWebBrowser from 'expo-web-browser';\nimport * as ExpoLinking from 'expo-linking';\nimport * as ExpoClipboard from 'expo-clipboard';\nimport {\n setHapticsProvider,\n setBundleStorage,\n registerBundleModules,\n} from '@ateam-ai-mobile/plugin-runtime';\nimport { setClipboardProvider } from '@ateam-ai-mobile/ui';\n\nimport { expoHaptics } from './haptics';\nimport { ExpoBundleStorage } from './bundleStorage';\nimport { ExpoAttachmentCache } from './attachmentCache';\n\nexport { expoHaptics } from './haptics';\nexport { ExpoBundleStorage } from './bundleStorage';\nexport { ExpoAttachmentCache } from './attachmentCache';\n// NOTE: createBridge (device capabilities bridge) is intentionally NOT\n// exported here — it statically imports the optional expo modules\n// (calendar/contacts/location/…), which would break Metro builds in hosts\n// that don't install them. Install those modules and import it from the\n// subpath: `import { createBridge } from '@ateam-ai-mobile/expo/bridge'`.\n\n/**\n * Wire all Expo providers in one call. Runs before initAdas() is fine —\n * these registries are independent of the core config.\n *\n * Returns the attachment cache so the host can pass it to initAdas():\n * const { attachmentCache } = setupExpoProviders();\n * initAdas({ ..., attachmentCache });\n */\nexport function setupExpoProviders(opts?: {\n /** Extra modules remote bundles may require(), e.g. react-native-webview.\n * Static imports must live in the HOST (it owns the native build). */\n bundleModules?: Record<string, any>;\n}): { attachmentCache: ExpoAttachmentCache } {\n setHapticsProvider(expoHaptics);\n setBundleStorage(new ExpoBundleStorage());\n setClipboardProvider({\n setString: (text: string) => ExpoClipboard.setStringAsync(text).then(() => {}),\n });\n registerBundleModules({\n 'expo-web-browser': ExpoWebBrowser,\n 'expo-linking': ExpoLinking,\n ...(opts?.bundleModules || {}),\n });\n return { attachmentCache: new ExpoAttachmentCache() };\n}\n","/**\n * expo-haptics implementation of the plugin HapticsAPI.\n */\nimport * as Haptics from 'expo-haptics';\nimport type { HapticsAPI } from '@ateam-ai-mobile/plugin-runtime';\n\nexport const expoHaptics: HapticsAPI = {\n impact(style) {\n const map = {\n light: Haptics.ImpactFeedbackStyle.Light,\n medium: Haptics.ImpactFeedbackStyle.Medium,\n heavy: Haptics.ImpactFeedbackStyle.Heavy,\n } as const;\n Haptics.impactAsync(map[style]).catch(() => {});\n },\n notification(type) {\n const map = {\n success: Haptics.NotificationFeedbackType.Success,\n warning: Haptics.NotificationFeedbackType.Warning,\n error: Haptics.NotificationFeedbackType.Error,\n } as const;\n Haptics.notificationAsync(map[type]).catch(() => {});\n },\n selection() {\n Haptics.selectionAsync().catch(() => {});\n },\n};\n","/**\n * Disk-backed BundleStorage: bundle files in the Expo cache directory,\n * index in AsyncStorage. Mirrors the reference app's cache semantics\n * (index version key, files under plugin-bundles/).\n */\nimport * as FileSystem from 'expo-file-system/legacy';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/plugin-runtime';\n\nconst CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;\nconst CACHE_INDEX_KEY = 'adas-plugin-bundles-index-v1';\n\nexport class ExpoBundleStorage implements BundleStorage {\n private dirReady = false;\n\n private async ensureDir(): Promise<void> {\n if (this.dirReady) return;\n try {\n const info = await FileSystem.getInfoAsync(CACHE_DIR);\n if (!info.exists) {\n await FileSystem.makeDirectoryAsync(CACHE_DIR, { intermediates: true });\n }\n } catch (err) {\n console.warn('[ExpoBundleStorage] cache dir error:', err);\n }\n this.dirReady = true;\n }\n\n async loadIndex(): Promise<Record<string, BundleCacheEntry>> {\n try {\n const raw = await AsyncStorage.getItem(CACHE_INDEX_KEY);\n return raw ? JSON.parse(raw) : {};\n } catch {\n return {};\n }\n }\n\n async saveIndex(index: Record<string, BundleCacheEntry>): Promise<void> {\n try {\n await AsyncStorage.setItem(CACHE_INDEX_KEY, JSON.stringify(index));\n } catch (err) {\n console.warn('[ExpoBundleStorage] index save error:', err);\n }\n }\n\n async readBundle(localPath: string): Promise<string | null> {\n try {\n const info = await FileSystem.getInfoAsync(localPath);\n if (!info.exists) return null;\n return await FileSystem.readAsStringAsync(localPath);\n } catch {\n return null; // file gone — caller re-downloads\n }\n }\n\n async writeBundle(key: string, content: string): Promise<string> {\n await this.ensureDir();\n const localPath = `${CACHE_DIR}${key}`;\n await FileSystem.writeAsStringAsync(localPath, content);\n return localPath;\n }\n\n async deleteBundle(localPath: string): Promise<void> {\n await FileSystem.deleteAsync(localPath, { idempotent: true }).catch(() => {});\n }\n\n async clearAll(): Promise<void> {\n await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {});\n await AsyncStorage.removeItem(CACHE_INDEX_KEY).catch(() => {});\n this.dirReady = false;\n }\n}\n","/**\n * Expo implementation of @ateam-ai-mobile/core's AttachmentCache: attachment bytes on\n * disk, index in AsyncStorage. Mirrors the reference app's chat attachment\n * cache (last-100 retention).\n */\nimport * as FileSystem from 'expo-file-system/legacy';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { AttachmentCache, AttachmentPreview, OutgoingAttachment } from '@ateam-ai-mobile/core';\n\nconst INDEX_KEY = 'adas-chat-attachment-index-v1';\nconst DIR = `${FileSystem.documentDirectory}chat-attachments/`;\nconst MAX_JOBS = 100;\n\ninterface CachedAttachment { mimeType: string; name: string; filePath: string }\n\nexport class ExpoAttachmentCache implements AttachmentCache {\n private index: Record<string, CachedAttachment[]> = {};\n private loaded: Promise<void>;\n\n constructor() {\n this.loaded = (async () => {\n try {\n const info = await FileSystem.getInfoAsync(DIR);\n if (!info.exists) await FileSystem.makeDirectoryAsync(DIR, { intermediates: true });\n const raw = await AsyncStorage.getItem(INDEX_KEY);\n if (raw) this.index = JSON.parse(raw);\n } catch {}\n })();\n }\n\n async ready(): Promise<void> {\n await this.loaded;\n }\n\n async save(jobId: string, attachments: OutgoingAttachment[]): Promise<void> {\n await this.loaded;\n const entries: CachedAttachment[] = [];\n for (let i = 0; i < attachments.length; i++) {\n const att = attachments[i];\n const ext = att.mimeType.startsWith('image/') ? att.mimeType.split('/')[1] || 'jpg' : 'bin';\n const filePath = `${DIR}${jobId}_${i}.${ext}`;\n try {\n await FileSystem.writeAsStringAsync(filePath, att.data, { encoding: FileSystem.EncodingType.Base64 });\n entries.push({ mimeType: att.mimeType, name: att.name, filePath });\n } catch (err) {\n console.warn('[ExpoAttachmentCache] write failed:', err);\n }\n }\n if (entries.length === 0) return;\n\n this.index[jobId] = entries;\n // Keep only the most recent MAX_JOBS jobs; delete evicted files.\n const keys = Object.keys(this.index);\n if (keys.length > MAX_JOBS) {\n for (const k of keys.slice(0, keys.length - MAX_JOBS)) {\n const old = this.index[k];\n if (old) for (const o of old) FileSystem.deleteAsync(o.filePath, { idempotent: true }).catch(() => {});\n delete this.index[k];\n }\n }\n try {\n await AsyncStorage.setItem(INDEX_KEY, JSON.stringify(this.index));\n } catch {}\n }\n\n get(jobId: string): AttachmentPreview[] | undefined {\n const entries = this.index[jobId];\n if (!entries || entries.length === 0) return undefined;\n return entries.map(e => ({\n mimeType: e.mimeType,\n name: e.name,\n previewUri: e.filePath,\n }));\n }\n}\n"],"mappings":"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","names":["FileSystem","AsyncStorage"]}