@ateam-ai-mobile/expo 0.1.4 → 0.1.7

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.
Files changed (158) hide show
  1. package/android/build.gradle +16 -0
  2. package/android/src/main/java/expo/modules/adavision/AdaVisionModule.kt +75 -0
  3. package/dist/attachmentCache-DhQTqi2E.d.mts +18 -0
  4. package/dist/attachmentCache-DhQTqi2E.d.ts +18 -0
  5. package/dist/bootstrap.d.mts +95 -0
  6. package/dist/bootstrap.d.ts +95 -0
  7. package/dist/bootstrap.js +2473 -0
  8. package/dist/bootstrap.js.map +1 -0
  9. package/dist/bootstrap.mjs +64 -0
  10. package/dist/bootstrap.mjs.map +1 -0
  11. package/dist/chunk-4BMWVN25.mjs +91 -0
  12. package/dist/chunk-4BMWVN25.mjs.map +1 -0
  13. package/dist/chunk-4XYXJYN5.mjs +1513 -0
  14. package/dist/chunk-4XYXJYN5.mjs.map +1 -0
  15. package/dist/chunk-5HM6UA2K.mjs +571 -0
  16. package/dist/chunk-5HM6UA2K.mjs.map +1 -0
  17. package/dist/chunk-6LXF3JVX.mjs +96 -0
  18. package/dist/chunk-6LXF3JVX.mjs.map +1 -0
  19. package/dist/chunk-FF6ZZZXH.mjs +71 -0
  20. package/dist/chunk-FF6ZZZXH.mjs.map +1 -0
  21. package/dist/chunk-KOXF2FZM.mjs +92 -0
  22. package/dist/chunk-KOXF2FZM.mjs.map +1 -0
  23. package/dist/chunk-L4V7OCJ3.mjs +71 -0
  24. package/dist/chunk-L4V7OCJ3.mjs.map +1 -0
  25. package/dist/chunk-TZXWVYYR.mjs +67 -0
  26. package/dist/chunk-TZXWVYYR.mjs.map +1 -0
  27. package/dist/chunk-VQJPSLHP.mjs +1801 -0
  28. package/dist/chunk-VQJPSLHP.mjs.map +1 -0
  29. package/dist/chunk-WMJKH4XE.mjs +34 -0
  30. package/dist/chunk-WMJKH4XE.mjs.map +1 -0
  31. package/dist/chunk-XQSY6MHP.mjs +213 -0
  32. package/dist/chunk-XQSY6MHP.mjs.map +1 -0
  33. package/dist/createBridge.mjs +2 -0
  34. package/dist/createBridge.mjs.map +1 -1
  35. package/dist/device/createDeviceProvider.d.mts +23 -0
  36. package/dist/device/createDeviceProvider.d.ts +23 -0
  37. package/dist/device/createDeviceProvider.js +967 -0
  38. package/dist/device/createDeviceProvider.js.map +1 -0
  39. package/dist/device/createDeviceProvider.mjs +17 -0
  40. package/dist/device/createDeviceProvider.mjs.map +1 -0
  41. package/dist/device-bridge/collectors/appEngagement.d.mts +10 -0
  42. package/dist/device-bridge/collectors/appEngagement.d.ts +10 -0
  43. package/dist/device-bridge/collectors/appEngagement.js +128 -0
  44. package/dist/device-bridge/collectors/appEngagement.js.map +1 -0
  45. package/dist/device-bridge/collectors/appEngagement.mjs +12 -0
  46. package/dist/device-bridge/collectors/appEngagement.mjs.map +1 -0
  47. package/dist/device-bridge/collectors/health.d.mts +24 -0
  48. package/dist/device-bridge/collectors/health.d.ts +24 -0
  49. package/dist/device-bridge/collectors/health.js +238 -0
  50. package/dist/device-bridge/collectors/health.js.map +1 -0
  51. package/dist/device-bridge/collectors/health.mjs +18 -0
  52. package/dist/device-bridge/collectors/health.mjs.map +1 -0
  53. package/dist/device-bridge/cookies.d.mts +49 -0
  54. package/dist/device-bridge/cookies.d.ts +49 -0
  55. package/dist/device-bridge/cookies.js +90 -0
  56. package/dist/device-bridge/cookies.js.map +1 -0
  57. package/dist/device-bridge/cookies.mjs +14 -0
  58. package/dist/device-bridge/cookies.mjs.map +1 -0
  59. package/dist/device-bridge/index.d.mts +483 -0
  60. package/dist/device-bridge/index.d.ts +483 -0
  61. package/dist/device-bridge/index.js +2084 -0
  62. package/dist/device-bridge/index.js.map +1 -0
  63. package/dist/device-bridge/index.mjs +32 -0
  64. package/dist/device-bridge/index.mjs.map +1 -0
  65. package/dist/device-bridge/nfc.d.mts +30 -0
  66. package/dist/device-bridge/nfc.d.ts +30 -0
  67. package/dist/device-bridge/nfc.js +109 -0
  68. package/dist/device-bridge/nfc.js.map +1 -0
  69. package/dist/device-bridge/nfc.mjs +14 -0
  70. package/dist/device-bridge/nfc.mjs.map +1 -0
  71. package/dist/index.d.mts +493 -18
  72. package/dist/index.d.ts +493 -18
  73. package/dist/index.js +1409 -6
  74. package/dist/index.js.map +1 -1
  75. package/dist/index.mjs +23 -194
  76. package/dist/index.mjs.map +1 -1
  77. package/dist/oauth/expoOAuth.d.mts +18 -0
  78. package/dist/oauth/expoOAuth.d.ts +18 -0
  79. package/dist/oauth/expoOAuth.js +102 -0
  80. package/dist/oauth/expoOAuth.js.map +1 -0
  81. package/dist/oauth/expoOAuth.mjs +8 -0
  82. package/dist/oauth/expoOAuth.mjs.map +1 -0
  83. package/dist/types-BRanJYTn.d.mts +138 -0
  84. package/dist/types-BRanJYTn.d.ts +138 -0
  85. package/dist/vision/index.d.mts +81 -0
  86. package/dist/vision/index.d.ts +81 -0
  87. package/dist/vision/index.js +101 -0
  88. package/dist/vision/index.js.map +1 -0
  89. package/dist/vision/index.mjs +28 -0
  90. package/dist/vision/index.mjs.map +1 -0
  91. package/expo-module.config.json +9 -0
  92. package/ios/AdaVision.podspec +28 -0
  93. package/ios/AdaVisionModule.swift +432 -0
  94. package/ios/Resources/yolov8n-oiv7.mlmodelc/analytics/coremldata.bin +0 -0
  95. package/ios/Resources/yolov8n-oiv7.mlmodelc/coremldata.bin +0 -0
  96. package/ios/Resources/yolov8n-oiv7.mlmodelc/metadata.json +773 -0
  97. package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/analytics/coremldata.bin +0 -0
  98. package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/coremldata.bin +0 -0
  99. package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/model.mil +720 -0
  100. package/ios/Resources/yolov8n-oiv7.mlmodelc/model0/weights/weight.bin +0 -0
  101. package/ios/Resources/yolov8n-oiv7.mlmodelc/model1/coremldata.bin +0 -0
  102. package/package.json +107 -7
  103. package/src/bootstrap.ts +159 -0
  104. package/src/camera/CameraTaskHost.tsx +175 -0
  105. package/src/camera/cameraTasks.ts +129 -0
  106. package/src/device/createDeviceProvider.ts +832 -0
  107. package/src/device-bridge/actions/calendar-actions.ts +217 -0
  108. package/src/device-bridge/actions/dnd.ts +74 -0
  109. package/src/device-bridge/actions/health-actions.ts +58 -0
  110. package/src/device-bridge/actions/index.ts +12 -0
  111. package/src/device-bridge/actions/messaging.ts +74 -0
  112. package/src/device-bridge/actions/navigation.ts +88 -0
  113. package/src/device-bridge/actions/queue-poller.ts +151 -0
  114. package/src/device-bridge/background.ts +137 -0
  115. package/src/device-bridge/biometrics.ts +64 -0
  116. package/src/device-bridge/capabilities.ts +218 -0
  117. package/src/device-bridge/collectors/appEngagement.ts +120 -0
  118. package/src/device-bridge/collectors/battery.ts +83 -0
  119. package/src/device-bridge/collectors/calendar.ts +162 -0
  120. package/src/device-bridge/collectors/camera.ts +27 -0
  121. package/src/device-bridge/collectors/connectivity.ts +60 -0
  122. package/src/device-bridge/collectors/contacts.ts +200 -0
  123. package/src/device-bridge/collectors/health.ts +299 -0
  124. package/src/device-bridge/collectors/index.ts +12 -0
  125. package/src/device-bridge/collectors/location.ts +224 -0
  126. package/src/device-bridge/collectors/mood.ts +141 -0
  127. package/src/device-bridge/collectors/notifications.ts +70 -0
  128. package/src/device-bridge/cookies.ts +89 -0
  129. package/src/device-bridge/dynamic-connector.ts +422 -0
  130. package/src/device-bridge/index.ts +467 -0
  131. package/src/device-bridge/nfc.ts +85 -0
  132. package/src/device-bridge/plugin.ts +71 -0
  133. package/src/device-bridge/relay-client.ts +237 -0
  134. package/src/device-bridge/scanner.ts +42 -0
  135. package/src/device-bridge/sharing.ts +35 -0
  136. package/src/device-bridge/types.ts +326 -0
  137. package/src/device-state/activityClassifier.ts +151 -0
  138. package/src/device-state/activityEngine.ts +252 -0
  139. package/src/device-state/collectors.ts +31 -0
  140. package/src/device-state/domains/activity.ts +93 -0
  141. package/src/device-state/domains/capability.ts +60 -0
  142. package/src/device-state/domains/connectivity.ts +60 -0
  143. package/src/device-state/domains/deviceContext.ts +55 -0
  144. package/src/device-state/domains/interaction.ts +59 -0
  145. package/src/device-state/domains/spatial.ts +283 -0
  146. package/src/device-state/domains/visual.ts +100 -0
  147. package/src/device-state/headingSource.ts +84 -0
  148. package/src/device-state/index.ts +77 -0
  149. package/src/device-state/quality.ts +60 -0
  150. package/src/device-state/replay.ts +220 -0
  151. package/src/device-state/turnDetector.ts +121 -0
  152. package/src/device-state/visualEngine.ts +341 -0
  153. package/src/haptics.ts +4 -0
  154. package/src/index.ts +37 -0
  155. package/src/oauth/expoOAuth.tsx +113 -0
  156. package/src/vision/analysis.js +80 -0
  157. package/src/vision/index.ts +144 -0
  158. package/src/vision/ring.js +35 -0
@@ -0,0 +1,96 @@
1
+ // src/camera/cameraTasks.ts
2
+ function cameraError(code, message) {
3
+ const err = new Error(message);
4
+ err.code = code;
5
+ return err;
6
+ }
7
+ var CameraTaskBroker = class {
8
+ constructor() {
9
+ this.task = null;
10
+ this.listeners = /* @__PURE__ */ new Set();
11
+ /** Set by the host while a camera is mounted and ready. */
12
+ this.hostMounted = false;
13
+ }
14
+ current() {
15
+ return this.task;
16
+ }
17
+ subscribe(fn) {
18
+ this.listeners.add(fn);
19
+ return () => this.listeners.delete(fn);
20
+ }
21
+ /**
22
+ * Declare that a `CameraTaskHost` exists.
23
+ *
24
+ * Without it nothing will ever settle the promise, so a request must fail
25
+ * IMMEDIATELY and say why. The alternative is a plugin awaiting forever,
26
+ * which is the worst failure of the set: no error, no timeout, no camera,
27
+ * and a conversation that simply stops.
28
+ */
29
+ setHostMounted(mounted) {
30
+ this.hostMounted = mounted;
31
+ if (!mounted) this.fail(cameraError("UNAVAILABLE", "The camera surface was dismissed before the task completed."));
32
+ this.notify();
33
+ }
34
+ isHostMounted() {
35
+ return this.hostMounted;
36
+ }
37
+ /** Enqueue and hand back the promise the plugin awaits. */
38
+ run(kind, opts) {
39
+ if (!this.hostMounted) {
40
+ return Promise.reject(cameraError(
41
+ "UNAVAILABLE",
42
+ "No camera surface is mounted in this app, so the camera cannot be shown. The host must render <CameraTaskHost /> once, near the app root."
43
+ ));
44
+ }
45
+ if (this.task) {
46
+ return Promise.reject(cameraError(
47
+ "BUSY",
48
+ `The camera is already in use (${this.task.kind}). Only one camera task can run at a time.`
49
+ ));
50
+ }
51
+ return new Promise((resolve, reject) => {
52
+ this.task = { kind, opts, resolve, reject };
53
+ this.notify();
54
+ });
55
+ }
56
+ /** Settle the running task successfully and clear it. */
57
+ complete(value) {
58
+ const t = this.task;
59
+ if (!t) return;
60
+ this.task = null;
61
+ this.notify();
62
+ t.resolve(value);
63
+ }
64
+ /** Settle the running task with a failure and clear it. */
65
+ fail(err) {
66
+ const t = this.task;
67
+ if (!t) return;
68
+ this.task = null;
69
+ this.notify();
70
+ t.reject(err);
71
+ }
72
+ /**
73
+ * The user backed out.
74
+ *
75
+ * A distinct code, because "the user chose not to" is not a malfunction and
76
+ * a guide should say something different about it than about a broken lens.
77
+ */
78
+ cancel() {
79
+ this.fail(cameraError("CANCELLED", "Cancelled \u2014 the camera was dismissed before anything was captured."));
80
+ }
81
+ notify() {
82
+ for (const fn of this.listeners) {
83
+ try {
84
+ fn();
85
+ } catch {
86
+ }
87
+ }
88
+ }
89
+ };
90
+ var cameraTasks = new CameraTaskBroker();
91
+
92
+ export {
93
+ cameraError,
94
+ cameraTasks
95
+ };
96
+ //# sourceMappingURL=chunk-6LXF3JVX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/camera/cameraTasks.ts"],"sourcesContent":["/**\n * The broker between a plugin's `await` and a mounted camera.\n *\n * WHY THIS EXISTS. expo-camera has no headless capture: scanning a barcode and\n * recording a clip both require a MOUNTED `<CameraView>`. But a plugin calls\n * `native.scanner.scanOnce()` and expects a promise — it has no way to mount\n * anything, and it must not have to.\n *\n * So the API enqueues a TASK here and awaits it; `CameraTaskHost`, mounted once\n * by the app, notices, shows the camera, does the work, and settles the\n * promise. This is the same split as the Visual engine, for the same reason —\n * and it is why `native.camera.scanBarcode` refuses and points here rather than\n * pretending a function call can own a React lifecycle.\n *\n * ONE TASK AT A TIME, and it is the phone that says so: there is one camera. A\n * second request while one is running is REJECTED rather than queued, because a\n * queued scan would open the camera again minutes later, long after the user\n * moved on and with no idea why it appeared.\n */\nexport interface ScanTask {\n kind: 'scan';\n opts: { formats?: string[]; timeoutMs?: number };\n resolve: (v: { data: string; format: string }) => void;\n reject: (e: Error) => void;\n}\n\nexport interface RecordTask {\n kind: 'record';\n opts: { maxDurationMs?: number; withAudio?: boolean };\n resolve: (v: { uri: string; durationMs: number | null }) => void;\n reject: (e: Error) => void;\n}\n\nexport type CameraTask = ScanTask | RecordTask;\n\ntype Listener = () => void;\n\n/** Typed failure, so a caller can say something specific and true. */\nexport function cameraError(code: string, message: string): Error {\n const err: any = new Error(message);\n err.code = code;\n return err;\n}\n\nclass CameraTaskBroker {\n private task: CameraTask | null = null;\n private listeners = new Set<Listener>();\n /** Set by the host while a camera is mounted and ready. */\n private hostMounted = false;\n\n current(): CameraTask | null { return this.task; }\n\n subscribe(fn: Listener): () => void {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n\n /**\n * Declare that a `CameraTaskHost` exists.\n *\n * Without it nothing will ever settle the promise, so a request must fail\n * IMMEDIATELY and say why. The alternative is a plugin awaiting forever,\n * which is the worst failure of the set: no error, no timeout, no camera,\n * and a conversation that simply stops.\n */\n setHostMounted(mounted: boolean): void {\n this.hostMounted = mounted;\n if (!mounted) this.fail(cameraError('UNAVAILABLE', 'The camera surface was dismissed before the task completed.'));\n this.notify();\n }\n\n isHostMounted(): boolean { return this.hostMounted; }\n\n /** Enqueue and hand back the promise the plugin awaits. */\n run<T>(kind: CameraTask['kind'], opts: any): Promise<T> {\n if (!this.hostMounted) {\n return Promise.reject(cameraError(\n 'UNAVAILABLE',\n 'No camera surface is mounted in this app, so the camera cannot be shown. ' +\n 'The host must render <CameraTaskHost /> once, near the app root.',\n ));\n }\n if (this.task) {\n return Promise.reject(cameraError(\n 'BUSY',\n `The camera is already in use (${this.task.kind}). Only one camera task can run at a time.`,\n ));\n }\n return new Promise<T>((resolve, reject) => {\n this.task = { kind, opts, resolve, reject } as unknown as CameraTask;\n this.notify();\n });\n }\n\n /** Settle the running task successfully and clear it. */\n complete(value: any): void {\n const t = this.task;\n if (!t) return;\n this.task = null;\n this.notify();\n t.resolve(value);\n }\n\n /** Settle the running task with a failure and clear it. */\n fail(err: Error): void {\n const t = this.task;\n if (!t) return;\n this.task = null;\n this.notify();\n t.reject(err);\n }\n\n /**\n * The user backed out.\n *\n * A distinct code, because \"the user chose not to\" is not a malfunction and\n * a guide should say something different about it than about a broken lens.\n */\n cancel(): void {\n this.fail(cameraError('CANCELLED', 'Cancelled — the camera was dismissed before anything was captured.'));\n }\n\n private notify(): void {\n for (const fn of this.listeners) { try { fn(); } catch { /* a bad listener is not the broker's problem */ } }\n }\n}\n\n/** One broker per app: there is one camera. */\nexport const cameraTasks = new CameraTaskBroker();\n"],"mappings":";AAsCO,SAAS,YAAY,MAAc,SAAwB;AAChE,QAAM,MAAW,IAAI,MAAM,OAAO;AAClC,MAAI,OAAO;AACX,SAAO;AACT;AAEA,IAAM,mBAAN,MAAuB;AAAA,EAAvB;AACE,SAAQ,OAA0B;AAClC,SAAQ,YAAY,oBAAI,IAAc;AAEtC;AAAA,SAAQ,cAAc;AAAA;AAAA,EAEtB,UAA6B;AAAE,WAAO,KAAK;AAAA,EAAM;AAAA,EAEjD,UAAU,IAA0B;AAClC,SAAK,UAAU,IAAI,EAAE;AACrB,WAAO,MAAM,KAAK,UAAU,OAAO,EAAE;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,eAAe,SAAwB;AACrC,SAAK,cAAc;AACnB,QAAI,CAAC,QAAS,MAAK,KAAK,YAAY,eAAe,6DAA6D,CAAC;AACjH,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,gBAAyB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA;AAAA,EAGpD,IAAO,MAA0B,MAAuB;AACtD,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO,QAAQ,OAAO;AAAA,QACpB;AAAA,QACA;AAAA,MAEF,CAAC;AAAA,IACH;AACA,QAAI,KAAK,MAAM;AACb,aAAO,QAAQ,OAAO;AAAA,QACpB;AAAA,QACA,iCAAiC,KAAK,KAAK,IAAI;AAAA,MACjD,CAAC;AAAA,IACH;AACA,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,WAAK,OAAO,EAAE,MAAM,MAAM,SAAS,OAAO;AAC1C,WAAK,OAAO;AAAA,IACd,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,OAAkB;AACzB,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAG;AACR,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,MAAE,QAAQ,KAAK;AAAA,EACjB;AAAA;AAAA,EAGA,KAAK,KAAkB;AACrB,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAG;AACR,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,MAAE,OAAO,GAAG;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAe;AACb,SAAK,KAAK,YAAY,aAAa,yEAAoE,CAAC;AAAA,EAC1G;AAAA,EAEQ,SAAe;AACrB,eAAW,MAAM,KAAK,WAAW;AAAE,UAAI;AAAE,WAAG;AAAA,MAAG,QAAQ;AAAA,MAAmD;AAAA,IAAE;AAAA,EAC9G;AACF;AAGO,IAAM,cAAc,IAAI,iBAAiB;","names":[]}
@@ -0,0 +1,71 @@
1
+ import {
2
+ __esm,
3
+ __export
4
+ } from "./chunk-WMJKH4XE.mjs";
5
+
6
+ // src/device-bridge/cookies.ts
7
+ var cookies_exports = {};
8
+ __export(cookies_exports, {
9
+ clearAll: () => clearAll,
10
+ get: () => get,
11
+ set: () => set
12
+ });
13
+ import CookieManager from "@react-native-cookies/cookies";
14
+ async function get(url, includeHttpOnly = true) {
15
+ const raw = await CookieManager.get(url, includeHttpOnly);
16
+ const out = [];
17
+ for (const [name, c] of Object.entries(raw || {})) {
18
+ if (!c || typeof c !== "object") continue;
19
+ const value = typeof c.value === "string" ? c.value : String(c.value ?? "");
20
+ const cookie = { name, value };
21
+ if (c.domain) cookie.domain = String(c.domain);
22
+ if (c.path) cookie.path = String(c.path);
23
+ if (c.expires) {
24
+ const t = Date.parse(c.expires);
25
+ if (!Number.isNaN(t)) cookie.expires = Math.floor(t / 1e3);
26
+ }
27
+ if (typeof c.httpOnly === "boolean") cookie.httpOnly = c.httpOnly;
28
+ if (typeof c.secure === "boolean") cookie.secure = c.secure;
29
+ if (c.sameSite === "Lax" || c.sameSite === "Strict" || c.sameSite === "None") {
30
+ cookie.sameSite = c.sameSite;
31
+ }
32
+ out.push(cookie);
33
+ }
34
+ return out;
35
+ }
36
+ async function clearAll() {
37
+ try {
38
+ await CookieManager.clearAll(true);
39
+ return { ok: true };
40
+ } catch (e) {
41
+ return { ok: false, error: String(e?.message || e) };
42
+ }
43
+ }
44
+ async function set(url, cookie) {
45
+ try {
46
+ await CookieManager.set(url, {
47
+ name: cookie.name,
48
+ value: cookie.value,
49
+ domain: cookie.domain,
50
+ path: cookie.path || "/",
51
+ httpOnly: cookie.httpOnly,
52
+ secure: cookie.secure
53
+ }, true);
54
+ return { ok: true };
55
+ } catch (e) {
56
+ return { ok: false, error: String(e?.message || e) };
57
+ }
58
+ }
59
+ var init_cookies = __esm({
60
+ "src/device-bridge/cookies.ts"() {
61
+ }
62
+ });
63
+
64
+ export {
65
+ get,
66
+ clearAll,
67
+ set,
68
+ cookies_exports,
69
+ init_cookies
70
+ };
71
+ //# sourceMappingURL=chunk-FF6ZZZXH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/device-bridge/cookies.ts"],"sourcesContent":["/**\n * Device-bridge: Cookies API\n *\n * Reads the platform's shared cookie jar (NSHTTPCookieStorage on iOS,\n * CookieManager on Android) so upper layers (plugin-sdk → RN plugins) can\n * extract session cookies captured during a native WebView login flow.\n *\n * Wraps `@react-native-cookies/cookies` so upper layers never import the\n * native module directly. If we swap implementations, only this file changes.\n *\n * Why device-bridge owns this: cookies are a device-level capability (tied\n * to the platform's shared HTTP stack). Plugins should consume a clean\n * abstraction; the native wiring lives here.\n */\n\nimport CookieManager from '@react-native-cookies/cookies';\n\nexport interface DeviceCookie {\n name: string;\n value: string;\n domain?: string;\n path?: string;\n /** Unix seconds. Omitted for session cookies. */\n expires?: number;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: 'Lax' | 'Strict' | 'None';\n}\n\n/**\n * Get every cookie attached to the URL's effective domain.\n * Set `includeHttpOnly` true to include HttpOnly cookies (required for\n * LinkedIn/Google session tokens).\n */\nexport async function get(url: string, includeHttpOnly: boolean = true): Promise<DeviceCookie[]> {\n const raw = (await CookieManager.get(url, includeHttpOnly)) as Record<string, any>;\n const out: DeviceCookie[] = [];\n for (const [name, c] of Object.entries(raw || {})) {\n if (!c || typeof c !== 'object') continue;\n const value = typeof c.value === 'string' ? c.value : String(c.value ?? '');\n const cookie: DeviceCookie = { name, value };\n if (c.domain) cookie.domain = String(c.domain);\n if (c.path) cookie.path = String(c.path);\n if (c.expires) {\n const t = Date.parse(c.expires);\n if (!Number.isNaN(t)) cookie.expires = Math.floor(t / 1000);\n }\n if (typeof c.httpOnly === 'boolean') cookie.httpOnly = c.httpOnly;\n if (typeof c.secure === 'boolean') cookie.secure = c.secure;\n if (c.sameSite === 'Lax' || c.sameSite === 'Strict' || c.sameSite === 'None') {\n cookie.sameSite = c.sameSite;\n }\n out.push(cookie);\n }\n return out;\n}\n\n/**\n * Wipe the shared cookie jar. Cross-platform scoped clear isn't supported\n * uniformly; `clearAll` is the safe operation on both iOS and Android.\n */\nexport async function clearAll(): Promise<{ ok: boolean; error?: string }> {\n try {\n await CookieManager.clearAll(true);\n return { ok: true };\n } catch (e: any) {\n return { ok: false, error: String(e?.message || e) };\n }\n}\n\n/**\n * Set a cookie programmatically. Useful when replaying a saved session on\n * the device's shared jar before opening a WebView.\n */\nexport async function set(url: string, cookie: DeviceCookie): Promise<{ ok: boolean; error?: string }> {\n try {\n await CookieManager.set(url, {\n name: cookie.name,\n value: cookie.value,\n domain: cookie.domain,\n path: cookie.path || '/',\n httpOnly: cookie.httpOnly,\n secure: cookie.secure,\n } as any, true);\n return { ok: true };\n } catch (e: any) {\n return { ok: false, error: String(e?.message || e) };\n }\n}\n"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,OAAO,mBAAmB;AAmB1B,eAAsB,IAAI,KAAa,kBAA2B,MAA+B;AAC/F,QAAM,MAAO,MAAM,cAAc,IAAI,KAAK,eAAe;AACzD,QAAM,MAAsB,CAAC;AAC7B,aAAW,CAAC,MAAM,CAAC,KAAK,OAAO,QAAQ,OAAO,CAAC,CAAC,GAAG;AACjD,QAAI,CAAC,KAAK,OAAO,MAAM,SAAU;AACjC,UAAM,QAAQ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ,OAAO,EAAE,SAAS,EAAE;AAC1E,UAAM,SAAuB,EAAE,MAAM,MAAM;AAC3C,QAAI,EAAE,OAAQ,QAAO,SAAS,OAAO,EAAE,MAAM;AAC7C,QAAI,EAAE,KAAM,QAAO,OAAO,OAAO,EAAE,IAAI;AACvC,QAAI,EAAE,SAAS;AACb,YAAM,IAAI,KAAK,MAAM,EAAE,OAAO;AAC9B,UAAI,CAAC,OAAO,MAAM,CAAC,EAAG,QAAO,UAAU,KAAK,MAAM,IAAI,GAAI;AAAA,IAC5D;AACA,QAAI,OAAO,EAAE,aAAa,UAAW,QAAO,WAAW,EAAE;AACzD,QAAI,OAAO,EAAE,WAAW,UAAW,QAAO,SAAS,EAAE;AACrD,QAAI,EAAE,aAAa,SAAS,EAAE,aAAa,YAAY,EAAE,aAAa,QAAQ;AAC5E,aAAO,WAAW,EAAE;AAAA,IACtB;AACA,QAAI,KAAK,MAAM;AAAA,EACjB;AACA,SAAO;AACT;AAMA,eAAsB,WAAqD;AACzE,MAAI;AACF,UAAM,cAAc,SAAS,IAAI;AACjC,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,GAAQ;AACf,WAAO,EAAE,IAAI,OAAO,OAAO,OAAO,GAAG,WAAW,CAAC,EAAE;AAAA,EACrD;AACF;AAMA,eAAsB,IAAI,KAAa,QAAgE;AACrG,MAAI;AACF,UAAM,cAAc,IAAI,KAAK;AAAA,MAC3B,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,MAAM,OAAO,QAAQ;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO;AAAA,IACjB,GAAU,IAAI;AACd,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,GAAQ;AACf,WAAO,EAAE,IAAI,OAAO,OAAO,OAAO,GAAG,WAAW,CAAC,EAAE;AAAA,EACrD;AACF;AAxFA;AAAA;AAAA;AAAA;","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-TZXWVYYR.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. */\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. */\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;AA4DH,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":[]}