@ateam-ai-mobile/expo 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +25 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +25 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/attachmentCache.ts +10 -4
- package/src/bundleStorage.ts +11 -4
- package/src/index.ts +13 -2
package/dist/index.d.mts
CHANGED
|
@@ -4,7 +4,13 @@ import { HapticsAPI, BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/pl
|
|
|
4
4
|
declare class ExpoAttachmentCache implements AttachmentCache {
|
|
5
5
|
private index;
|
|
6
6
|
private loaded;
|
|
7
|
-
|
|
7
|
+
private indexKey;
|
|
8
|
+
/** Pass `indexKey` when migrating from an app that already persisted an
|
|
9
|
+
* index under a different AsyncStorage key — otherwise existing users
|
|
10
|
+
* lose every cached preview on upgrade. */
|
|
11
|
+
constructor(opts?: {
|
|
12
|
+
indexKey?: string;
|
|
13
|
+
});
|
|
8
14
|
ready(): Promise<void>;
|
|
9
15
|
save(jobId: string, attachments: OutgoingAttachment[]): Promise<void>;
|
|
10
16
|
get(jobId: string): AttachmentPreview[] | undefined;
|
|
@@ -14,6 +20,12 @@ declare const expoHaptics: HapticsAPI;
|
|
|
14
20
|
|
|
15
21
|
declare class ExpoBundleStorage implements BundleStorage {
|
|
16
22
|
private dirReady;
|
|
23
|
+
private indexKey;
|
|
24
|
+
/** Pass `indexKey` when migrating from an app that already persisted a
|
|
25
|
+
* bundle index under a different key — keeps the existing cache warm. */
|
|
26
|
+
constructor(opts?: {
|
|
27
|
+
indexKey?: string;
|
|
28
|
+
});
|
|
17
29
|
private ensureDir;
|
|
18
30
|
loadIndex(): Promise<Record<string, BundleCacheEntry>>;
|
|
19
31
|
saveIndex(index: Record<string, BundleCacheEntry>): Promise<void>;
|
|
@@ -35,6 +47,10 @@ declare function setupExpoProviders(opts?: {
|
|
|
35
47
|
/** Extra modules remote bundles may require(), e.g. react-native-webview.
|
|
36
48
|
* Static imports must live in the HOST (it owns the native build). */
|
|
37
49
|
bundleModules?: Record<string, any>;
|
|
50
|
+
/** AsyncStorage key overrides — pass your pre-SDK keys when migrating an
|
|
51
|
+
* existing app so users keep their attachment previews / bundle cache. */
|
|
52
|
+
attachmentIndexKey?: string;
|
|
53
|
+
bundleIndexKey?: string;
|
|
38
54
|
}): {
|
|
39
55
|
attachmentCache: ExpoAttachmentCache;
|
|
40
56
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { HapticsAPI, BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/pl
|
|
|
4
4
|
declare class ExpoAttachmentCache implements AttachmentCache {
|
|
5
5
|
private index;
|
|
6
6
|
private loaded;
|
|
7
|
-
|
|
7
|
+
private indexKey;
|
|
8
|
+
/** Pass `indexKey` when migrating from an app that already persisted an
|
|
9
|
+
* index under a different AsyncStorage key — otherwise existing users
|
|
10
|
+
* lose every cached preview on upgrade. */
|
|
11
|
+
constructor(opts?: {
|
|
12
|
+
indexKey?: string;
|
|
13
|
+
});
|
|
8
14
|
ready(): Promise<void>;
|
|
9
15
|
save(jobId: string, attachments: OutgoingAttachment[]): Promise<void>;
|
|
10
16
|
get(jobId: string): AttachmentPreview[] | undefined;
|
|
@@ -14,6 +20,12 @@ declare const expoHaptics: HapticsAPI;
|
|
|
14
20
|
|
|
15
21
|
declare class ExpoBundleStorage implements BundleStorage {
|
|
16
22
|
private dirReady;
|
|
23
|
+
private indexKey;
|
|
24
|
+
/** Pass `indexKey` when migrating from an app that already persisted a
|
|
25
|
+
* bundle index under a different key — keeps the existing cache warm. */
|
|
26
|
+
constructor(opts?: {
|
|
27
|
+
indexKey?: string;
|
|
28
|
+
});
|
|
17
29
|
private ensureDir;
|
|
18
30
|
loadIndex(): Promise<Record<string, BundleCacheEntry>>;
|
|
19
31
|
saveIndex(index: Record<string, BundleCacheEntry>): Promise<void>;
|
|
@@ -35,6 +47,10 @@ declare function setupExpoProviders(opts?: {
|
|
|
35
47
|
/** Extra modules remote bundles may require(), e.g. react-native-webview.
|
|
36
48
|
* Static imports must live in the HOST (it owns the native build). */
|
|
37
49
|
bundleModules?: Record<string, any>;
|
|
50
|
+
/** AsyncStorage key overrides — pass your pre-SDK keys when migrating an
|
|
51
|
+
* existing app so users keep their attachment previews / bundle cache. */
|
|
52
|
+
attachmentIndexKey?: string;
|
|
53
|
+
bundleIndexKey?: string;
|
|
38
54
|
}): {
|
|
39
55
|
attachmentCache: ExpoAttachmentCache;
|
|
40
56
|
};
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
39
39
|
var ExpoWebBrowser = __toESM(require("expo-web-browser"));
|
|
40
40
|
var ExpoLinking = __toESM(require("expo-linking"));
|
|
41
41
|
var ExpoClipboard = __toESM(require("expo-clipboard"));
|
|
42
|
+
var import_async_storage3 = __toESM(require("@react-native-async-storage/async-storage"));
|
|
43
|
+
var import_core = require("@ateam-ai-mobile/core");
|
|
42
44
|
var import_plugin_runtime = require("@ateam-ai-mobile/plugin-runtime");
|
|
43
45
|
var import_ui = require("@ateam-ai-mobile/ui");
|
|
44
46
|
|
|
@@ -73,10 +75,13 @@ var expoHaptics = {
|
|
|
73
75
|
var FileSystem = __toESM(require("expo-file-system/legacy"));
|
|
74
76
|
var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
|
|
75
77
|
var CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;
|
|
76
|
-
var
|
|
78
|
+
var DEFAULT_CACHE_INDEX_KEY = "adas-plugin-bundles-index-v1";
|
|
77
79
|
var ExpoBundleStorage = class {
|
|
78
|
-
|
|
80
|
+
/** Pass `indexKey` when migrating from an app that already persisted a
|
|
81
|
+
* bundle index under a different key — keeps the existing cache warm. */
|
|
82
|
+
constructor(opts) {
|
|
79
83
|
this.dirReady = false;
|
|
84
|
+
this.indexKey = opts?.indexKey || DEFAULT_CACHE_INDEX_KEY;
|
|
80
85
|
}
|
|
81
86
|
async ensureDir() {
|
|
82
87
|
if (this.dirReady) return;
|
|
@@ -92,7 +97,7 @@ var ExpoBundleStorage = class {
|
|
|
92
97
|
}
|
|
93
98
|
async loadIndex() {
|
|
94
99
|
try {
|
|
95
|
-
const raw = await import_async_storage.default.getItem(
|
|
100
|
+
const raw = await import_async_storage.default.getItem(this.indexKey);
|
|
96
101
|
return raw ? JSON.parse(raw) : {};
|
|
97
102
|
} catch {
|
|
98
103
|
return {};
|
|
@@ -100,7 +105,7 @@ var ExpoBundleStorage = class {
|
|
|
100
105
|
}
|
|
101
106
|
async saveIndex(index) {
|
|
102
107
|
try {
|
|
103
|
-
await import_async_storage.default.setItem(
|
|
108
|
+
await import_async_storage.default.setItem(this.indexKey, JSON.stringify(index));
|
|
104
109
|
} catch (err) {
|
|
105
110
|
console.warn("[ExpoBundleStorage] index save error:", err);
|
|
106
111
|
}
|
|
@@ -127,7 +132,7 @@ var ExpoBundleStorage = class {
|
|
|
127
132
|
async clearAll() {
|
|
128
133
|
await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {
|
|
129
134
|
});
|
|
130
|
-
await import_async_storage.default.removeItem(
|
|
135
|
+
await import_async_storage.default.removeItem(this.indexKey).catch(() => {
|
|
131
136
|
});
|
|
132
137
|
this.dirReady = false;
|
|
133
138
|
}
|
|
@@ -136,17 +141,22 @@ var ExpoBundleStorage = class {
|
|
|
136
141
|
// src/attachmentCache.ts
|
|
137
142
|
var FileSystem2 = __toESM(require("expo-file-system/legacy"));
|
|
138
143
|
var import_async_storage2 = __toESM(require("@react-native-async-storage/async-storage"));
|
|
139
|
-
var
|
|
144
|
+
var DEFAULT_INDEX_KEY = "adas-chat-attachment-index-v1";
|
|
140
145
|
var DIR = `${FileSystem2.documentDirectory}chat-attachments/`;
|
|
141
146
|
var MAX_JOBS = 100;
|
|
142
147
|
var ExpoAttachmentCache = class {
|
|
143
|
-
|
|
148
|
+
/** Pass `indexKey` when migrating from an app that already persisted an
|
|
149
|
+
* index under a different AsyncStorage key — otherwise existing users
|
|
150
|
+
* lose every cached preview on upgrade. */
|
|
151
|
+
constructor(opts) {
|
|
144
152
|
this.index = {};
|
|
153
|
+
const indexKey = opts?.indexKey || DEFAULT_INDEX_KEY;
|
|
154
|
+
this.indexKey = indexKey;
|
|
145
155
|
this.loaded = (async () => {
|
|
146
156
|
try {
|
|
147
157
|
const info = await FileSystem2.getInfoAsync(DIR);
|
|
148
158
|
if (!info.exists) await FileSystem2.makeDirectoryAsync(DIR, { intermediates: true });
|
|
149
|
-
const raw = await import_async_storage2.default.getItem(
|
|
159
|
+
const raw = await import_async_storage2.default.getItem(indexKey);
|
|
150
160
|
if (raw) this.index = JSON.parse(raw);
|
|
151
161
|
} catch {
|
|
152
162
|
}
|
|
@@ -181,7 +191,7 @@ var ExpoAttachmentCache = class {
|
|
|
181
191
|
}
|
|
182
192
|
}
|
|
183
193
|
try {
|
|
184
|
-
await import_async_storage2.default.setItem(
|
|
194
|
+
await import_async_storage2.default.setItem(this.indexKey, JSON.stringify(this.index));
|
|
185
195
|
} catch {
|
|
186
196
|
}
|
|
187
197
|
}
|
|
@@ -199,7 +209,7 @@ var ExpoAttachmentCache = class {
|
|
|
199
209
|
// src/index.ts
|
|
200
210
|
function setupExpoProviders(opts) {
|
|
201
211
|
(0, import_plugin_runtime.setHapticsProvider)(expoHaptics);
|
|
202
|
-
(0, import_plugin_runtime.setBundleStorage)(new ExpoBundleStorage());
|
|
212
|
+
(0, import_plugin_runtime.setBundleStorage)(new ExpoBundleStorage({ indexKey: opts?.bundleIndexKey }));
|
|
203
213
|
(0, import_ui.setClipboardProvider)({
|
|
204
214
|
setString: (text) => ExpoClipboard.setStringAsync(text).then(() => {
|
|
205
215
|
})
|
|
@@ -209,7 +219,11 @@ function setupExpoProviders(opts) {
|
|
|
209
219
|
"expo-linking": ExpoLinking,
|
|
210
220
|
...opts?.bundleModules || {}
|
|
211
221
|
});
|
|
212
|
-
|
|
222
|
+
(0, import_core.setAcsCacheStorage)({
|
|
223
|
+
getItem: (k) => import_async_storage3.default.getItem(k),
|
|
224
|
+
setItem: (k, v) => import_async_storage3.default.setItem(k, v)
|
|
225
|
+
});
|
|
226
|
+
return { attachmentCache: new ExpoAttachmentCache({ indexKey: opts?.attachmentIndexKey }) };
|
|
213
227
|
}
|
|
214
228
|
// Annotate the CommonJS export names for ESM import in node:
|
|
215
229
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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"]}
|
|
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 AsyncStorage from '@react-native-async-storage/async-storage';\nimport { setAcsCacheStorage } from '@ateam-ai-mobile/core';\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 /** AsyncStorage key overrides — pass your pre-SDK keys when migrating an\n * existing app so users keep their attachment previews / bundle cache. */\n attachmentIndexKey?: string;\n bundleIndexKey?: string;\n}): { attachmentCache: ExpoAttachmentCache } {\n setHapticsProvider(expoHaptics);\n setBundleStorage(new ExpoBundleStorage({ indexKey: opts?.bundleIndexKey }));\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 // Persist the ACS home-actions feed across cold starts.\n setAcsCacheStorage({\n getItem: (k: string) => AsyncStorage.getItem(k),\n setItem: (k: string, v: string) => AsyncStorage.setItem(k, v),\n });\n return { attachmentCache: new ExpoAttachmentCache({ indexKey: opts?.attachmentIndexKey }) };\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 DEFAULT_CACHE_INDEX_KEY = 'adas-plugin-bundles-index-v1';\n\nexport class ExpoBundleStorage implements BundleStorage {\n private dirReady = false;\n private indexKey: string;\n\n /** Pass `indexKey` when migrating from an app that already persisted a\n * bundle index under a different key — keeps the existing cache warm. */\n constructor(opts?: { indexKey?: string }) {\n this.indexKey = opts?.indexKey || DEFAULT_CACHE_INDEX_KEY;\n }\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(this.indexKey);\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(this.indexKey, 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(this.indexKey).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 DEFAULT_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 private indexKey: string;\n\n /** Pass `indexKey` when migrating from an app that already persisted an\n * index under a different AsyncStorage key — otherwise existing users\n * lose every cached preview on upgrade. */\n constructor(opts?: { indexKey?: string }) {\n const indexKey = opts?.indexKey || DEFAULT_INDEX_KEY;\n this.indexKey = indexKey;\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(indexKey);\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(this.indexKey, 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":["import_async_storage","AsyncStorage","FileSystem","import_async_storage","AsyncStorage","AsyncStorage"]}
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import * as ExpoWebBrowser from "expo-web-browser";
|
|
3
3
|
import * as ExpoLinking from "expo-linking";
|
|
4
4
|
import * as ExpoClipboard from "expo-clipboard";
|
|
5
|
+
import AsyncStorage3 from "@react-native-async-storage/async-storage";
|
|
6
|
+
import { setAcsCacheStorage } from "@ateam-ai-mobile/core";
|
|
5
7
|
import {
|
|
6
8
|
setHapticsProvider,
|
|
7
9
|
setBundleStorage,
|
|
@@ -40,10 +42,13 @@ var expoHaptics = {
|
|
|
40
42
|
import * as FileSystem from "expo-file-system/legacy";
|
|
41
43
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
42
44
|
var CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;
|
|
43
|
-
var
|
|
45
|
+
var DEFAULT_CACHE_INDEX_KEY = "adas-plugin-bundles-index-v1";
|
|
44
46
|
var ExpoBundleStorage = class {
|
|
45
|
-
|
|
47
|
+
/** Pass `indexKey` when migrating from an app that already persisted a
|
|
48
|
+
* bundle index under a different key — keeps the existing cache warm. */
|
|
49
|
+
constructor(opts) {
|
|
46
50
|
this.dirReady = false;
|
|
51
|
+
this.indexKey = opts?.indexKey || DEFAULT_CACHE_INDEX_KEY;
|
|
47
52
|
}
|
|
48
53
|
async ensureDir() {
|
|
49
54
|
if (this.dirReady) return;
|
|
@@ -59,7 +64,7 @@ var ExpoBundleStorage = class {
|
|
|
59
64
|
}
|
|
60
65
|
async loadIndex() {
|
|
61
66
|
try {
|
|
62
|
-
const raw = await AsyncStorage.getItem(
|
|
67
|
+
const raw = await AsyncStorage.getItem(this.indexKey);
|
|
63
68
|
return raw ? JSON.parse(raw) : {};
|
|
64
69
|
} catch {
|
|
65
70
|
return {};
|
|
@@ -67,7 +72,7 @@ var ExpoBundleStorage = class {
|
|
|
67
72
|
}
|
|
68
73
|
async saveIndex(index) {
|
|
69
74
|
try {
|
|
70
|
-
await AsyncStorage.setItem(
|
|
75
|
+
await AsyncStorage.setItem(this.indexKey, JSON.stringify(index));
|
|
71
76
|
} catch (err) {
|
|
72
77
|
console.warn("[ExpoBundleStorage] index save error:", err);
|
|
73
78
|
}
|
|
@@ -94,7 +99,7 @@ var ExpoBundleStorage = class {
|
|
|
94
99
|
async clearAll() {
|
|
95
100
|
await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {
|
|
96
101
|
});
|
|
97
|
-
await AsyncStorage.removeItem(
|
|
102
|
+
await AsyncStorage.removeItem(this.indexKey).catch(() => {
|
|
98
103
|
});
|
|
99
104
|
this.dirReady = false;
|
|
100
105
|
}
|
|
@@ -103,17 +108,22 @@ var ExpoBundleStorage = class {
|
|
|
103
108
|
// src/attachmentCache.ts
|
|
104
109
|
import * as FileSystem2 from "expo-file-system/legacy";
|
|
105
110
|
import AsyncStorage2 from "@react-native-async-storage/async-storage";
|
|
106
|
-
var
|
|
111
|
+
var DEFAULT_INDEX_KEY = "adas-chat-attachment-index-v1";
|
|
107
112
|
var DIR = `${FileSystem2.documentDirectory}chat-attachments/`;
|
|
108
113
|
var MAX_JOBS = 100;
|
|
109
114
|
var ExpoAttachmentCache = class {
|
|
110
|
-
|
|
115
|
+
/** Pass `indexKey` when migrating from an app that already persisted an
|
|
116
|
+
* index under a different AsyncStorage key — otherwise existing users
|
|
117
|
+
* lose every cached preview on upgrade. */
|
|
118
|
+
constructor(opts) {
|
|
111
119
|
this.index = {};
|
|
120
|
+
const indexKey = opts?.indexKey || DEFAULT_INDEX_KEY;
|
|
121
|
+
this.indexKey = indexKey;
|
|
112
122
|
this.loaded = (async () => {
|
|
113
123
|
try {
|
|
114
124
|
const info = await FileSystem2.getInfoAsync(DIR);
|
|
115
125
|
if (!info.exists) await FileSystem2.makeDirectoryAsync(DIR, { intermediates: true });
|
|
116
|
-
const raw = await AsyncStorage2.getItem(
|
|
126
|
+
const raw = await AsyncStorage2.getItem(indexKey);
|
|
117
127
|
if (raw) this.index = JSON.parse(raw);
|
|
118
128
|
} catch {
|
|
119
129
|
}
|
|
@@ -148,7 +158,7 @@ var ExpoAttachmentCache = class {
|
|
|
148
158
|
}
|
|
149
159
|
}
|
|
150
160
|
try {
|
|
151
|
-
await AsyncStorage2.setItem(
|
|
161
|
+
await AsyncStorage2.setItem(this.indexKey, JSON.stringify(this.index));
|
|
152
162
|
} catch {
|
|
153
163
|
}
|
|
154
164
|
}
|
|
@@ -166,7 +176,7 @@ var ExpoAttachmentCache = class {
|
|
|
166
176
|
// src/index.ts
|
|
167
177
|
function setupExpoProviders(opts) {
|
|
168
178
|
setHapticsProvider(expoHaptics);
|
|
169
|
-
setBundleStorage(new ExpoBundleStorage());
|
|
179
|
+
setBundleStorage(new ExpoBundleStorage({ indexKey: opts?.bundleIndexKey }));
|
|
170
180
|
setClipboardProvider({
|
|
171
181
|
setString: (text) => ExpoClipboard.setStringAsync(text).then(() => {
|
|
172
182
|
})
|
|
@@ -176,7 +186,11 @@ function setupExpoProviders(opts) {
|
|
|
176
186
|
"expo-linking": ExpoLinking,
|
|
177
187
|
...opts?.bundleModules || {}
|
|
178
188
|
});
|
|
179
|
-
|
|
189
|
+
setAcsCacheStorage({
|
|
190
|
+
getItem: (k) => AsyncStorage3.getItem(k),
|
|
191
|
+
setItem: (k, v) => AsyncStorage3.setItem(k, v)
|
|
192
|
+
});
|
|
193
|
+
return { attachmentCache: new ExpoAttachmentCache({ indexKey: opts?.attachmentIndexKey }) };
|
|
180
194
|
}
|
|
181
195
|
export {
|
|
182
196
|
ExpoAttachmentCache,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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 AsyncStorage from '@react-native-async-storage/async-storage';\nimport { setAcsCacheStorage } from '@ateam-ai-mobile/core';\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 /** AsyncStorage key overrides — pass your pre-SDK keys when migrating an\n * existing app so users keep their attachment previews / bundle cache. */\n attachmentIndexKey?: string;\n bundleIndexKey?: string;\n}): { attachmentCache: ExpoAttachmentCache } {\n setHapticsProvider(expoHaptics);\n setBundleStorage(new ExpoBundleStorage({ indexKey: opts?.bundleIndexKey }));\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 // Persist the ACS home-actions feed across cold starts.\n setAcsCacheStorage({\n getItem: (k: string) => AsyncStorage.getItem(k),\n setItem: (k: string, v: string) => AsyncStorage.setItem(k, v),\n });\n return { attachmentCache: new ExpoAttachmentCache({ indexKey: opts?.attachmentIndexKey }) };\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 DEFAULT_CACHE_INDEX_KEY = 'adas-plugin-bundles-index-v1';\n\nexport class ExpoBundleStorage implements BundleStorage {\n private dirReady = false;\n private indexKey: string;\n\n /** Pass `indexKey` when migrating from an app that already persisted a\n * bundle index under a different key — keeps the existing cache warm. */\n constructor(opts?: { indexKey?: string }) {\n this.indexKey = opts?.indexKey || DEFAULT_CACHE_INDEX_KEY;\n }\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(this.indexKey);\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(this.indexKey, 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(this.indexKey).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 DEFAULT_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 private indexKey: string;\n\n /** Pass `indexKey` when migrating from an app that already persisted an\n * index under a different AsyncStorage key — otherwise existing users\n * lose every cached preview on upgrade. */\n constructor(opts?: { indexKey?: string }) {\n const indexKey = opts?.indexKey || DEFAULT_INDEX_KEY;\n this.indexKey = indexKey;\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(indexKey);\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(this.indexKey, 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","AsyncStorage","AsyncStorage"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ateam-ai-mobile/expo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Expo-backed providers for the ADAS SDK \u2014 haptics, bundle disk cache, attachment cache, device bridge.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"build": "tsup"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@ateam-ai-mobile/core": "0.1.
|
|
13
|
-
"@ateam-ai-mobile/plugin-runtime": "0.1.
|
|
14
|
-
"@ateam-ai-mobile/ui": "0.1.
|
|
12
|
+
"@ateam-ai-mobile/core": "0.1.1",
|
|
13
|
+
"@ateam-ai-mobile/plugin-runtime": "0.1.1",
|
|
14
|
+
"@ateam-ai-mobile/ui": "0.1.1"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"@react-native-async-storage/async-storage": "*",
|
package/src/attachmentCache.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as FileSystem from 'expo-file-system/legacy';
|
|
|
7
7
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
8
|
import type { AttachmentCache, AttachmentPreview, OutgoingAttachment } from '@ateam-ai-mobile/core';
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const DEFAULT_INDEX_KEY = 'adas-chat-attachment-index-v1';
|
|
11
11
|
const DIR = `${FileSystem.documentDirectory}chat-attachments/`;
|
|
12
12
|
const MAX_JOBS = 100;
|
|
13
13
|
|
|
@@ -16,13 +16,19 @@ interface CachedAttachment { mimeType: string; name: string; filePath: string }
|
|
|
16
16
|
export class ExpoAttachmentCache implements AttachmentCache {
|
|
17
17
|
private index: Record<string, CachedAttachment[]> = {};
|
|
18
18
|
private loaded: Promise<void>;
|
|
19
|
+
private indexKey: string;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
/** Pass `indexKey` when migrating from an app that already persisted an
|
|
22
|
+
* index under a different AsyncStorage key — otherwise existing users
|
|
23
|
+
* lose every cached preview on upgrade. */
|
|
24
|
+
constructor(opts?: { indexKey?: string }) {
|
|
25
|
+
const indexKey = opts?.indexKey || DEFAULT_INDEX_KEY;
|
|
26
|
+
this.indexKey = indexKey;
|
|
21
27
|
this.loaded = (async () => {
|
|
22
28
|
try {
|
|
23
29
|
const info = await FileSystem.getInfoAsync(DIR);
|
|
24
30
|
if (!info.exists) await FileSystem.makeDirectoryAsync(DIR, { intermediates: true });
|
|
25
|
-
const raw = await AsyncStorage.getItem(
|
|
31
|
+
const raw = await AsyncStorage.getItem(indexKey);
|
|
26
32
|
if (raw) this.index = JSON.parse(raw);
|
|
27
33
|
} catch {}
|
|
28
34
|
})();
|
|
@@ -59,7 +65,7 @@ export class ExpoAttachmentCache implements AttachmentCache {
|
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
try {
|
|
62
|
-
await AsyncStorage.setItem(
|
|
68
|
+
await AsyncStorage.setItem(this.indexKey, JSON.stringify(this.index));
|
|
63
69
|
} catch {}
|
|
64
70
|
}
|
|
65
71
|
|
package/src/bundleStorage.ts
CHANGED
|
@@ -8,10 +8,17 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
|
8
8
|
import type { BundleStorage, BundleCacheEntry } from '@ateam-ai-mobile/plugin-runtime';
|
|
9
9
|
|
|
10
10
|
const CACHE_DIR = `${FileSystem.cacheDirectory}plugin-bundles/`;
|
|
11
|
-
const
|
|
11
|
+
const DEFAULT_CACHE_INDEX_KEY = 'adas-plugin-bundles-index-v1';
|
|
12
12
|
|
|
13
13
|
export class ExpoBundleStorage implements BundleStorage {
|
|
14
14
|
private dirReady = false;
|
|
15
|
+
private indexKey: string;
|
|
16
|
+
|
|
17
|
+
/** Pass `indexKey` when migrating from an app that already persisted a
|
|
18
|
+
* bundle index under a different key — keeps the existing cache warm. */
|
|
19
|
+
constructor(opts?: { indexKey?: string }) {
|
|
20
|
+
this.indexKey = opts?.indexKey || DEFAULT_CACHE_INDEX_KEY;
|
|
21
|
+
}
|
|
15
22
|
|
|
16
23
|
private async ensureDir(): Promise<void> {
|
|
17
24
|
if (this.dirReady) return;
|
|
@@ -28,7 +35,7 @@ export class ExpoBundleStorage implements BundleStorage {
|
|
|
28
35
|
|
|
29
36
|
async loadIndex(): Promise<Record<string, BundleCacheEntry>> {
|
|
30
37
|
try {
|
|
31
|
-
const raw = await AsyncStorage.getItem(
|
|
38
|
+
const raw = await AsyncStorage.getItem(this.indexKey);
|
|
32
39
|
return raw ? JSON.parse(raw) : {};
|
|
33
40
|
} catch {
|
|
34
41
|
return {};
|
|
@@ -37,7 +44,7 @@ export class ExpoBundleStorage implements BundleStorage {
|
|
|
37
44
|
|
|
38
45
|
async saveIndex(index: Record<string, BundleCacheEntry>): Promise<void> {
|
|
39
46
|
try {
|
|
40
|
-
await AsyncStorage.setItem(
|
|
47
|
+
await AsyncStorage.setItem(this.indexKey, JSON.stringify(index));
|
|
41
48
|
} catch (err) {
|
|
42
49
|
console.warn('[ExpoBundleStorage] index save error:', err);
|
|
43
50
|
}
|
|
@@ -66,7 +73,7 @@ export class ExpoBundleStorage implements BundleStorage {
|
|
|
66
73
|
|
|
67
74
|
async clearAll(): Promise<void> {
|
|
68
75
|
await FileSystem.deleteAsync(CACHE_DIR, { idempotent: true }).catch(() => {});
|
|
69
|
-
await AsyncStorage.removeItem(
|
|
76
|
+
await AsyncStorage.removeItem(this.indexKey).catch(() => {});
|
|
70
77
|
this.dirReady = false;
|
|
71
78
|
}
|
|
72
79
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import * as ExpoWebBrowser from 'expo-web-browser';
|
|
10
10
|
import * as ExpoLinking from 'expo-linking';
|
|
11
11
|
import * as ExpoClipboard from 'expo-clipboard';
|
|
12
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
13
|
+
import { setAcsCacheStorage } from '@ateam-ai-mobile/core';
|
|
12
14
|
import {
|
|
13
15
|
setHapticsProvider,
|
|
14
16
|
setBundleStorage,
|
|
@@ -41,9 +43,13 @@ export function setupExpoProviders(opts?: {
|
|
|
41
43
|
/** Extra modules remote bundles may require(), e.g. react-native-webview.
|
|
42
44
|
* Static imports must live in the HOST (it owns the native build). */
|
|
43
45
|
bundleModules?: Record<string, any>;
|
|
46
|
+
/** AsyncStorage key overrides — pass your pre-SDK keys when migrating an
|
|
47
|
+
* existing app so users keep their attachment previews / bundle cache. */
|
|
48
|
+
attachmentIndexKey?: string;
|
|
49
|
+
bundleIndexKey?: string;
|
|
44
50
|
}): { attachmentCache: ExpoAttachmentCache } {
|
|
45
51
|
setHapticsProvider(expoHaptics);
|
|
46
|
-
setBundleStorage(new ExpoBundleStorage());
|
|
52
|
+
setBundleStorage(new ExpoBundleStorage({ indexKey: opts?.bundleIndexKey }));
|
|
47
53
|
setClipboardProvider({
|
|
48
54
|
setString: (text: string) => ExpoClipboard.setStringAsync(text).then(() => {}),
|
|
49
55
|
});
|
|
@@ -52,5 +58,10 @@ export function setupExpoProviders(opts?: {
|
|
|
52
58
|
'expo-linking': ExpoLinking,
|
|
53
59
|
...(opts?.bundleModules || {}),
|
|
54
60
|
});
|
|
55
|
-
|
|
61
|
+
// Persist the ACS home-actions feed across cold starts.
|
|
62
|
+
setAcsCacheStorage({
|
|
63
|
+
getItem: (k: string) => AsyncStorage.getItem(k),
|
|
64
|
+
setItem: (k: string, v: string) => AsyncStorage.setItem(k, v),
|
|
65
|
+
});
|
|
66
|
+
return { attachmentCache: new ExpoAttachmentCache({ indexKey: opts?.attachmentIndexKey }) };
|
|
56
67
|
}
|