@ateam-ai-mobile/expo 0.1.5 → 0.1.8
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/android/build.gradle +16 -0
- package/android/src/main/java/expo/modules/adavision/AdaVisionModule.kt +75 -0
- package/dist/attachmentCache-DhQTqi2E.d.mts +18 -0
- package/dist/attachmentCache-DhQTqi2E.d.ts +18 -0
- package/dist/bootstrap.d.mts +95 -0
- package/dist/bootstrap.d.ts +95 -0
- package/dist/bootstrap.js +2991 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bootstrap.mjs +66 -0
- package/dist/bootstrap.mjs.map +1 -0
- package/dist/chunk-4BMWVN25.mjs +91 -0
- package/dist/chunk-4BMWVN25.mjs.map +1 -0
- package/dist/chunk-6LXF3JVX.mjs +96 -0
- package/dist/chunk-6LXF3JVX.mjs.map +1 -0
- package/dist/chunk-7FWYEHN5.mjs +203 -0
- package/dist/chunk-7FWYEHN5.mjs.map +1 -0
- package/dist/chunk-C7M5WAHA.mjs +1798 -0
- package/dist/chunk-C7M5WAHA.mjs.map +1 -0
- package/dist/chunk-FF6ZZZXH.mjs +71 -0
- package/dist/chunk-FF6ZZZXH.mjs.map +1 -0
- package/dist/chunk-GF2MPTAB.mjs +647 -0
- package/dist/chunk-GF2MPTAB.mjs.map +1 -0
- package/dist/chunk-J6XRNX7P.mjs +19 -0
- package/dist/chunk-J6XRNX7P.mjs.map +1 -0
- package/dist/chunk-KOXF2FZM.mjs +92 -0
- package/dist/chunk-KOXF2FZM.mjs.map +1 -0
- package/dist/chunk-L4V7OCJ3.mjs +71 -0
- package/dist/chunk-L4V7OCJ3.mjs.map +1 -0
- package/dist/chunk-PCAEUX2N.mjs +67 -0
- package/dist/chunk-PCAEUX2N.mjs.map +1 -0
- package/dist/chunk-WMJKH4XE.mjs +34 -0
- package/dist/chunk-WMJKH4XE.mjs.map +1 -0
- package/dist/chunk-XQSY6MHP.mjs +213 -0
- package/dist/chunk-XQSY6MHP.mjs.map +1 -0
- package/dist/chunk-Z5AI2SXX.mjs +1810 -0
- package/dist/chunk-Z5AI2SXX.mjs.map +1 -0
- package/dist/createBridge.js +41 -10
- package/dist/createBridge.js.map +1 -1
- package/dist/createBridge.mjs +7 -1
- package/dist/createBridge.mjs.map +1 -1
- package/dist/device/createDeviceProvider.d.mts +23 -0
- package/dist/device/createDeviceProvider.d.ts +23 -0
- package/dist/device/createDeviceProvider.js +1228 -0
- package/dist/device/createDeviceProvider.js.map +1 -0
- package/dist/device/createDeviceProvider.mjs +19 -0
- package/dist/device/createDeviceProvider.mjs.map +1 -0
- package/dist/device-bridge/collectors/appEngagement.d.mts +10 -0
- package/dist/device-bridge/collectors/appEngagement.d.ts +10 -0
- package/dist/device-bridge/collectors/appEngagement.js +128 -0
- package/dist/device-bridge/collectors/appEngagement.js.map +1 -0
- package/dist/device-bridge/collectors/appEngagement.mjs +12 -0
- package/dist/device-bridge/collectors/appEngagement.mjs.map +1 -0
- package/dist/device-bridge/collectors/health.d.mts +24 -0
- package/dist/device-bridge/collectors/health.d.ts +24 -0
- package/dist/device-bridge/collectors/health.js +238 -0
- package/dist/device-bridge/collectors/health.js.map +1 -0
- package/dist/device-bridge/collectors/health.mjs +18 -0
- package/dist/device-bridge/collectors/health.mjs.map +1 -0
- package/dist/device-bridge/cookies.d.mts +49 -0
- package/dist/device-bridge/cookies.d.ts +49 -0
- package/dist/device-bridge/cookies.js +90 -0
- package/dist/device-bridge/cookies.js.map +1 -0
- package/dist/device-bridge/cookies.mjs +14 -0
- package/dist/device-bridge/cookies.mjs.map +1 -0
- package/dist/device-bridge/index.d.mts +495 -0
- package/dist/device-bridge/index.d.ts +495 -0
- package/dist/device-bridge/index.js +2121 -0
- package/dist/device-bridge/index.js.map +1 -0
- package/dist/device-bridge/index.mjs +34 -0
- package/dist/device-bridge/index.mjs.map +1 -0
- package/dist/device-bridge/nfc.d.mts +30 -0
- package/dist/device-bridge/nfc.d.ts +30 -0
- package/dist/device-bridge/nfc.js +109 -0
- package/dist/device-bridge/nfc.js.map +1 -0
- package/dist/device-bridge/nfc.mjs +14 -0
- package/dist/device-bridge/nfc.mjs.map +1 -0
- package/dist/index.d.mts +509 -18
- package/dist/index.d.ts +509 -18
- package/dist/index.js +1706 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -194
- package/dist/index.mjs.map +1 -1
- package/dist/maps/index.d.mts +56 -0
- package/dist/maps/index.d.ts +56 -0
- package/dist/maps/index.js +95 -0
- package/dist/maps/index.js.map +1 -0
- package/dist/maps/index.mjs +17 -0
- package/dist/maps/index.mjs.map +1 -0
- package/dist/oauth/expoOAuth.d.mts +18 -0
- package/dist/oauth/expoOAuth.d.ts +18 -0
- package/dist/oauth/expoOAuth.js +102 -0
- package/dist/oauth/expoOAuth.js.map +1 -0
- package/dist/oauth/expoOAuth.mjs +8 -0
- package/dist/oauth/expoOAuth.mjs.map +1 -0
- package/dist/types-BRanJYTn.d.mts +138 -0
- package/dist/types-BRanJYTn.d.ts +138 -0
- package/dist/vision/index.d.mts +83 -0
- package/dist/vision/index.d.ts +83 -0
- package/dist/vision/index.js +101 -0
- package/dist/vision/index.js.map +1 -0
- package/dist/vision/index.mjs +28 -0
- package/dist/vision/index.mjs.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/AdaMaps/AdaMapsModule.swift +180 -0
- package/ios/AdaMaps.podspec +25 -0
- package/ios/AdaVision.podspec +32 -0
- package/ios/AdaVisionModule.swift +486 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/analytics/coremldata.bin +0 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/coremldata.bin +0 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/metadata.json +773 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/analytics/coremldata.bin +0 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/coremldata.bin +0 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/model.mil +720 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/weights/weight.bin +0 -0
- package/ios/Resources/yolov8n-oiv7.mlmodelc/model1/coremldata.bin +0 -0
- package/package.json +113 -7
- package/src/args.ts +56 -0
- package/src/bootstrap.ts +159 -0
- package/src/camera/CameraTaskHost.tsx +175 -0
- package/src/camera/cameraTasks.ts +129 -0
- package/src/createBridge.ts +3 -1
- package/src/device/createDeviceProvider.ts +917 -0
- package/src/device-bridge/actions/calendar-actions.ts +217 -0
- package/src/device-bridge/actions/dnd.ts +74 -0
- package/src/device-bridge/actions/health-actions.ts +58 -0
- package/src/device-bridge/actions/index.ts +12 -0
- package/src/device-bridge/actions/messaging.ts +74 -0
- package/src/device-bridge/actions/navigation.ts +88 -0
- package/src/device-bridge/actions/queue-poller.ts +151 -0
- package/src/device-bridge/background.ts +137 -0
- package/src/device-bridge/biometrics.ts +64 -0
- package/src/device-bridge/capabilities.ts +218 -0
- package/src/device-bridge/collectors/appEngagement.ts +120 -0
- package/src/device-bridge/collectors/battery.ts +83 -0
- package/src/device-bridge/collectors/calendar.ts +162 -0
- package/src/device-bridge/collectors/camera.ts +27 -0
- package/src/device-bridge/collectors/connectivity.ts +60 -0
- package/src/device-bridge/collectors/contacts.ts +200 -0
- package/src/device-bridge/collectors/health.ts +299 -0
- package/src/device-bridge/collectors/index.ts +12 -0
- package/src/device-bridge/collectors/location.ts +236 -0
- package/src/device-bridge/collectors/mood.ts +141 -0
- package/src/device-bridge/collectors/notifications.ts +70 -0
- package/src/device-bridge/cookies.ts +89 -0
- package/src/device-bridge/dynamic-connector.ts +422 -0
- package/src/device-bridge/index.ts +467 -0
- package/src/device-bridge/nfc.ts +85 -0
- package/src/device-bridge/plugin.ts +71 -0
- package/src/device-bridge/relay-client.ts +237 -0
- package/src/device-bridge/scanner.ts +42 -0
- package/src/device-bridge/sharing.ts +35 -0
- package/src/device-bridge/types.ts +326 -0
- package/src/device-state/activityClassifier.ts +151 -0
- package/src/device-state/activityEngine.ts +252 -0
- package/src/device-state/collectors.ts +35 -0
- package/src/device-state/domains/activity.ts +93 -0
- package/src/device-state/domains/capability.ts +60 -0
- package/src/device-state/domains/connectivity.ts +60 -0
- package/src/device-state/domains/deviceContext.ts +57 -0
- package/src/device-state/domains/interaction.ts +60 -0
- package/src/device-state/domains/spatial.ts +367 -0
- package/src/device-state/domains/visual.ts +121 -0
- package/src/device-state/events.ts +49 -0
- package/src/device-state/headingSource.ts +119 -0
- package/src/device-state/index.ts +78 -0
- package/src/device-state/quality.ts +60 -0
- package/src/device-state/replay.ts +285 -0
- package/src/device-state/spatialEvents.ts +300 -0
- package/src/device-state/turnDetector.ts +121 -0
- package/src/device-state/visualEngine.ts +341 -0
- package/src/geo.ts +23 -0
- package/src/haptics.ts +4 -0
- package/src/index.ts +37 -0
- package/src/maps/index.ts +81 -0
- package/src/maps/normalize.ts +225 -0
- package/src/oauth/expoOAuth.tsx +113 -0
- package/src/vision/analysis.js +80 -0
- package/src/vision/index.ts +146 -0
- package/src/vision/ring.js +35 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/device/createDeviceProvider.ts"],"sourcesContent":["/**\n * SDK device-primitive provider (Expo-backed).\n *\n * `createDeviceProvider()` builds the FULL, UNGATED `PluginNative` implementation\n * a host registers once via `setPluginNativeProvider`. Per-plugin capability\n * gating and per-plugin filesystem scoping are applied downstream by\n * plugin-runtime's `gatePluginNative` — this module does not gate.\n *\n * Not a React hook: it holds no React state and must be safe to build at module\n * scope, including on a headless background wake before any UI exists.\n */\nimport * as Haptics from 'expo-haptics';\n// Both are already app dependencies and already used by the device-bridge\n// collectors — safe for eager import (unlike cookies/nfc, which need native\n// modules absent from Expo Go).\nimport * as ExpoLocation from 'expo-location';\nimport * as ImagePicker from 'expo-image-picker';\nimport * as Notifications from 'expo-notifications';\nimport * as DocumentPicker from 'expo-document-picker';\nimport * as FS from 'expo-file-system';\nimport * as SQLite from 'expo-sqlite';\n// The broker behind scanner.scanOnce() and camera.recordVideo(): both need a\n// MOUNTED camera, which a function call cannot provide.\nimport { cameraTasks } from '../camera/cameraTasks';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\n// native.vision: the on-device analyzer (ada-vision module) — resolve a frame id\n// to its local pixels, measure/read/classify on-device, never off the phone.\nimport * as adaVision from '../vision';\n// native.maps: place search + walking routes (ada-maps module, MapKit) and the\n// SDK's one reverse geocode. NOT on-device — queries go to Apple's servers.\nimport * as adaMaps from '../maps';\nimport { countArg, fractionArg, refuseArgument, shown } from '../args';\nimport {\n coordinateArg, mapsFailure, searchArgs, toMapsPlaces, toMapsRoute, toReverseGeocodedPlace,\n} from '../maps/normalize';\n// Device-bridge owns every native module. plugin-sdk delegates so RN\n// plugins never import native modules directly. When adding a new\n// capability: add it to device-bridge, import here, declare in types.ts,\n// wire in the factory below.\n//\n// IMPORTANT: cookies and nfc use native modules NOT available in Expo Go.\n// We lazy-import them so the app doesn't crash at load time.\n// Only biometrics (expo-local-authentication), sharing (expo-sharing),\n// and scanner (expo-camera) are safe for eager import.\nimport {\n biometrics as deviceBiometrics,\n sharing as deviceSharing,\n scanner as deviceScanner,\n} from '../device-bridge';\n\n// Lazy loaders for native modules not in Expo Go\nfunction getDeviceCookies() {\n try { return require('../device-bridge/cookies'); } catch { return null; }\n}\nfunction getDeviceNfc() {\n try { return require('../device-bridge/nfc'); } catch { return null; }\n}\nimport type {\n NativeCapabilities,\n PluginNative,\n CameraAPI,\n LocationAPI,\n StorageAPI,\n BiometricsAPI,\n BiometricsAPIv2,\n NotificationsAPI,\n HapticsAPI,\n ConnectivityAPI,\n FileSystemAPI,\n CookiesAPI,\n WebViewCookie,\n SharingAPI,\n ScannerAPI,\n NfcAPI,\n BluetoothAPI,\n VisionAPI,\n MapsAPI,\n} from '@ateam-ai-mobile/plugin-runtime';\n\nclass CapabilityUnavailableError extends Error {\n code = 'UNAVAILABLE' as const;\n constructor(capability: string, reason?: string) {\n super(reason || `${capability} is not yet implemented. Coming in Phase 2.`);\n this.name = 'CapabilityUnavailableError';\n }\n}\n\nclass CapabilityNotDeclaredError extends Error {\n code = 'NOT_DECLARED' as const;\n constructor(capability: string) {\n super(`Capability '${capability}' not declared in plugin manifest. Add it to the native.${capability} field.`);\n this.name = 'CapabilityNotDeclaredError';\n }\n}\n\nfunction stubApi<T extends object>(capability: string): T {\n return new Proxy({} as T, {\n get(_target, prop) {\n if (typeof prop === 'string') {\n return () => {\n throw new CapabilityUnavailableError(capability);\n };\n }\n },\n });\n}\n\n// ── Location (expo-location — was stubbed, is real now) ───────────\n//\n// `location` was wired to stubApi, so a plugin that correctly DECLARED\n// `location: true` still got \"location is not yet implemented. Coming in\n// Phase 2.\" on every call. expo-location has been a dependency all along and\n// the device-bridge collectors use it; only the plugin-facing API was missing.\n\nfunction createLocationAPI(): LocationAPI {\n /** Ask once, and say what the user can do about a refusal. */\n const ensurePermission = async (): Promise<void> => {\n const existing = await ExpoLocation.getForegroundPermissionsAsync();\n if (existing.status === 'granted') return;\n const asked = await ExpoLocation.requestForegroundPermissionsAsync();\n if (asked.status !== 'granted') {\n // A voice assistant reads this out, so it has to be actionable rather\n // than a status code.\n throw new Error(\n 'Location permission was not granted. Enable it for this app in Settings > Privacy > Location Services.',\n );\n }\n };\n\n return {\n async getCurrent(opts) {\n await ensurePermission();\n // 'high' costs battery and a second or two of settling; default balanced,\n // which is plenty for \"which street am I on\".\n const fix = await ExpoLocation.getCurrentPositionAsync({\n accuracy:\n opts?.accuracy === 'high'\n ? ExpoLocation.Accuracy.High\n : ExpoLocation.Accuracy.Balanced,\n });\n return {\n latitude: fix.coords.latitude,\n longitude: fix.coords.longitude,\n altitude: fix.coords.altitude ?? undefined,\n };\n },\n\n watchPosition(cb) {\n let subscription: { remove(): void } | null = null;\n let cancelled = false;\n\n void (async () => {\n try {\n await ensurePermission();\n if (cancelled) return;\n subscription = await ExpoLocation.watchPositionAsync(\n { accuracy: ExpoLocation.Accuracy.Balanced },\n fix => cb({ latitude: fix.coords.latitude, longitude: fix.coords.longitude }),\n );\n // Unsubscribed while we were still asking for permission — otherwise\n // the GPS stays on with nobody listening.\n if (cancelled) { subscription?.remove(); subscription = null; }\n } catch (err: any) {\n console.warn('[location] watchPosition failed:', err?.message || String(err));\n }\n })();\n\n return () => {\n cancelled = true;\n subscription?.remove();\n subscription = null;\n };\n },\n };\n}\n\n// ── Camera (expo-image-picker — was stubbed, is real now) ─────────\n//\n// Every method here opens the SYSTEM camera or picker UI, which is why they\n// are honest one-shot actions rather than a frame source: expo-camera can\n// deliver a live feed, but only through a MOUNTED CameraView component, which\n// a headless tool call has no way to mount. A continuous visual feed is the\n// semantic Visual domain's job (Device Bridge v2), not this API's.\n\nfunction createCameraAPI(): CameraAPI {\n const ensurePermission = async (): Promise<void> => {\n const existing = await ImagePicker.getCameraPermissionsAsync();\n if (existing.granted) return;\n const asked = await ImagePicker.requestCameraPermissionsAsync();\n if (!asked.granted) {\n throw new Error(\n 'Camera permission was not granted. Enable it for this app in Settings > Privacy > Camera.',\n );\n }\n };\n\n return {\n async takePhoto(opts) {\n await ensurePermission();\n const res = await ImagePicker.launchCameraAsync({\n quality: opts?.quality ?? 0.7,\n cameraType:\n opts?.facing === 'front'\n ? ImagePicker.CameraType.front\n : ImagePicker.CameraType.back,\n });\n // The user backing out is not an error — but it is also not a photo, so\n // it must not come back as an empty-looking success.\n if (res.canceled || !res.assets?.[0]) {\n throw new Error('No photo taken — the camera was dismissed.');\n }\n const a = res.assets[0];\n return { uri: a.uri, width: a.width, height: a.height };\n },\n\n async scanBarcode(opts) {\n // Kept refusing, and now the refusal has somewhere to send you. Scanning\n // is a mounted camera lifecycle, which is why it lives on `scanner`.\n // Silently forwarding would leave a misleading name in every plugin.\n throw new CapabilityUnavailableError(\n 'camera.scanBarcode',\n 'Use native.scanner.scanOnce() instead — scanning needs a mounted camera, ' +\n 'which this call cannot provide. Same arguments, and it returns { data, format }.' +\n (opts?.formats?.length ? ' Pass your formats through to scanOnce.' : ''),\n );\n },\n\n async recordVideo(opts) {\n // A FILE the user deliberately makes — not the Visual domain's frame\n // ring, which is ephemeral and nobody keeps. Runs on the shared camera\n // surface, so it throws the same coded failures as scanOnce.\n return cameraTasks.run('record', {\n maxDurationMs: opts?.maxDurationMs,\n withAudio: opts?.withAudio ?? false,\n });\n },\n\n async pickImage(opts) {\n const existing = await ImagePicker.getMediaLibraryPermissionsAsync();\n if (!existing.granted) {\n const asked = await ImagePicker.requestMediaLibraryPermissionsAsync();\n if (!asked.granted) {\n throw new Error(\n 'Photo library permission was not granted. Enable it for this app in Settings > Privacy > Photos.',\n );\n }\n }\n const res = await ImagePicker.launchImageLibraryAsync({\n allowsMultipleSelection: !!opts?.multiple,\n });\n if (res.canceled) return [];\n return (res.assets || []).map(a => ({ uri: a.uri }));\n },\n };\n}\n\n// ── Storage (AsyncStorage — was stubbed) ──────────────────────────\n//\n// KV is real. `db.execute` is NOT: expo-sqlite is not installed, and a fake\n// SQL layer over AsyncStorage would be a worse lie than an error — it would\n// accept a query and return an empty rowset, which reads as \"no rows matched\".\n\n/**\n * One SQLite file for plugin storage, opened lazily and once.\n *\n * Lazy because opening a database at module load costs startup time for every\n * user who never touches a plugin that needs one; once because two handles to\n * the same file is how you get a locked database under concurrent writes.\n */\n// Typed `any` for the handle: expo-sqlite is an any-shim in this SDK repo's\n// typecheck (the host supplies the real types), so `SQLite.SQLiteDatabase` is\n// not a nameable type here. Runtime is unchanged — SQLite.openDatabaseAsync\n// still returns a real SQLiteDatabase on device.\n// One handle PER plugin database file, opened lazily and once each. A Map (not a\n// single promise) because storage is now per-plugin: each plugin gets its own\n// file, and two handles to the same file is how you get a locked database.\nconst _pluginDbs = new Map<string, Promise<any>>();\nfunction openPluginDb(file: string): Promise<any> {\n const existing = _pluginDbs.get(file);\n if (existing) return existing;\n const opened: Promise<any> = SQLite.openDatabaseAsync(file);\n _pluginDbs.set(file, opened);\n return opened;\n}\n\n// The leading SQL keyword, uppercased, after stripping leading whitespace and\n// leading `-- line` / `/* block */` comments — so a comment cannot smuggle the\n// verb past the cross-file check (e.g. `/* */ ATTACH …`).\nfunction sqlLeadingKeyword(sql: string): string {\n let s = String(sql);\n for (;;) {\n const before = s;\n s = s.replace(/^\\s+/, '');\n if (s.startsWith('--')) { const nl = s.indexOf('\\n'); s = nl === -1 ? '' : s.slice(nl + 1); }\n else if (s.startsWith('/*')) { const end = s.indexOf('*/'); s = end === -1 ? '' : s.slice(end + 2); }\n if (s === before) break;\n }\n const m = s.match(/^[A-Za-z]+/);\n return m ? m[0].toUpperCase() : '';\n}\n\n// R3 SECURITY — per-plugin storage isolation. Both the KV namespace and the\n// SQLite database are scoped to the plugin's OWN id, so Plugin A cannot read,\n// overwrite, or query Plugin B's entries/tables. The id is threaded in from the\n// TRUSTED gate (storageFor(pluginId)) — never a caller-supplied prefix.\n//\n// The id is encoded with encodeURIComponent (not used raw) precisely because KV\n// is a FLAT concatenated keyspace with no structural boundary: a raw id like\n// `mcp:conn:plug` shares the `:` delimiter, so `@plugin:mcp:conn:plug:<k>` from\n// one plugin could collide with another plugin's id+key split. Encoding escapes\n// `:` (→ %3A), making the id segment unambiguous and the mapping injective, so\n// distinct ids can never produce overlapping namespaces regardless of the key.\n// (The filesystem sandbox can use the raw id as a directory name because a\n// directory boundary is structural, not delimiter-based.) The same encoded\n// token names the plugin's database file, giving each plugin a distinct DB.\nfunction createStorageAPI(pluginId: string): StorageAPI {\n const ns = encodeURIComponent(pluginId);\n const key = (k: string) => `@plugin:${ns}:${k}`;\n const dbFile = `plugin-storage-${ns}.db`;\n\n return {\n get: (k) => AsyncStorage.getItem(key(k)),\n set: (k, v) => AsyncStorage.setItem(key(k), v),\n delete: (k) => AsyncStorage.removeItem(key(k)),\n db: {\n async execute(sql: string, params?: any[]) {\n const verb = sqlLeadingKeyword(sql);\n // Per-plugin storage isolation rests on each plugin owning its OWN\n // database FILE. SQLite can cross files — ATTACH mounts another db,\n // VACUUM INTO writes one — so the file boundary only holds if those are\n // refused. Otherwise a plugin could `ATTACH '.../plugin-storage-<other\n // id>.db'` (the id is public and the filename deterministic) and read or\n // overwrite another plugin's tables. Refuse cross-file statements at the\n // trusted gate; a plugin never needs them for its own single database.\n // (runAsync/getAllAsync compile only the FIRST statement, so a trailing\n // `; ATTACH …` never runs — the leading verb is the whole surface.)\n // ATTACH/DETACH/VACUUM are the only cross-FILE statements in the standard\n // expo-sqlite build: loadable extensions are disabled and the file-vtab\n // modules (fileio/csv/zipfile) are not compiled in, so there is no other\n // way for a plugin's own connection to reach another plugin's file. If a\n // future SQLite build enables those, revisit (an allowlist would be the\n // durable form).\n if (verb === 'ATTACH' || verb === 'DETACH' || verb === 'VACUUM') {\n throw new CapabilityUnavailableError(\n 'storage.db',\n `SQL '${verb}' is not permitted: a plugin's storage is confined to its own database and cannot reach another file.`,\n );\n }\n const db = await openPluginDb(dbFile);\n // A statement either RETURNS ROWS or CHANGES THEM, and the two need\n // different calls. Deciding by the leading keyword rather than by\n // trying one and falling back: a failed INSERT retried as a query\n // would run it twice.\n const returnsRows = verb === 'SELECT' || verb === 'PRAGMA' || verb === 'WITH';\n\n if (returnsRows) {\n const rows = await db.getAllAsync(sql, (params ?? []) as any);\n // changes: 0 is the truth for a SELECT, not a placeholder.\n return { rows, changes: 0 };\n }\n const res = await db.runAsync(sql, (params ?? []) as any);\n // rows: [] is likewise true for a write — it did not select anything.\n return { rows: [], changes: res.changes ?? 0 };\n },\n },\n };\n}\n\n/**\n * Wipe ALL plugin storage — KV, SQLite, and the file sandbox — across EVERY\n * plugin. Per-plugin isolation (R3) keeps plugins apart from each other; this is\n * the USER boundary: on sign-out it must run so the NEXT user/tenant cannot read\n * the previous one's plugin data. Best-effort per store — a hiccup clearing one\n * must never block sign-out — so each part is guarded independently.\n */\nexport async function clearAllPluginData(): Promise<void> {\n // 1. KV — every `@plugin:` namespace (all plugins, all keys).\n try {\n const keys = await AsyncStorage.getAllKeys();\n const mine = keys.filter((k: string) => k.startsWith('@plugin:'));\n if (mine.length) await AsyncStorage.multiRemove(mine);\n } catch (e: any) {\n console.warn('[adas] clearAllPluginData: KV clear failed:', e?.message || e);\n }\n\n // 2. SQLite — close any open per-plugin handles, then delete the db files.\n try {\n for (const [, p] of _pluginDbs) {\n try { const db = await p; await db.closeAsync?.(); } catch { /* already closed */ }\n }\n _pluginDbs.clear();\n const sqliteDir = new FS.Directory(FS.Paths.document, 'SQLite');\n if (sqliteDir.exists) {\n for (const entry of sqliteDir.list()) {\n if (/^plugin-storage-.*\\.db$/.test(String(entry.name ?? ''))) {\n try { entry.delete(); } catch { /* best effort */ }\n }\n }\n }\n } catch (e: any) {\n console.warn('[adas] clearAllPluginData: SQLite clear failed:', e?.message || e);\n }\n\n // 3. Files — the whole plugin-files/ sandbox tree (all plugins).\n try {\n const dir = new FS.Directory(FS.Paths.document, 'plugin-files');\n if (dir.exists) dir.delete();\n } catch (e: any) {\n console.warn('[adas] clearAllPluginData: file sandbox clear failed:', e?.message || e);\n }\n}\n\n// ── Notifications (expo-notifications — was stubbed) ──────────────\n\nfunction createNotificationsAPI(): NotificationsAPI {\n return {\n async schedule({ title, body, trigger }) {\n const perm = await Notifications.getPermissionsAsync();\n if (!perm.granted) {\n const asked = await Notifications.requestPermissionsAsync();\n if (!asked.granted) {\n throw new Error(\n 'Notification permission was not granted. Enable it for this app in Settings > Notifications.',\n );\n }\n }\n // `seconds` and `date` are the two the contract declares; anything else\n // would be this API inventing a scheduling model of its own.\n const when: any =\n typeof trigger?.seconds === 'number'\n ? { type: 'timeInterval', seconds: trigger.seconds, repeats: false }\n : trigger?.date\n ? { type: 'date', date: new Date(trigger.date) }\n : null;\n if (!when) {\n throw new Error('schedule() needs trigger.seconds or trigger.date.');\n }\n return Notifications.scheduleNotificationAsync({\n content: { title, body },\n trigger: when,\n });\n },\n\n cancel: (id) => Notifications.cancelScheduledNotificationAsync(id),\n\n onReceived(cb) {\n const sub = Notifications.addNotificationReceivedListener(n => {\n cb({\n id: n.request.identifier,\n title: n.request.content.title || '',\n body: n.request.content.body || '',\n data: n.request.content.data,\n });\n });\n return () => sub.remove();\n },\n };\n}\n\n// ── File system (expo-document-picker — partially real) ───────────\n//\n// pickDocument works. read/write do NOT: expo-file-system is not installed.\n// Named honestly rather than returning empty strings, which would read as an\n// empty file rather than a missing capability.\n\nfunction createFileSystemAPI(pluginId: string): FileSystemAPI {\n /**\n * Every path a plugin gives us is resolved INSIDE its own sandbox directory.\n *\n * A plugin is code a solution ships that runs on someone's phone, so\n * `readFile('/etc/passwd')` — or, more realistically, reading the app's own\n * auth store or another solution's data — must not be reachable. This is the\n * same reasoning that took the `sw.*` filesystem tools out of the kernel and\n * anchored them per tenant.\n *\n * The check is on the RESOLVED path, not the input string: `..` segments are\n * what a blocklist of prefixes misses, and `plugin-files/../../secrets` looks\n * innocent until it is normalised.\n */\n /**\n * Resolved on FIRST USE, never at construction.\n *\n * `new FS.Directory(FS.Paths.document, …)` is a SYNCHRONOUS NATIVE CALL, and\n * `createNativeCapabilities({ fileSystem: true, … })` runs at MODULE SCOPE in\n * src/adas.ts. Building it eagerly therefore did native work during startup\n * module evaluation — on every launch, including a headless background wake\n * from a push, before any UI exists. If the native module is missing or\n * `Paths.document` throws there, the whole app dies with no screen to show it\n * on. Nothing in this factory may touch native until a method is called.\n */\n let _root: InstanceType<typeof FS.Directory> | null = null;\n const rootDir = () => (_root ??= new FS.Directory(FS.Paths.document, 'plugin-files', pluginId));\n\n const resolve = (p: string): InstanceType<typeof FS.File> => {\n const root = rootDir();\n const file = new FS.File(root, p);\n if (!file.uri.startsWith(root.uri)) {\n throw new CapabilityUnavailableError(\n 'fileSystem',\n `Path escapes the plugin's storage area: ${p}. Plugin files live under plugin-files/ and paths cannot traverse out of it.`,\n );\n }\n return file;\n };\n\n return {\n async readFile(path) {\n const file = resolve(path);\n if (!file.exists) {\n // A missing file is not an empty file. Returning '' would read as a\n // document that exists and happens to have nothing in it.\n throw new CapabilityUnavailableError(\n 'fileSystem.readFile',\n `No such file: ${path}. Nothing was read.`,\n );\n }\n return file.text();\n },\n\n async writeFile(path, content) {\n const file = resolve(path);\n // Create parents as needed — a plugin writing 'cache/report.json' should\n // not have to know that directories are a separate concept.\n const parent = file.parentDirectory;\n if (!parent.exists) parent.create({ intermediates: true });\n if (!file.exists) file.create();\n file.write(content);\n },\n async pickDocument(opts) {\n const res = await DocumentPicker.getDocumentAsync({\n type: opts?.types?.length ? opts.types : '*/*',\n copyToCacheDirectory: true,\n });\n // Backing out is not a file. Returning zeros here would look like an\n // empty document the user actually chose.\n if (res.canceled || !res.assets?.[0]) {\n throw new Error('No document chosen — the picker was dismissed.');\n }\n const a = res.assets[0];\n return { uri: a.uri, name: a.name, size: a.size ?? 0 };\n },\n };\n}\n\n// ── Haptics (fully implemented — expo-haptics already installed) ───\n\nfunction createHapticsAPI(): HapticsAPI {\n return {\n impact(style: 'light' | 'medium' | 'heavy') {\n const map = {\n light: Haptics.ImpactFeedbackStyle.Light,\n medium: Haptics.ImpactFeedbackStyle.Medium,\n heavy: Haptics.ImpactFeedbackStyle.Heavy,\n };\n Haptics.impactAsync(map[style]);\n },\n notification(type: 'success' | 'warning' | 'error') {\n const map = {\n success: Haptics.NotificationFeedbackType.Success,\n warning: Haptics.NotificationFeedbackType.Warning,\n error: Haptics.NotificationFeedbackType.Error,\n };\n Haptics.notificationAsync(map[type]);\n },\n // Shorthand for notification('error'). Plugins call this; it was never\n // implemented, and optional chaining meant the failure the user was meant\n // to FEEL simply did not happen.\n error() {\n Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);\n },\n selection() {\n Haptics.selectionAsync();\n },\n };\n}\n\n// ── Connectivity (implemented using basic RN fetch check) ──────\n\nfunction createConnectivityAPI(reachabilityUrl?: string): ConnectivityAPI {\n let online = true;\n const listeners = new Set<(online: boolean) => void>();\n // Lazy + re-armable polling: the instance is now a singleton shared by ALL\n // plugins (injected once via setPluginNativeProvider), so the interval must\n // (a) not run at app boot when nobody is listening, and (b) restart when a\n // later plugin subscribes after an earlier one unsubscribed to zero.\n // Pre-singleton, each Plugin mount got its own instance and clearInterval\n // was harmless; with a shared instance it would kill polling app-wide.\n let interval: ReturnType<typeof setInterval> | null = null;\n\n // Simple connectivity check — upgrade to @react-native-community/netinfo in Phase 2.\n // NO hardcoded URL: the host injects its own health endpoint. With none given\n // we assume reachable (return true) rather than pin an A-Team-specific default.\n const checkOnline = async () => {\n if (!reachabilityUrl) return true;\n try {\n const res = await fetch(reachabilityUrl, {\n method: 'HEAD',\n signal: AbortSignal.timeout(5000),\n });\n return res.ok;\n } catch {\n return false;\n }\n };\n\n const startPolling = () => {\n // Nothing to poll without a reachability URL: `online` stays constant-true,\n // so an interval would only wake the app to learn the same answer.\n if (!reachabilityUrl) return;\n if (interval) return;\n interval = setInterval(async () => {\n const newState = await checkOnline();\n if (newState !== online) {\n online = newState;\n listeners.forEach(cb => cb(online));\n }\n }, 30000);\n };\n\n return {\n isOnline() {\n return online;\n },\n onChange(cb: (online: boolean) => void) {\n listeners.add(cb);\n startPolling();\n return () => {\n listeners.delete(cb);\n if (listeners.size === 0 && interval) {\n clearInterval(interval);\n interval = null;\n }\n };\n },\n };\n}\n\n// ── Cookies (delegates to device-bridge, lazy-loaded) ──\nfunction createCookiesAPI(): CookiesAPI {\n return {\n async get(url: string): Promise<WebViewCookie[]> {\n const mod = getDeviceCookies();\n if (!mod) throw new CapabilityUnavailableError('cookies (native module not available — use a dev build)');\n const list = await mod.get(url, true);\n return list as WebViewCookie[];\n },\n async clear(_url: string): Promise<{ ok: boolean }> {\n const mod = getDeviceCookies();\n if (!mod) throw new CapabilityUnavailableError('cookies (native module not available — use a dev build)');\n const r = await mod.clearAll();\n return { ok: r.ok };\n },\n };\n}\n\n// ── Biometrics v2 (Face ID / Touch ID / fingerprint) ──\nfunction createBiometricsAPIv2(): BiometricsAPIv2 {\n return {\n capabilities: () => deviceBiometrics.capabilities(),\n authenticate: (opts) => deviceBiometrics.authenticate(opts),\n };\n}\n\n// ── Sharing (native share sheet) ──\nfunction createSharingAPI(): SharingAPI {\n return {\n isAvailable: () => deviceSharing.isAvailable(),\n shareFile: (fileUri, opts) => deviceSharing.shareFile(fileUri, opts),\n };\n}\n\n// ── Scanner (QR / barcode permission helper) ──\nfunction createScannerAPI(): ScannerAPI {\n return {\n requestPermissions: () => deviceScanner.requestPermissions(),\n // Raises the shared camera surface and settles on the first code read.\n // The plugin mounts nothing — that lifecycle is owned once, by\n // CameraTaskHost, instead of by every plugin that wants to scan.\n scanOnce: (opts) => cameraTasks.run('scan', {\n formats: opts?.formats,\n timeoutMs: opts?.timeoutMs,\n }),\n };\n}\n\n// ── NFC (tag read, lazy-loaded) ──\nfunction createNfcAPI(): NfcAPI {\n return {\n async isSupported() {\n const mod = getDeviceNfc();\n if (!mod) return false;\n return mod.isSupported();\n },\n async isEnabled() {\n const mod = getDeviceNfc();\n if (!mod) return false;\n return mod.isEnabled();\n },\n readOnce(timeoutMs) {\n const mod = getDeviceNfc();\n if (!mod) return Promise.resolve({ ok: false, error: 'nfc_unavailable' } as any);\n return mod.readOnce(timeoutMs);\n },\n };\n}\n\n// ── Bluetooth / BLE (SHAPE ONLY — every method refuses) ──\n//\n// Nothing implements this yet, and it is wired up anyway. `bluetooth` has been\n// a manifest flag with no API behind it, so a plugin that correctly declared it\n// got `undefined` and crashed with \"cannot read properties of undefined\" — a\n// failure that names nothing and teaches nothing.\n//\n// Refusing BY NAME is the whole difference. The rule this layer runs on is that\n// absence is an answer, but only when it says what is absent.\n//\n// KEPT rather than dropped because BLE beacons are one of the few indoor\n// position signals that survive where GPS does not — which is precisely where\n// Ada Guide ends up the moment the user walks into a building.\nfunction createBluetoothAPI(): BluetoothAPI {\n const refuse = (method: string) =>\n new CapabilityUnavailableError(\n `bluetooth.${method}`,\n 'Bluetooth is not implemented in this build — no BLE native module is installed. ' +\n 'The capability is declared so it fails by name rather than silently; nothing was scanned.',\n );\n\n return {\n // These two answer `false` rather than throwing, because \"is BLE usable?\"\n // is exactly the question a plugin should be allowed to ask before\n // committing to a flow, and `false` is the true answer here.\n async isSupported() { return false; },\n async isEnabled() { return false; },\n // scan() THROWS, because there is no honest empty result: an empty device\n // list is a real finding (\"nothing is advertising nearby\") and returning\n // one would report a scan that never happened.\n async scan() { throw refuse('scan'); },\n };\n}\n\n\n// ── Vision — on-device image understanding (SHAPE ONLY) ──\n//\n// Nothing implements this yet; every method refuses BY NAME. Declared now\n// because it is the last missing piece of Ada Guide and a solution should be\n// able to design against it before the native side exists.\n//\n// WHY IT IS NOT IN THE VISUAL DOMAIN: the freeze reserves Visual for the frame\n// TIMELINE and puts everything interpretive — what is in the scene, what the\n// sign says — ABOVE Device Bridge. This is that layer.\n//\n// FRAMES MUST NEVER LEAVE THE PHONE (Arie, 2026-08-15). Whoever implements\n// this: iOS Vision (VNRecognizeTextRequest / VNClassifyImageRequest) or\n// Android MLKit, both on-device and free per call. An implementation that\n// POSTs the frame anywhere satisfies these types and violates the design.\nfunction createVisionAPI(): VisionAPI {\n // All three methods are implemented on-device via the ada-vision native module:\n // assessQuality (pixel statistics), readText (VNRecognizeText), describe\n // (VNClassifyImage). Frames never leave the phone.\n return {\n // \"Can I see?\" — true once the native module is present in the binary. On a\n // build without it, adaVision.isSupported() answers false, and assessQuality\n // below refuses cleanly (no crash), exactly the pre-module behaviour.\n async isSupported() { return adaVision.isSupported(); },\n // readText: on-device OCR on a published frame (native resolves the id →\n // pixels). [] = looked, found no text (a real finding); throws = could not\n // look (unknown/aged-out id). describe (VNClassifyImage) is the next pass.\n async readText(frameId: string, opts?: { languages?: string[] }) {\n const langs = opts?.languages;\n if (langs != null && (!Array.isArray(langs) || langs.some((l) => typeof l !== 'string'))) {\n throw refuseArgument('vision', 'readText', `languages must be an array of language codes, not ${shown(langs)}`, 'nothing was read');\n }\n return adaVision.readText(frameId, langs);\n },\n // describe: on-device labels (VNClassifyImage), not prose — the sentence is\n // the skill's job. [] = looked, recognised nothing.\n async describe(frameId: string, opts?: { maxLabels?: number }) {\n // Checked here: a NaN/Infinity count would trap Swift's Int conversion (src/args.ts).\n return adaVision.describe(frameId, countArg('vision', 'describe', 'maxLabels', opts?.maxLabels, 5, 50, 'nothing was labelled'));\n },\n // detectObjects: on-device located objects (bundled detector), each with a\n // box — what a guidance loop reads to say WHERE a thing is, not just that the\n // scene contains it. [] = looked, found nothing; throws = could not look.\n async detectObjects(frameId: string, opts?: { maxItems?: number; minConfidence?: number }) {\n const nothing = 'nothing was detected';\n return adaVision.detectObjects(\n frameId,\n countArg('vision', 'detectObjects', 'maxItems', opts?.maxItems, 10, 50, nothing),\n fractionArg('vision', 'detectObjects', 'minConfidence', opts?.minConfidence, 0.25, nothing),\n );\n },\n // assessQuality: resolve the frame id to its LOCAL file, measure on-device,\n // and record the result back onto the frame so a later read of\n // VisualFrameRef.quality reflects it. Throwing here means \"could not look\"\n // (unknown/aged-out id, or no native module) — never a measured verdict.\n async assessQuality(frameId: string) {\n // Quality was measured at publish; the pixels live in the native ring,\n // resolved by id inside native. Null = the frame is unknown or has aged out\n // of the ring — \"could not look\", never \"looked and found nothing\".\n const q = adaVision.qualityById(frameId);\n if (!q) {\n throw new CapabilityUnavailableError(\n 'vision.assessQuality',\n `No frame for id \"${frameId}\" — unknown or aged out of the ring. ` +\n 'This is \"could not look\", not a measurement.',\n );\n }\n return q;\n },\n };\n}\n\n// ── Maps — place search, reverse geocoding, walking routes ──\n//\n// NOT on-device, unlike vision: the query and the coordinates it is given go to\n// Apple's map servers (the platform geocoder on Android). It never reads the\n// user's location — the caller passes every coordinate — so it is not a second\n// door to where the user is.\n//\n// search/route are the ada-maps module (MapKit, iOS only). reverseGeocode is the\n// SDK's one reverse geocode (expo-location), the same call the location\n// collector and the connector bridge make — so it works on Android and on a\n// build without ada-maps.\nfunction createMapsAPI(): MapsAPI {\n // Refuse BY NAME before any native call: what this build or platform is\n // missing, and that nothing left the phone.\n const refuseIfMissing = (verb: string, nothing: string) => {\n const missing = adaMaps.missingReason();\n if (missing) {\n throw new CapabilityUnavailableError(`maps.${verb}`, `maps.${verb} needs ${missing}; ${nothing}.`);\n }\n };\n\n // Every argument is checked (searchArgs / coordinateArg) BEFORE the missing\n // check and before anything crosses the bridge: a remote bundle is plain JS,\n // and a malformed call refuses as INVALID_ARGUMENT instead of crashing Swift\n // (NaN limit) or searching around 0,0 (`{ lat, lng }`).\n return {\n async isSupported() { return adaMaps.isSupported(); },\n // [] = Apple looked and found nothing (a real finding); throws = could not\n // look. The defaults (radiusM 3000, limit 8) live in searchArgs.\n async search(query, opts) {\n const args = searchArgs(query, opts);\n refuseIfMissing('search', 'nothing was searched');\n const raw = await adaMaps\n .search(args.query, args.near, args.radiusM, args.limit)\n .catch(err => { throw mapsFailure('search', err); });\n return toMapsPlaces(raw, args.near);\n },\n // null = nothing at that point; throws = could not look.\n async reverseGeocode(at) {\n const point = coordinateArg('reverseGeocode', 'at', at, 'nothing was looked up');\n const address = await adaMaps\n .reverseGeocode(point)\n .catch(err => { throw mapsFailure('reverseGeocode', err); });\n return address ? toReverseGeocodedPlace(point, address) : null;\n },\n // null = Apple found no route. Never an empty 0 m route, which would read as\n // \"you have arrived\". A remote bundle can pass any string as `mode`, so a\n // mode this build cannot honour refuses rather than silently walking.\n async route(from, to, opts) {\n if (opts?.mode !== undefined && opts.mode !== 'walking') {\n throw new CapabilityUnavailableError(\n 'maps.route',\n `maps.route supports walking only, not \"${String(opts.mode)}\"; no route was requested.`,\n );\n }\n const a = coordinateArg('route', 'from', from, 'no route was requested');\n const b = coordinateArg('route', 'to', to, 'no route was requested');\n refuseIfMissing('route', 'no route was requested');\n const raw = await adaMaps\n .route(a, b)\n .catch(err => { throw mapsFailure('route', err); });\n return raw ? toMapsRoute(raw) : null;\n },\n };\n}\n\n// ── Provider factory ───────────────────────────────────────────\n\nexport interface DeviceProviderConfig {\n /** Endpoint the connectivity check HEAD-fetches (host-injected, e.g. the app's\n * own health URL). Omitted → connectivity assumes reachable (no hardcoded URL). */\n reachabilityUrl?: string;\n}\n\n/**\n * The full device-primitive implementation a host registers once via\n * setPluginNativeProvider. It is UNGATED — per-plugin capability gating and\n * per-plugin filesystem scoping are applied by plugin-runtime's gatePluginNative.\n */\nexport function createDeviceProvider(config?: DeviceProviderConfig): PluginNative {\n const connectivity = createConnectivityAPI(config?.reachabilityUrl);\n return {\n camera: createCameraAPI(),\n location: createLocationAPI(),\n // Only the per-plugin storageFor/fileSystemFor are exposed — NO shared\n // `storage`/`fileSystem` bucket. The gate scopes by pluginId and fails\n // closed without one, so a shared instance would be an unreachable\n // (and dangerous) fallback. Each plugin gets its own namespace + db file /\n // sandbox directory, or nothing.\n storageFor: (pluginId: string) => createStorageAPI(pluginId),\n biometrics: stubApi<BiometricsAPI>('biometrics'),\n biometricsV2: createBiometricsAPIv2(),\n notifications: createNotificationsAPI(),\n haptics: createHapticsAPI(),\n connectivity,\n fileSystemFor: (pluginId: string) => createFileSystemAPI(pluginId),\n cookies: createCookiesAPI(),\n sharing: createSharingAPI(),\n scanner: createScannerAPI(),\n nfc: createNfcAPI(),\n bluetooth: createBluetoothAPI(),\n vision: createVisionAPI(),\n maps: createMapsAPI(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,YAAY,aAAa;AAIzB,YAAY,kBAAkB;AAC9B,YAAY,iBAAiB;AAC7B,YAAY,mBAAmB;AAC/B,YAAY,oBAAoB;AAChC,YAAY,QAAQ;AACpB,YAAY,YAAY;AAIxB,OAAO,kBAAkB;AA2BzB,SAAS,mBAAmB;AAC1B,MAAI;AAAE,WAAO;AAAA,EAAqC,QAAQ;AAAE,WAAO;AAAA,EAAM;AAC3E;AACA,SAAS,eAAe;AACtB,MAAI;AAAE,WAAO;AAAA,EAAiC,QAAQ;AAAE,WAAO;AAAA,EAAM;AACvE;AAuBA,IAAM,6BAAN,cAAyC,MAAM;AAAA,EAE7C,YAAY,YAAoB,QAAiB;AAC/C,UAAM,UAAU,GAAG,UAAU,6CAA6C;AAF5E,gBAAO;AAGL,SAAK,OAAO;AAAA,EACd;AACF;AAUA,SAAS,QAA0B,YAAuB;AACxD,SAAO,IAAI,MAAM,CAAC,GAAQ;AAAA,IACxB,IAAI,SAAS,MAAM;AACjB,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,MAAM;AACX,gBAAM,IAAI,2BAA2B,UAAU;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AASA,SAAS,oBAAiC;AAExC,QAAM,mBAAmB,YAA2B;AAClD,UAAM,WAAW,MAAmB,2CAA8B;AAClE,QAAI,SAAS,WAAW,UAAW;AACnC,UAAM,QAAQ,MAAmB,+CAAkC;AACnE,QAAI,MAAM,WAAW,WAAW;AAG9B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,WAAW,MAAM;AACrB,YAAM,iBAAiB;AAGvB,YAAM,MAAM,MAAmB,qCAAwB;AAAA,QACrD,UACE,MAAM,aAAa,SACF,sBAAS,OACT,sBAAS;AAAA,MAC9B,CAAC;AACD,aAAO;AAAA,QACL,UAAU,IAAI,OAAO;AAAA,QACrB,WAAW,IAAI,OAAO;AAAA,QACtB,UAAU,IAAI,OAAO,YAAY;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,cAAc,IAAI;AAChB,UAAI,eAA0C;AAC9C,UAAI,YAAY;AAEhB,YAAM,YAAY;AAChB,YAAI;AACF,gBAAM,iBAAiB;AACvB,cAAI,UAAW;AACf,yBAAe,MAAmB;AAAA,YAChC,EAAE,UAAuB,sBAAS,SAAS;AAAA,YAC3C,SAAO,GAAG,EAAE,UAAU,IAAI,OAAO,UAAU,WAAW,IAAI,OAAO,UAAU,CAAC;AAAA,UAC9E;AAGA,cAAI,WAAW;AAAE,0BAAc,OAAO;AAAG,2BAAe;AAAA,UAAM;AAAA,QAChE,SAAS,KAAU;AACjB,kBAAQ,KAAK,oCAAoC,KAAK,WAAW,OAAO,GAAG,CAAC;AAAA,QAC9E;AAAA,MACF,GAAG;AAEH,aAAO,MAAM;AACX,oBAAY;AACZ,sBAAc,OAAO;AACrB,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACF;AAUA,SAAS,kBAA6B;AACpC,QAAM,mBAAmB,YAA2B;AAClD,UAAM,WAAW,MAAkB,sCAA0B;AAC7D,QAAI,SAAS,QAAS;AACtB,UAAM,QAAQ,MAAkB,0CAA8B;AAC9D,QAAI,CAAC,MAAM,SAAS;AAClB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,UAAU,MAAM;AACpB,YAAM,iBAAiB;AACvB,YAAM,MAAM,MAAkB,8BAAkB;AAAA,QAC9C,SAAS,MAAM,WAAW;AAAA,QAC1B,YACE,MAAM,WAAW,UACD,uBAAW,QACX,uBAAW;AAAA,MAC/B,CAAC;AAGD,UAAI,IAAI,YAAY,CAAC,IAAI,SAAS,CAAC,GAAG;AACpC,cAAM,IAAI,MAAM,iDAA4C;AAAA,MAC9D;AACA,YAAM,IAAI,IAAI,OAAO,CAAC;AACtB,aAAO,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO;AAAA,IACxD;AAAA,IAEA,MAAM,YAAY,MAAM;AAItB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,oKAEG,MAAM,SAAS,SAAS,4CAA4C;AAAA,MACzE;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,MAAM;AAItB,aAAO,YAAY,IAAI,UAAU;AAAA,QAC/B,eAAe,MAAM;AAAA,QACrB,WAAW,MAAM,aAAa;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,UAAU,MAAM;AACpB,YAAM,WAAW,MAAkB,4CAAgC;AACnE,UAAI,CAAC,SAAS,SAAS;AACrB,cAAM,QAAQ,MAAkB,gDAAoC;AACpE,YAAI,CAAC,MAAM,SAAS;AAClB,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,YAAM,MAAM,MAAkB,oCAAwB;AAAA,QACpD,yBAAyB,CAAC,CAAC,MAAM;AAAA,MACnC,CAAC;AACD,UAAI,IAAI,SAAU,QAAO,CAAC;AAC1B,cAAQ,IAAI,UAAU,CAAC,GAAG,IAAI,QAAM,EAAE,KAAK,EAAE,IAAI,EAAE;AAAA,IACrD;AAAA,EACF;AACF;AAsBA,IAAM,aAAa,oBAAI,IAA0B;AACjD,SAAS,aAAa,MAA4B;AAChD,QAAM,WAAW,WAAW,IAAI,IAAI;AACpC,MAAI,SAAU,QAAO;AACrB,QAAM,SAA8B,yBAAkB,IAAI;AAC1D,aAAW,IAAI,MAAM,MAAM;AAC3B,SAAO;AACT;AAKA,SAAS,kBAAkB,KAAqB;AAC9C,MAAI,IAAI,OAAO,GAAG;AAClB,aAAS;AACP,UAAM,SAAS;AACf,QAAI,EAAE,QAAQ,QAAQ,EAAE;AACxB,QAAI,EAAE,WAAW,IAAI,GAAG;AAAE,YAAM,KAAK,EAAE,QAAQ,IAAI;AAAG,UAAI,OAAO,KAAK,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,IAAG,WACnF,EAAE,WAAW,IAAI,GAAG;AAAE,YAAM,MAAM,EAAE,QAAQ,IAAI;AAAG,UAAI,QAAQ,KAAK,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,IAAG;AACpG,QAAI,MAAM,OAAQ;AAAA,EACpB;AACA,QAAM,IAAI,EAAE,MAAM,YAAY;AAC9B,SAAO,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI;AAClC;AAgBA,SAAS,iBAAiB,UAA8B;AACtD,QAAM,KAAK,mBAAmB,QAAQ;AACtC,QAAM,MAAM,CAAC,MAAc,WAAW,EAAE,IAAI,CAAC;AAC7C,QAAM,SAAS,kBAAkB,EAAE;AAEnC,SAAO;AAAA,IACL,KAAK,CAAC,MAAM,aAAa,QAAQ,IAAI,CAAC,CAAC;AAAA,IACvC,KAAK,CAAC,GAAG,MAAM,aAAa,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAC7C,QAAQ,CAAC,MAAM,aAAa,WAAW,IAAI,CAAC,CAAC;AAAA,IAC7C,IAAI;AAAA,MACF,MAAM,QAAQ,KAAa,QAAgB;AACzC,cAAM,OAAO,kBAAkB,GAAG;AAgBlC,YAAI,SAAS,YAAY,SAAS,YAAY,SAAS,UAAU;AAC/D,gBAAM,IAAI;AAAA,YACR;AAAA,YACA,QAAQ,IAAI;AAAA,UACd;AAAA,QACF;AACA,cAAM,KAAK,MAAM,aAAa,MAAM;AAKpC,cAAM,cAAc,SAAS,YAAY,SAAS,YAAY,SAAS;AAEvE,YAAI,aAAa;AACf,gBAAM,OAAO,MAAM,GAAG,YAAY,KAAM,UAAU,CAAC,CAAS;AAE5D,iBAAO,EAAE,MAAM,SAAS,EAAE;AAAA,QAC5B;AACA,cAAM,MAAM,MAAM,GAAG,SAAS,KAAM,UAAU,CAAC,CAAS;AAExD,eAAO,EAAE,MAAM,CAAC,GAAG,SAAS,IAAI,WAAW,EAAE;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;AASA,eAAsB,qBAAoC;AAExD,MAAI;AACF,UAAM,OAAO,MAAM,aAAa,WAAW;AAC3C,UAAM,OAAO,KAAK,OAAO,CAAC,MAAc,EAAE,WAAW,UAAU,CAAC;AAChE,QAAI,KAAK,OAAQ,OAAM,aAAa,YAAY,IAAI;AAAA,EACtD,SAAS,GAAQ;AACf,YAAQ,KAAK,+CAA+C,GAAG,WAAW,CAAC;AAAA,EAC7E;AAGA,MAAI;AACF,eAAW,CAAC,EAAE,CAAC,KAAK,YAAY;AAC9B,UAAI;AAAE,cAAM,KAAK,MAAM;AAAG,cAAM,GAAG,aAAa;AAAA,MAAG,QAAQ;AAAA,MAAuB;AAAA,IACpF;AACA,eAAW,MAAM;AACjB,UAAM,YAAY,IAAO,aAAa,SAAM,UAAU,QAAQ;AAC9D,QAAI,UAAU,QAAQ;AACpB,iBAAW,SAAS,UAAU,KAAK,GAAG;AACpC,YAAI,0BAA0B,KAAK,OAAO,MAAM,QAAQ,EAAE,CAAC,GAAG;AAC5D,cAAI;AAAE,kBAAM,OAAO;AAAA,UAAG,QAAQ;AAAA,UAAoB;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,GAAQ;AACf,YAAQ,KAAK,mDAAmD,GAAG,WAAW,CAAC;AAAA,EACjF;AAGA,MAAI;AACF,UAAM,MAAM,IAAO,aAAa,SAAM,UAAU,cAAc;AAC9D,QAAI,IAAI,OAAQ,KAAI,OAAO;AAAA,EAC7B,SAAS,GAAQ;AACf,YAAQ,KAAK,yDAAyD,GAAG,WAAW,CAAC;AAAA,EACvF;AACF;AAIA,SAAS,yBAA2C;AAClD,SAAO;AAAA,IACL,MAAM,SAAS,EAAE,OAAO,MAAM,QAAQ,GAAG;AACvC,YAAM,OAAO,MAAoB,kCAAoB;AACrD,UAAI,CAAC,KAAK,SAAS;AACjB,cAAM,QAAQ,MAAoB,sCAAwB;AAC1D,YAAI,CAAC,MAAM,SAAS;AAClB,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,OACJ,OAAO,SAAS,YAAY,WACxB,EAAE,MAAM,gBAAgB,SAAS,QAAQ,SAAS,SAAS,MAAM,IACjE,SAAS,OACP,EAAE,MAAM,QAAQ,MAAM,IAAI,KAAK,QAAQ,IAAI,EAAE,IAC7C;AACR,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,mDAAmD;AAAA,MACrE;AACA,aAAqB,wCAA0B;AAAA,QAC7C,SAAS,EAAE,OAAO,KAAK;AAAA,QACvB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,IAEA,QAAQ,CAAC,OAAqB,+CAAiC,EAAE;AAAA,IAEjE,WAAW,IAAI;AACb,YAAM,MAAoB,8CAAgC,OAAK;AAC7D,WAAG;AAAA,UACD,IAAI,EAAE,QAAQ;AAAA,UACd,OAAO,EAAE,QAAQ,QAAQ,SAAS;AAAA,UAClC,MAAM,EAAE,QAAQ,QAAQ,QAAQ;AAAA,UAChC,MAAM,EAAE,QAAQ,QAAQ;AAAA,QAC1B,CAAC;AAAA,MACH,CAAC;AACD,aAAO,MAAM,IAAI,OAAO;AAAA,IAC1B;AAAA,EACF;AACF;AAQA,SAAS,oBAAoB,UAAiC;AAyB5D,MAAI,QAAkD;AACtD,QAAM,UAAU,MAAO,kBAAU,IAAO,aAAa,SAAM,UAAU,gBAAgB,QAAQ;AAE7F,QAAM,UAAU,CAAC,MAA4C;AAC3D,UAAM,OAAO,QAAQ;AACrB,UAAM,OAAO,IAAO,QAAK,MAAM,CAAC;AAChC,QAAI,CAAC,KAAK,IAAI,WAAW,KAAK,GAAG,GAAG;AAClC,YAAM,IAAI;AAAA,QACR;AAAA,QACA,2CAA2C,CAAC;AAAA,MAC9C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,SAAS,MAAM;AACnB,YAAM,OAAO,QAAQ,IAAI;AACzB,UAAI,CAAC,KAAK,QAAQ;AAGhB,cAAM,IAAI;AAAA,UACR;AAAA,UACA,iBAAiB,IAAI;AAAA,QACvB;AAAA,MACF;AACA,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,IAEA,MAAM,UAAU,MAAM,SAAS;AAC7B,YAAM,OAAO,QAAQ,IAAI;AAGzB,YAAM,SAAS,KAAK;AACpB,UAAI,CAAC,OAAO,OAAQ,QAAO,OAAO,EAAE,eAAe,KAAK,CAAC;AACzD,UAAI,CAAC,KAAK,OAAQ,MAAK,OAAO;AAC9B,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,IACA,MAAM,aAAa,MAAM;AACvB,YAAM,MAAM,MAAqB,gCAAiB;AAAA,QAChD,MAAM,MAAM,OAAO,SAAS,KAAK,QAAQ;AAAA,QACzC,sBAAsB;AAAA,MACxB,CAAC;AAGD,UAAI,IAAI,YAAY,CAAC,IAAI,SAAS,CAAC,GAAG;AACpC,cAAM,IAAI,MAAM,qDAAgD;AAAA,MAClE;AACA,YAAM,IAAI,IAAI,OAAO,CAAC;AACtB,aAAO,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE,QAAQ,EAAE;AAAA,IACvD;AAAA,EACF;AACF;AAIA,SAAS,mBAA+B;AACtC,SAAO;AAAA,IACL,OAAO,OAAqC;AAC1C,YAAM,MAAM;AAAA,QACV,OAAe,4BAAoB;AAAA,QACnC,QAAgB,4BAAoB;AAAA,QACpC,OAAe,4BAAoB;AAAA,MACrC;AACA,MAAQ,oBAAY,IAAI,KAAK,CAAC;AAAA,IAChC;AAAA,IACA,aAAa,MAAuC;AAClD,YAAM,MAAM;AAAA,QACV,SAAiB,iCAAyB;AAAA,QAC1C,SAAiB,iCAAyB;AAAA,QAC1C,OAAe,iCAAyB;AAAA,MAC1C;AACA,MAAQ,0BAAkB,IAAI,IAAI,CAAC;AAAA,IACrC;AAAA;AAAA;AAAA;AAAA,IAIA,QAAQ;AACN,MAAQ,0BAA0B,iCAAyB,KAAK;AAAA,IAClE;AAAA,IACA,YAAY;AACV,MAAQ,uBAAe;AAAA,IACzB;AAAA,EACF;AACF;AAIA,SAAS,sBAAsB,iBAA2C;AACxE,MAAI,SAAS;AACb,QAAM,YAAY,oBAAI,IAA+B;AAOrD,MAAI,WAAkD;AAKtD,QAAM,cAAc,YAAY;AAC9B,QAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,iBAAiB;AAAA,QACvC,QAAQ;AAAA,QACR,QAAQ,YAAY,QAAQ,GAAI;AAAA,MAClC,CAAC;AACD,aAAO,IAAI;AAAA,IACb,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AAGzB,QAAI,CAAC,gBAAiB;AACtB,QAAI,SAAU;AACd,eAAW,YAAY,YAAY;AACjC,YAAM,WAAW,MAAM,YAAY;AACnC,UAAI,aAAa,QAAQ;AACvB,iBAAS;AACT,kBAAU,QAAQ,QAAM,GAAG,MAAM,CAAC;AAAA,MACpC;AAAA,IACF,GAAG,GAAK;AAAA,EACV;AAEA,SAAO;AAAA,IACL,WAAW;AACT,aAAO;AAAA,IACT;AAAA,IACA,SAAS,IAA+B;AACtC,gBAAU,IAAI,EAAE;AAChB,mBAAa;AACb,aAAO,MAAM;AACX,kBAAU,OAAO,EAAE;AACnB,YAAI,UAAU,SAAS,KAAK,UAAU;AACpC,wBAAc,QAAQ;AACtB,qBAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,SAAS,mBAA+B;AACtC,SAAO;AAAA,IACL,MAAM,IAAI,KAAuC;AAC/C,YAAM,MAAM,iBAAiB;AAC7B,UAAI,CAAC,IAAK,OAAM,IAAI,2BAA2B,8DAAyD;AACxG,YAAM,OAAO,MAAM,IAAI,IAAI,KAAK,IAAI;AACpC,aAAO;AAAA,IACT;AAAA,IACA,MAAM,MAAM,MAAwC;AAClD,YAAM,MAAM,iBAAiB;AAC7B,UAAI,CAAC,IAAK,OAAM,IAAI,2BAA2B,8DAAyD;AACxG,YAAM,IAAI,MAAM,IAAI,SAAS;AAC7B,aAAO,EAAE,IAAI,EAAE,GAAG;AAAA,IACpB;AAAA,EACF;AACF;AAGA,SAAS,wBAAyC;AAChD,SAAO;AAAA,IACL,cAAc,MAAM,mBAAiB,aAAa;AAAA,IAClD,cAAc,CAAC,SAAS,mBAAiB,aAAa,IAAI;AAAA,EAC5D;AACF;AAGA,SAAS,mBAA+B;AACtC,SAAO;AAAA,IACL,aAAa,MAAM,gBAAc,YAAY;AAAA,IAC7C,WAAW,CAAC,SAAS,SAAS,gBAAc,UAAU,SAAS,IAAI;AAAA,EACrE;AACF;AAGA,SAAS,mBAA+B;AACtC,SAAO;AAAA,IACL,oBAAoB,MAAM,gBAAc,mBAAmB;AAAA;AAAA;AAAA;AAAA,IAI3D,UAAU,CAAC,SAAS,YAAY,IAAI,QAAQ;AAAA,MAC1C,SAAS,MAAM;AAAA,MACf,WAAW,MAAM;AAAA,IACnB,CAAC;AAAA,EACH;AACF;AAGA,SAAS,eAAuB;AAC9B,SAAO;AAAA,IACL,MAAM,cAAc;AAClB,YAAM,MAAM,aAAa;AACzB,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,IAAI,YAAY;AAAA,IACzB;AAAA,IACA,MAAM,YAAY;AAChB,YAAM,MAAM,aAAa;AACzB,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,IAAI,UAAU;AAAA,IACvB;AAAA,IACA,SAAS,WAAW;AAClB,YAAM,MAAM,aAAa;AACzB,UAAI,CAAC,IAAK,QAAO,QAAQ,QAAQ,EAAE,IAAI,OAAO,OAAO,kBAAkB,CAAQ;AAC/E,aAAO,IAAI,SAAS,SAAS;AAAA,IAC/B;AAAA,EACF;AACF;AAeA,SAAS,qBAAmC;AAC1C,QAAM,SAAS,CAAC,WACd,IAAI;AAAA,IACF,aAAa,MAAM;AAAA,IACnB;AAAA,EAEF;AAEF,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,MAAM,cAAc;AAAE,aAAO;AAAA,IAAO;AAAA,IACpC,MAAM,YAAY;AAAE,aAAO;AAAA,IAAO;AAAA;AAAA;AAAA;AAAA,IAIlC,MAAM,OAAO;AAAE,YAAM,OAAO,MAAM;AAAA,IAAG;AAAA,EACvC;AACF;AAiBA,SAAS,kBAA6B;AAIpC,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,MAAM,cAAc;AAAE,aAAiB,YAAY;AAAA,IAAG;AAAA;AAAA;AAAA;AAAA,IAItD,MAAM,SAAS,SAAiB,MAAiC;AAC/D,YAAM,QAAQ,MAAM;AACpB,UAAI,SAAS,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,KAAK,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAI;AACxF,cAAM,eAAe,UAAU,YAAY,qDAAqD,MAAM,KAAK,CAAC,IAAI,kBAAkB;AAAA,MACpI;AACA,aAAiB,SAAS,SAAS,KAAK;AAAA,IAC1C;AAAA;AAAA;AAAA,IAGA,MAAM,SAAS,SAAiB,MAA+B;AAE7D,aAAiB,SAAS,SAAS,SAAS,UAAU,YAAY,aAAa,MAAM,WAAW,GAAG,IAAI,sBAAsB,CAAC;AAAA,IAChI;AAAA;AAAA;AAAA;AAAA,IAIA,MAAM,cAAc,SAAiB,MAAsD;AACzF,YAAM,UAAU;AAChB,aAAiB;AAAA,QACf;AAAA,QACA,SAAS,UAAU,iBAAiB,YAAY,MAAM,UAAU,IAAI,IAAI,OAAO;AAAA,QAC/E,YAAY,UAAU,iBAAiB,iBAAiB,MAAM,eAAe,MAAM,OAAO;AAAA,MAC5F;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,cAAc,SAAiB;AAInC,YAAM,IAAc,YAAY,OAAO;AACvC,UAAI,CAAC,GAAG;AACN,cAAM,IAAI;AAAA,UACR;AAAA,UACA,oBAAoB,OAAO;AAAA,QAE7B;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAaA,SAAS,gBAAyB;AAGhC,QAAM,kBAAkB,CAAC,MAAc,YAAoB;AACzD,UAAM,UAAkB,cAAc;AACtC,QAAI,SAAS;AACX,YAAM,IAAI,2BAA2B,QAAQ,IAAI,IAAI,QAAQ,IAAI,UAAU,OAAO,KAAK,OAAO,GAAG;AAAA,IACnG;AAAA,EACF;AAMA,SAAO;AAAA,IACL,MAAM,cAAc;AAAE,aAAeA,aAAY;AAAA,IAAG;AAAA;AAAA;AAAA,IAGpD,MAAM,OAAO,OAAO,MAAM;AACxB,YAAM,OAAO,WAAW,OAAO,IAAI;AACnC,sBAAgB,UAAU,sBAAsB;AAChD,YAAM,MAAM,MACT,OAAO,KAAK,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,KAAK,EACtD,MAAM,SAAO;AAAE,cAAM,YAAY,UAAU,GAAG;AAAA,MAAG,CAAC;AACrD,aAAO,aAAa,KAAK,KAAK,IAAI;AAAA,IACpC;AAAA;AAAA,IAEA,MAAM,eAAe,IAAI;AACvB,YAAM,QAAQ,cAAc,kBAAkB,MAAM,IAAI,uBAAuB;AAC/E,YAAM,UAAU,MACb,eAAe,KAAK,EACpB,MAAM,SAAO;AAAE,cAAM,YAAY,kBAAkB,GAAG;AAAA,MAAG,CAAC;AAC7D,aAAO,UAAU,uBAAuB,OAAO,OAAO,IAAI;AAAA,IAC5D;AAAA;AAAA;AAAA;AAAA,IAIA,MAAM,MAAM,MAAM,IAAI,MAAM;AAC1B,UAAI,MAAM,SAAS,UAAa,KAAK,SAAS,WAAW;AACvD,cAAM,IAAI;AAAA,UACR;AAAA,UACA,0CAA0C,OAAO,KAAK,IAAI,CAAC;AAAA,QAC7D;AAAA,MACF;AACA,YAAM,IAAI,cAAc,SAAS,QAAQ,MAAM,wBAAwB;AACvE,YAAM,IAAI,cAAc,SAAS,MAAM,IAAI,wBAAwB;AACnE,sBAAgB,SAAS,wBAAwB;AACjD,YAAM,MAAM,MACT,MAAM,GAAG,CAAC,EACV,MAAM,SAAO;AAAE,cAAM,YAAY,SAAS,GAAG;AAAA,MAAG,CAAC;AACpD,aAAO,MAAM,YAAY,GAAG,IAAI;AAAA,IAClC;AAAA,EACF;AACF;AAeO,SAAS,qBAAqB,QAA6C;AAChF,QAAM,eAAe,sBAAsB,QAAQ,eAAe;AAClE,SAAO;AAAA,IACL,QAAQ,gBAAgB;AAAA,IACxB,UAAU,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAM5B,YAAY,CAAC,aAAqB,iBAAiB,QAAQ;AAAA,IAC3D,YAAY,QAAuB,YAAY;AAAA,IAC/C,cAAc,sBAAsB;AAAA,IACpC,eAAe,uBAAuB;AAAA,IACtC,SAAS,iBAAiB;AAAA,IAC1B;AAAA,IACA,eAAe,CAAC,aAAqB,oBAAoB,QAAQ;AAAA,IACjE,SAAS,iBAAiB;AAAA,IAC1B,SAAS,iBAAiB;AAAA,IAC1B,SAAS,iBAAiB;AAAA,IAC1B,KAAK,aAAa;AAAA,IAClB,WAAW,mBAAmB;AAAA,IAC9B,QAAQ,gBAAgB;AAAA,IACxB,MAAM,cAAc;AAAA,EACtB;AACF;","names":["isSupported"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/geo.ts
|
|
2
|
+
function distanceOf(path) {
|
|
3
|
+
const R = 6371e3;
|
|
4
|
+
let total = 0;
|
|
5
|
+
for (let i = 1; i < path.length; i++) {
|
|
6
|
+
const a = path[i - 1], b = path[i];
|
|
7
|
+
const dLat = (b.latitude - a.latitude) * Math.PI / 180;
|
|
8
|
+
const dLon = (b.longitude - a.longitude) * Math.PI / 180;
|
|
9
|
+
const la1 = a.latitude * Math.PI / 180, la2 = b.latitude * Math.PI / 180;
|
|
10
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(la1) * Math.cos(la2) * Math.sin(dLon / 2) ** 2;
|
|
11
|
+
total += 2 * R * Math.asin(Math.sqrt(h));
|
|
12
|
+
}
|
|
13
|
+
return Math.round(total);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
distanceOf
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=chunk-J6XRNX7P.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/geo.ts"],"sourcesContent":["// Great-circle distance — the SDK's one answer to \"how far apart are these\".\n//\n// Moved out of device-state/domains/spatial.ts when native.maps needed the same\n// question answered (a search result's distance from `near`). Two haversines\n// would be two answers that could drift; both callers import this one.\n//\n// No imports on purpose: it runs under plain `node --test` with the maps\n// normaliser that uses it.\n\n/** Metres along a path, great-circle per leg, rounded to the metre. */\nexport function distanceOf(path: ReadonlyArray<{ latitude: number; longitude: number }>): number {\n const R = 6_371_000;\n let total = 0;\n for (let i = 1; i < path.length; i++) {\n const a = path[i - 1], b = path[i];\n const dLat = ((b.latitude - a.latitude) * Math.PI) / 180;\n const dLon = ((b.longitude - a.longitude) * Math.PI) / 180;\n const la1 = (a.latitude * Math.PI) / 180, la2 = (b.latitude * Math.PI) / 180;\n const h = Math.sin(dLat / 2) ** 2 + Math.cos(la1) * Math.cos(la2) * Math.sin(dLon / 2) ** 2;\n total += 2 * R * Math.asin(Math.sqrt(h));\n }\n return Math.round(total);\n}\n"],"mappings":";AAUO,SAAS,WAAW,MAAsE;AAC/F,QAAM,IAAI;AACV,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC;AACjC,UAAM,QAAS,EAAE,WAAW,EAAE,YAAY,KAAK,KAAM;AACrD,UAAM,QAAS,EAAE,YAAY,EAAE,aAAa,KAAK,KAAM;AACvD,UAAM,MAAO,EAAE,WAAW,KAAK,KAAM,KAAK,MAAO,EAAE,WAAW,KAAK,KAAM;AACzE,UAAM,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK;AAC1F,aAAS,IAAI,IAAI,KAAK,KAAK,KAAK,KAAK,CAAC,CAAC;AAAA,EACzC;AACA,SAAO,KAAK,MAAM,KAAK;AACzB;","names":[]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/device-bridge/collectors/appEngagement.ts
|
|
2
|
+
import { AppState } from "react-native";
|
|
3
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
4
|
+
var KEY = "ateam.appEngagement.v1";
|
|
5
|
+
var _tally = null;
|
|
6
|
+
var _foregroundSince = null;
|
|
7
|
+
var _started = false;
|
|
8
|
+
var _sub = null;
|
|
9
|
+
function todayKey() {
|
|
10
|
+
const d = /* @__PURE__ */ new Date();
|
|
11
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
12
|
+
}
|
|
13
|
+
function fresh() {
|
|
14
|
+
return { date: todayKey(), sessions: 0, activeMs: 0 };
|
|
15
|
+
}
|
|
16
|
+
async function load() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await AsyncStorage.getItem(KEY);
|
|
19
|
+
if (raw) {
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
_tally = parsed?.date === todayKey() ? parsed : fresh();
|
|
22
|
+
} else {
|
|
23
|
+
_tally = fresh();
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
_tally = fresh();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function persist() {
|
|
30
|
+
if (_tally) AsyncStorage.setItem(KEY, JSON.stringify(_tally)).catch(() => {
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function rollIfNewDay() {
|
|
34
|
+
if (!_tally || _tally.date !== todayKey()) {
|
|
35
|
+
_tally = fresh();
|
|
36
|
+
persist();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function flushForeground() {
|
|
40
|
+
if (_foregroundSince == null || !_tally) return;
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
const delta = now - _foregroundSince;
|
|
43
|
+
if (delta > 0) _tally.activeMs += delta;
|
|
44
|
+
_foregroundSince = now;
|
|
45
|
+
}
|
|
46
|
+
function onChange(state) {
|
|
47
|
+
rollIfNewDay();
|
|
48
|
+
if (state === "active") {
|
|
49
|
+
if (_tally) _tally.sessions += 1;
|
|
50
|
+
_foregroundSince = Date.now();
|
|
51
|
+
persist();
|
|
52
|
+
} else {
|
|
53
|
+
flushForeground();
|
|
54
|
+
_foregroundSince = null;
|
|
55
|
+
persist();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function start() {
|
|
59
|
+
if (_started) return;
|
|
60
|
+
_started = true;
|
|
61
|
+
await load();
|
|
62
|
+
rollIfNewDay();
|
|
63
|
+
if (_tally) _tally.sessions += 1;
|
|
64
|
+
_foregroundSince = Date.now();
|
|
65
|
+
persist();
|
|
66
|
+
_sub = AppState.addEventListener("change", onChange);
|
|
67
|
+
}
|
|
68
|
+
function stop() {
|
|
69
|
+
flushForeground();
|
|
70
|
+
persist();
|
|
71
|
+
_sub?.remove();
|
|
72
|
+
_sub = null;
|
|
73
|
+
_started = false;
|
|
74
|
+
}
|
|
75
|
+
async function getToday() {
|
|
76
|
+
if (!_tally) await load();
|
|
77
|
+
rollIfNewDay();
|
|
78
|
+
flushForeground();
|
|
79
|
+
persist();
|
|
80
|
+
if (!_tally) return { app_sessions: null, app_active_min: null };
|
|
81
|
+
return {
|
|
82
|
+
app_sessions: _tally.sessions,
|
|
83
|
+
app_active_min: Math.round(_tally.activeMs / 6e4)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
start,
|
|
89
|
+
stop,
|
|
90
|
+
getToday
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=chunk-KOXF2FZM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/device-bridge/collectors/appEngagement.ts"],"sourcesContent":["/**\n * App Engagement Collector — our OWN usage analytics (no OS permission, fully\n * available on every platform). Feeds the mood summary's app_sessions +\n * app_active_min, which iOS otherwise blocks for third-party apps.\n *\n * app_sessions — count of foreground sessions today (a background→active\n * transition, or a cold start, starts a session).\n * app_active_min — total minutes the app was in the foreground today.\n *\n * Counters are kept per local day and persisted to AsyncStorage so they\n * survive relaunches. start() must be called once at app boot to attach the\n * AppState listener; getToday() returns the current day's tallies.\n */\nimport { AppState, type AppStateStatus } from 'react-native';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\n\nconst KEY = 'ateam.appEngagement.v1';\n\ninterface DayTally {\n date: string; // local YYYY-MM-DD\n sessions: number;\n activeMs: number;\n}\n\nlet _tally: DayTally | null = null;\nlet _foregroundSince: number | null = null;\nlet _started = false;\nlet _sub: { remove: () => void } | null = null;\n\nfunction todayKey(): string {\n const d = new Date();\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\n}\n\nfunction fresh(): DayTally {\n return { date: todayKey(), sessions: 0, activeMs: 0 };\n}\n\nasync function load(): Promise<void> {\n try {\n const raw = await AsyncStorage.getItem(KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as DayTally;\n _tally = parsed?.date === todayKey() ? parsed : fresh();\n } else {\n _tally = fresh();\n }\n } catch {\n _tally = fresh();\n }\n}\n\nfunction persist(): void {\n if (_tally) AsyncStorage.setItem(KEY, JSON.stringify(_tally)).catch(() => {});\n}\n\nfunction rollIfNewDay(): void {\n if (!_tally || _tally.date !== todayKey()) {\n _tally = fresh();\n persist();\n }\n}\n\n/** Fold the time spent foregrounded since the last checkpoint into activeMs. */\nfunction flushForeground(): void {\n if (_foregroundSince == null || !_tally) return;\n const now = Date.now();\n const delta = now - _foregroundSince;\n if (delta > 0) _tally.activeMs += delta;\n _foregroundSince = now;\n}\n\nfunction onChange(state: AppStateStatus): void {\n rollIfNewDay();\n if (state === 'active') {\n // New foreground session.\n if (_tally) _tally.sessions += 1;\n _foregroundSince = Date.now();\n persist();\n } else {\n // Leaving foreground — bank the elapsed active time.\n flushForeground();\n _foregroundSince = null;\n persist();\n }\n}\n\n/** Attach the AppState listener. Idempotent; call once at app boot. */\nexport async function start(): Promise<void> {\n if (_started) return;\n _started = true;\n await load();\n // Count the launch itself as the first active session.\n rollIfNewDay();\n if (_tally) _tally.sessions += 1;\n _foregroundSince = Date.now();\n persist();\n _sub = AppState.addEventListener('change', onChange);\n}\n\nexport function stop(): void {\n flushForeground();\n persist();\n _sub?.remove();\n _sub = null;\n _started = false;\n}\n\n/** Current day's tallies for the mood summary. */\nexport async function getToday(): Promise<{ app_sessions: number | null; app_active_min: number | null }> {\n if (!_tally) await load();\n rollIfNewDay();\n flushForeground(); // include the in-progress foreground stretch\n persist();\n if (!_tally) return { app_sessions: null, app_active_min: null };\n return {\n app_sessions: _tally.sessions,\n app_active_min: Math.round(_tally.activeMs / 60000),\n };\n}\n"],"mappings":";AAaA,SAAS,gBAAqC;AAC9C,OAAO,kBAAkB;AAEzB,IAAM,MAAM;AAQZ,IAAI,SAA0B;AAC9B,IAAI,mBAAkC;AACtC,IAAI,WAAW;AACf,IAAI,OAAsC;AAE1C,SAAS,WAAmB;AAC1B,QAAM,IAAI,oBAAI,KAAK;AACnB,SAAO,GAAG,EAAE,YAAY,CAAC,IAAI,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAChH;AAEA,SAAS,QAAkB;AACzB,SAAO,EAAE,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,EAAE;AACtD;AAEA,eAAe,OAAsB;AACnC,MAAI;AACF,UAAM,MAAM,MAAM,aAAa,QAAQ,GAAG;AAC1C,QAAI,KAAK;AACP,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,eAAS,QAAQ,SAAS,SAAS,IAAI,SAAS,MAAM;AAAA,IACxD,OAAO;AACL,eAAS,MAAM;AAAA,IACjB;AAAA,EACF,QAAQ;AACN,aAAS,MAAM;AAAA,EACjB;AACF;AAEA,SAAS,UAAgB;AACvB,MAAI,OAAQ,cAAa,QAAQ,KAAK,KAAK,UAAU,MAAM,CAAC,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AAC9E;AAEA,SAAS,eAAqB;AAC5B,MAAI,CAAC,UAAU,OAAO,SAAS,SAAS,GAAG;AACzC,aAAS,MAAM;AACf,YAAQ;AAAA,EACV;AACF;AAGA,SAAS,kBAAwB;AAC/B,MAAI,oBAAoB,QAAQ,CAAC,OAAQ;AACzC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,QAAQ,MAAM;AACpB,MAAI,QAAQ,EAAG,QAAO,YAAY;AAClC,qBAAmB;AACrB;AAEA,SAAS,SAAS,OAA6B;AAC7C,eAAa;AACb,MAAI,UAAU,UAAU;AAEtB,QAAI,OAAQ,QAAO,YAAY;AAC/B,uBAAmB,KAAK,IAAI;AAC5B,YAAQ;AAAA,EACV,OAAO;AAEL,oBAAgB;AAChB,uBAAmB;AACnB,YAAQ;AAAA,EACV;AACF;AAGA,eAAsB,QAAuB;AAC3C,MAAI,SAAU;AACd,aAAW;AACX,QAAM,KAAK;AAEX,eAAa;AACb,MAAI,OAAQ,QAAO,YAAY;AAC/B,qBAAmB,KAAK,IAAI;AAC5B,UAAQ;AACR,SAAO,SAAS,iBAAiB,UAAU,QAAQ;AACrD;AAEO,SAAS,OAAa;AAC3B,kBAAgB;AAChB,UAAQ;AACR,QAAM,OAAO;AACb,SAAO;AACP,aAAW;AACb;AAGA,eAAsB,WAAoF;AACxG,MAAI,CAAC,OAAQ,OAAM,KAAK;AACxB,eAAa;AACb,kBAAgB;AAChB,UAAQ;AACR,MAAI,CAAC,OAAQ,QAAO,EAAE,cAAc,MAAM,gBAAgB,KAAK;AAC/D,SAAO;AAAA,IACL,cAAc,OAAO;AAAA,IACrB,gBAAgB,KAAK,MAAM,OAAO,WAAW,GAAK;AAAA,EACpD;AACF;","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/oauth/expoOAuth.tsx
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import {
|
|
4
|
+
GoogleSignin,
|
|
5
|
+
statusCodes
|
|
6
|
+
} from "@react-native-google-signin/google-signin";
|
|
7
|
+
import * as AppleAuthentication from "expo-apple-authentication";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
var NOOP = () => {
|
|
10
|
+
};
|
|
11
|
+
function createExpoOAuthProvider(host = {}) {
|
|
12
|
+
return {
|
|
13
|
+
async signInWithGoogle(cfg) {
|
|
14
|
+
const webClientId = cfg?.webClientId || host.googleWebClientId || "";
|
|
15
|
+
const iosClientId = cfg?.iosClientId || host.googleIosClientId || "";
|
|
16
|
+
const params = { webClientId };
|
|
17
|
+
if (iosClientId) params.iosClientId = iosClientId;
|
|
18
|
+
GoogleSignin.configure(params);
|
|
19
|
+
try {
|
|
20
|
+
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
|
|
21
|
+
const result = await GoogleSignin.signIn();
|
|
22
|
+
return result?.data?.idToken || result?.idToken || null;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
const code = err?.code;
|
|
25
|
+
if (code === statusCodes.SIGN_IN_CANCELLED || code === statusCodes.IN_PROGRESS) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
async signInWithApple() {
|
|
32
|
+
try {
|
|
33
|
+
const credential = await AppleAuthentication.signInAsync({
|
|
34
|
+
requestedScopes: [
|
|
35
|
+
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
|
36
|
+
AppleAuthentication.AppleAuthenticationScope.EMAIL
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
return credential.identityToken || null;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err?.code === "ERR_REQUEST_CANCELED") return null;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
async isAppleAvailable() {
|
|
46
|
+
if (Platform.OS !== "ios") return false;
|
|
47
|
+
try {
|
|
48
|
+
return await AppleAuthentication.isAvailableAsync();
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
renderAppleButton({ onPress, disabled, style }) {
|
|
54
|
+
return /* @__PURE__ */ jsx(
|
|
55
|
+
AppleAuthentication.AppleAuthenticationButton,
|
|
56
|
+
{
|
|
57
|
+
buttonType: AppleAuthentication.AppleAuthenticationButtonType.CONTINUE,
|
|
58
|
+
buttonStyle: AppleAuthentication.AppleAuthenticationButtonStyle.BLACK,
|
|
59
|
+
cornerRadius: 999,
|
|
60
|
+
style,
|
|
61
|
+
onPress: disabled ? NOOP : onPress
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
createExpoOAuthProvider
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=chunk-L4V7OCJ3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oauth/expoOAuth.tsx"],"sourcesContent":["/**\n * @ateam-ai-mobile/expo/oauth — the native OAuth provider for the relocated\n * auth screens (LoginScreen / JoinScreen / OpenJoinScreen in @ateam-ai-mobile/ui).\n *\n * This is the ONLY place @react-native-google-signin + expo-apple-authentication\n * are imported. The `ui` package stays native-free: its screens call\n * getOAuthProvider() for the id_token acquisition + the real Apple HIG button.\n *\n * Deliberately a SUBPATH (`@ateam-ai-mobile/expo/oauth`), not part of the main\n * entry — same rule as `/bridge` and `/device`: a host that doesn't install\n * these native modules must still be able to build @ateam-ai-mobile/expo.\n *\n * Behaviour is ported verbatim from the app's old login.tsx so the iPhone\n * parity gate sees no change:\n * - Google: configure({ webClientId, iosClientId }) → hasPlayServices →\n * signIn → normalize the v15-vs-older id_token shape. A missing iosClientId\n * silently falls back to a web flow (unchanged); the caller gates on\n * webClientId, so we never reach here without one for the login path.\n * - a user-cancel (SIGN_IN_CANCELLED / IN_PROGRESS / ERR_REQUEST_CANCELED)\n * resolves to null — the screen treats null as \"no error\", never a red flash.\n */\nimport React from 'react';\nimport { Platform } from 'react-native';\nimport {\n GoogleSignin,\n statusCodes,\n type ConfigureParams,\n} from '@react-native-google-signin/google-signin';\nimport * as AppleAuthentication from 'expo-apple-authentication';\nimport type { OAuthProvider, OAuthGoogleConfig } from '@ateam-ai-mobile/ui';\n\nconst NOOP = () => {};\n\nexport interface ExpoOAuthConfig {\n /**\n * The host's bundled Google client ids, used by the join / open-join screens\n * that carry no tenant config (they pass no cfg to signInWithGoogle). The\n * login screen passes the tenant's configured ids and overrides these.\n */\n googleWebClientId?: string;\n googleIosClientId?: string;\n}\n\n/**\n * Build the native OAuth provider. Wire it once at startup:\n * setOAuthProvider(createExpoOAuthProvider({ googleWebClientId, googleIosClientId }));\n */\nexport function createExpoOAuthProvider(host: ExpoOAuthConfig = {}): OAuthProvider {\n return {\n async signInWithGoogle(cfg?: OAuthGoogleConfig): Promise<string | null> {\n const webClientId = cfg?.webClientId || host.googleWebClientId || '';\n const iosClientId = cfg?.iosClientId || host.googleIosClientId || '';\n // configure is idempotent + cheap; do it at sign-in time so the tenant's\n // ids are always current (replaces login.tsx's configure effect).\n const params: ConfigureParams = { webClientId };\n if (iosClientId) (params as any).iosClientId = iosClientId;\n GoogleSignin.configure(params);\n try {\n // android-only; no-op on iOS. Called defensively, as before.\n await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });\n const result: any = await GoogleSignin.signIn();\n // v15+ returns { data: { idToken } }; older returns { idToken }.\n return result?.data?.idToken || result?.idToken || null;\n } catch (err: any) {\n const code = err?.code;\n if (code === statusCodes.SIGN_IN_CANCELLED || code === statusCodes.IN_PROGRESS) {\n return null;\n }\n throw err;\n }\n },\n\n async signInWithApple(): Promise<string | null> {\n try {\n const credential = await AppleAuthentication.signInAsync({\n requestedScopes: [\n AppleAuthentication.AppleAuthenticationScope.FULL_NAME,\n AppleAuthentication.AppleAuthenticationScope.EMAIL,\n ],\n });\n return credential.identityToken || null;\n } catch (err: any) {\n // ERR_REQUEST_CANCELED = the user dismissed the sheet.\n if (err?.code === 'ERR_REQUEST_CANCELED') return null;\n throw err;\n }\n },\n\n async isAppleAvailable(): Promise<boolean> {\n if (Platform.OS !== 'ios') return false;\n try {\n return await AppleAuthentication.isAvailableAsync();\n } catch {\n return false;\n }\n },\n\n renderAppleButton({ onPress, disabled, style }): React.ReactNode {\n // Apple's HIG requires their own styled button when Apple Sign-In sits\n // alongside other providers. Same chrome the app shipped: CONTINUE /\n // BLACK / fully-rounded. Layout (width/height) comes from the screen.\n return (\n <AppleAuthentication.AppleAuthenticationButton\n buttonType={AppleAuthentication.AppleAuthenticationButtonType.CONTINUE}\n buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}\n cornerRadius={999}\n style={style as any}\n onPress={disabled ? NOOP : onPress}\n />\n );\n },\n };\n}\n"],"mappings":";AAsBA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,YAAY,yBAAyB;AA0E7B;AAvER,IAAM,OAAO,MAAM;AAAC;AAgBb,SAAS,wBAAwB,OAAwB,CAAC,GAAkB;AACjF,SAAO;AAAA,IACL,MAAM,iBAAiB,KAAiD;AACtE,YAAM,cAAc,KAAK,eAAe,KAAK,qBAAqB;AAClE,YAAM,cAAc,KAAK,eAAe,KAAK,qBAAqB;AAGlE,YAAM,SAA0B,EAAE,YAAY;AAC9C,UAAI,YAAa,CAAC,OAAe,cAAc;AAC/C,mBAAa,UAAU,MAAM;AAC7B,UAAI;AAEF,cAAM,aAAa,gBAAgB,EAAE,8BAA8B,KAAK,CAAC;AACzE,cAAM,SAAc,MAAM,aAAa,OAAO;AAE9C,eAAO,QAAQ,MAAM,WAAW,QAAQ,WAAW;AAAA,MACrD,SAAS,KAAU;AACjB,cAAM,OAAO,KAAK;AAClB,YAAI,SAAS,YAAY,qBAAqB,SAAS,YAAY,aAAa;AAC9E,iBAAO;AAAA,QACT;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,kBAA0C;AAC9C,UAAI;AACF,cAAM,aAAa,MAA0B,gCAAY;AAAA,UACvD,iBAAiB;AAAA,YACK,6CAAyB;AAAA,YACzB,6CAAyB;AAAA,UAC/C;AAAA,QACF,CAAC;AACD,eAAO,WAAW,iBAAiB;AAAA,MACrC,SAAS,KAAU;AAEjB,YAAI,KAAK,SAAS,uBAAwB,QAAO;AACjD,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,mBAAqC;AACzC,UAAI,SAAS,OAAO,MAAO,QAAO;AAClC,UAAI;AACF,eAAO,MAA0B,qCAAiB;AAAA,MACpD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,kBAAkB,EAAE,SAAS,UAAU,MAAM,GAAoB;AAI/D,aACE;AAAA,QAAqB;AAAA,QAApB;AAAA,UACC,YAAgC,kDAA8B;AAAA,UAC9D,aAAiC,mDAA+B;AAAA,UAChE,cAAc;AAAA,UACd;AAAA,UACA,SAAS,WAAW,OAAO;AAAA;AAAA,MAC7B;AAAA,IAEJ;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/vision/index.ts
|
|
2
|
+
import { requireOptionalNativeModule } from "expo-modules-core";
|
|
3
|
+
import { requireNativeView } from "expo";
|
|
4
|
+
var CameraPreview = (() => {
|
|
5
|
+
try {
|
|
6
|
+
return requireNativeView("AdaVision");
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
})();
|
|
11
|
+
var native = requireOptionalNativeModule("AdaVision");
|
|
12
|
+
function isSupported() {
|
|
13
|
+
try {
|
|
14
|
+
return native?.isSupported() ?? false;
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function assessQuality(uri) {
|
|
20
|
+
if (!native) throw new Error("ada-vision native module not present in this build");
|
|
21
|
+
return native.assessQuality(uri);
|
|
22
|
+
}
|
|
23
|
+
async function startVision(intervalMs = 1e3) {
|
|
24
|
+
if (!native) throw new Error("ada-vision native module not present in this build");
|
|
25
|
+
return native.startVision(intervalMs);
|
|
26
|
+
}
|
|
27
|
+
function stopVision() {
|
|
28
|
+
native?.stopVision?.();
|
|
29
|
+
}
|
|
30
|
+
function onVisionFrame(cb) {
|
|
31
|
+
if (!native) return { remove() {
|
|
32
|
+
} };
|
|
33
|
+
return native.addListener("visionFrame", cb);
|
|
34
|
+
}
|
|
35
|
+
function visionRetentionMs() {
|
|
36
|
+
return native?.visionRetentionMs?.() ?? 0;
|
|
37
|
+
}
|
|
38
|
+
function qualityById(id) {
|
|
39
|
+
return native?.qualityById?.(id) ?? null;
|
|
40
|
+
}
|
|
41
|
+
async function readText(id, languages) {
|
|
42
|
+
if (!native) throw new Error("ada-vision native module not present in this build");
|
|
43
|
+
return native.readText(id, languages ?? null);
|
|
44
|
+
}
|
|
45
|
+
async function describe(id, maxLabels = 5) {
|
|
46
|
+
if (!native) throw new Error("ada-vision native module not present in this build");
|
|
47
|
+
return native.describe(id, maxLabels);
|
|
48
|
+
}
|
|
49
|
+
async function detectObjects(id, maxItems = 10, minConfidence = 0.25) {
|
|
50
|
+
if (!native) throw new Error("ada-vision native module not present in this build");
|
|
51
|
+
return native.detectObjects(id, maxItems, minConfidence);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
CameraPreview,
|
|
56
|
+
isSupported,
|
|
57
|
+
assessQuality,
|
|
58
|
+
startVision,
|
|
59
|
+
stopVision,
|
|
60
|
+
onVisionFrame,
|
|
61
|
+
visionRetentionMs,
|
|
62
|
+
qualityById,
|
|
63
|
+
readText,
|
|
64
|
+
describe,
|
|
65
|
+
detectObjects
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=chunk-PCAEUX2N.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vision/index.ts"],"sourcesContent":["// ada-vision — on-device image understanding, our own native code (no camera lib).\n//\n// On-device, frames never leave the phone:\n// assessQuality(uri) — one-shot quality of a LOCAL frame file.\n// startVision()/onVisionFrame — the headless camera session that publishes a\n// timeline frame (id + quality, never pixels) at a\n// cadence; readText/describe/detectObjects resolve\n// a published id ON-DEVICE.\n//\n// The luma math mirrors ./analysis.js (which has the exhaustive unit tests).\n//\n// requireOptionalNativeModule: on a binary WITHOUT the native module, the import\n// is null, isSupported() is false, and the session calls refuse cleanly (no crash).\n\nimport type { ComponentType } from 'react';\nimport { requireOptionalNativeModule } from 'expo-modules-core';\nimport { requireNativeView } from 'expo';\n\n/** TEST-ONLY live preview bound to the same headless session (so a tester can\n * aim). The real Guide runs headless and never mounts this. Null on a binary\n * without the native view, so importing this module never crashes. */\nexport const CameraPreview: ComponentType<any> | null = (() => {\n try { return requireNativeView('AdaVision'); } catch { return null; }\n})();\n\nexport interface RawFrameQuality {\n blur: 'sharp' | 'slight' | 'severe' | 'unknown';\n exposure: 'good' | 'dark' | 'bright' | 'unknown';\n /** true = lens covered; false = clearly not; null = not assessed. */\n obstructed: boolean | null;\n}\n\ninterface Subscription { remove(): void }\n\n/** A published timeline frame from the vision session (metadata only — never\n * pixels; the buffer stays in the native ring, resolved by id). */\nexport interface VisionFrame {\n id: string;\n ts: number;\n expiresAt: number;\n blur: RawFrameQuality['blur'];\n exposure: RawFrameQuality['exposure'];\n obstructed: RawFrameQuality['obstructed'];\n}\n\n/** One OCR result on a frame — matches the SDK's VisionText. */\nexport interface VisionText {\n text: string;\n /** 0..1 recogniser confidence (never a 1.0 filler). */\n confidence: number | null;\n /** Normalised 0..1 box, origin TOP-left, in the UPRIGHT portrait frame (native\n * rotates frames at capture) — x runs from the user's left to their right. */\n box?: { x: number; y: number; width: number; height: number };\n}\n\n/** One classifier label — a vocabulary term, NOT prose. Matches VisionLabel. */\nexport interface VisionLabel {\n label: string;\n confidence: number | null;\n}\n\n/** One detected object — a located item with a box, from the bundled detector. */\nexport interface VisionObject {\n /** Open-Images class name (e.g. \"Car\", \"Traffic light\", \"Building\", \"Person\"). */\n label: string;\n /** 0..1 detector confidence. */\n confidence: number | null;\n /** Normalised 0..1 box, origin TOP-left, in the UPRIGHT portrait frame (native\n * rotates frames at capture) — x runs from the user's left to their right. */\n box?: { x: number; y: number; width: number; height: number };\n}\n\ninterface AdaVisionNative {\n isSupported(): boolean;\n assessQuality(uri: string): Promise<RawFrameQuality>;\n startVision(intervalMs: number): Promise<void>;\n stopVision(): void;\n visionRetentionMs(): number;\n qualityById(id: string): RawFrameQuality | null;\n readText(id: string, languages: string[] | null): Promise<VisionText[]>;\n describe(id: string, maxLabels: number): Promise<VisionLabel[]>;\n detectObjects(id: string, maxItems: number, minConfidence: number): Promise<VisionObject[]>;\n addListener(event: 'visionFrame', cb: (f: VisionFrame) => void): Subscription;\n}\n\nconst native = requireOptionalNativeModule<AdaVisionNative>('AdaVision');\n\n/** Is the native vision module present in this binary? */\nexport function isSupported(): boolean {\n try { return native?.isSupported() ?? false; } catch { return false; }\n}\n\n/** One-shot: measure a frame's quality from its LOCAL file. Throws if it cannot look. */\nexport async function assessQuality(uri: string): Promise<RawFrameQuality> {\n if (!native) throw new Error('ada-vision native module not present in this build');\n return native.assessQuality(uri);\n}\n\n// ── Vision session (feeds the Visual Engine) ──\n\n/** Start the vision session — publishes a timeline frame every `intervalMs`. */\nexport async function startVision(intervalMs = 1000): Promise<void> {\n if (!native) throw new Error('ada-vision native module not present in this build');\n return native.startVision(intervalMs);\n}\n\n/** Stop the vision session and release the camera. */\nexport function stopVision(): void {\n native?.stopVision?.();\n}\n\n/** Subscribe to published timeline frames (id + quality + expiry; no pixels). */\nexport function onVisionFrame(cb: (f: VisionFrame) => void): Subscription {\n if (!native) return { remove() {} };\n return native.addListener('visionFrame', cb);\n}\n\n/** How far back the native ring can still resolve a frame id (ms). */\nexport function visionRetentionMs(): number {\n return native?.visionRetentionMs?.() ?? 0;\n}\n\n/** Resolve a published frame id → its measured quality (null if evicted). */\nexport function qualityById(id: string): RawFrameQuality | null {\n return native?.qualityById?.(id) ?? null;\n}\n\n/** On-device OCR on a published frame. [] = looked, no text; throws = couldn't look. */\nexport async function readText(id: string, languages?: string[]): Promise<VisionText[]> {\n if (!native) throw new Error('ada-vision native module not present in this build');\n return native.readText(id, languages ?? null);\n}\n\n/** On-device image classification → labels (not prose). [] = nothing recognised. */\nexport async function describe(id: string, maxLabels = 5): Promise<VisionLabel[]> {\n if (!native) throw new Error('ada-vision native module not present in this build');\n return native.describe(id, maxLabels);\n}\n\n/** On-device object detection (bundled YOLOv8n, Open-Images vocabulary) → located\n * items with boxes. [] = looked, found nothing above `minConfidence`; throws =\n * couldn't look (id aged out, or the model isn't in this build). */\nexport async function detectObjects(id: string, maxItems = 10, minConfidence = 0.25): Promise<VisionObject[]> {\n if (!native) throw new Error('ada-vision native module not present in this build');\n return native.detectObjects(id, maxItems, minConfidence);\n}\n"],"mappings":";AAeA,SAAS,mCAAmC;AAC5C,SAAS,yBAAyB;AAK3B,IAAM,iBAA4C,MAAM;AAC7D,MAAI;AAAE,WAAO,kBAAkB,WAAW;AAAA,EAAG,QAAQ;AAAE,WAAO;AAAA,EAAM;AACtE,GAAG;AA8DH,IAAM,SAAS,4BAA6C,WAAW;AAGhE,SAAS,cAAuB;AACrC,MAAI;AAAE,WAAO,QAAQ,YAAY,KAAK;AAAA,EAAO,QAAQ;AAAE,WAAO;AAAA,EAAO;AACvE;AAGA,eAAsB,cAAc,KAAuC;AACzE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AACjF,SAAO,OAAO,cAAc,GAAG;AACjC;AAKA,eAAsB,YAAY,aAAa,KAAqB;AAClE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AACjF,SAAO,OAAO,YAAY,UAAU;AACtC;AAGO,SAAS,aAAmB;AACjC,UAAQ,aAAa;AACvB;AAGO,SAAS,cAAc,IAA4C;AACxE,MAAI,CAAC,OAAQ,QAAO,EAAE,SAAS;AAAA,EAAC,EAAE;AAClC,SAAO,OAAO,YAAY,eAAe,EAAE;AAC7C;AAGO,SAAS,oBAA4B;AAC1C,SAAO,QAAQ,oBAAoB,KAAK;AAC1C;AAGO,SAAS,YAAY,IAAoC;AAC9D,SAAO,QAAQ,cAAc,EAAE,KAAK;AACtC;AAGA,eAAsB,SAAS,IAAY,WAA6C;AACtF,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AACjF,SAAO,OAAO,SAAS,IAAI,aAAa,IAAI;AAC9C;AAGA,eAAsB,SAAS,IAAY,YAAY,GAA2B;AAChF,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AACjF,SAAO,OAAO,SAAS,IAAI,SAAS;AACtC;AAKA,eAAsB,cAAc,IAAY,WAAW,IAAI,gBAAgB,MAA+B;AAC5G,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oDAAoD;AACjF,SAAO,OAAO,cAAc,IAAI,UAAU,aAAa;AACzD;","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
__require,
|
|
30
|
+
__esm,
|
|
31
|
+
__export,
|
|
32
|
+
__toCommonJS
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=chunk-WMJKH4XE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|