@mentra/engine 3.1.0-dev.4 → 3.1.0-dev.40

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 (103) hide show
  1. package/README.md +19 -5
  2. package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
  3. package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/ota-transport.js +2 -0
  5. package/build/bluetooth-sdk/ota-transport.js.map +1 -0
  6. package/build/engine.d.ts +5 -2
  7. package/build/engine.d.ts.map +1 -1
  8. package/build/facades/ota.d.ts +9 -6
  9. package/build/facades/ota.d.ts.map +1 -1
  10. package/build/facades/ota.js +6 -4
  11. package/build/facades/ota.js.map +1 -1
  12. package/build/facades/pairing.d.ts +4 -0
  13. package/build/facades/pairing.d.ts.map +1 -1
  14. package/build/facades/pairing.js +81 -7
  15. package/build/facades/pairing.js.map +1 -1
  16. package/build/generated/releaseMetadata.js +5 -5
  17. package/build/generated/releaseMetadata.js.map +1 -1
  18. package/build/index.d.ts +1 -1
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js.map +1 -1
  21. package/build/react/MentraLiveOtaFlow.d.ts +3 -2
  22. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  23. package/build/react/MentraLiveOtaFlow.js +113 -352
  24. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  25. package/build/react/index.d.ts +1 -0
  26. package/build/react/index.d.ts.map +1 -1
  27. package/build/react/index.js +1 -0
  28. package/build/react/index.js.map +1 -1
  29. package/build/react/useMentraLiveOta.d.ts +80 -0
  30. package/build/react/useMentraLiveOta.d.ts.map +1 -0
  31. package/build/react/useMentraLiveOta.js +550 -0
  32. package/build/react/useMentraLiveOta.js.map +1 -0
  33. package/build/services/BlobStore.d.ts +2 -0
  34. package/build/services/BlobStore.d.ts.map +1 -1
  35. package/build/services/BlobStore.js +63 -10
  36. package/build/services/BlobStore.js.map +1 -1
  37. package/build/services/GlassesStatusProjection.d.ts +15 -2
  38. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  39. package/build/services/GlassesStatusProjection.js +3 -3
  40. package/build/services/GlassesStatusProjection.js.map +1 -1
  41. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  42. package/build/services/HotspotOtaTransport.js +8 -7
  43. package/build/services/HotspotOtaTransport.js.map +1 -1
  44. package/build/services/OtaAutoChain.d.ts +9 -2
  45. package/build/services/OtaAutoChain.d.ts.map +1 -1
  46. package/build/services/OtaAutoChain.js +10 -2
  47. package/build/services/OtaAutoChain.js.map +1 -1
  48. package/build/services/OtaInstallCoordinator.d.ts +24 -3
  49. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  50. package/build/services/OtaInstallCoordinator.js +14 -12
  51. package/build/services/OtaInstallCoordinator.js.map +1 -1
  52. package/build/services/OtaService.d.ts.map +1 -1
  53. package/build/services/OtaService.js +1 -1
  54. package/build/services/OtaService.js.map +1 -1
  55. package/build/services/OtaUpdateCheckService.d.ts +4 -1
  56. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  57. package/build/services/OtaUpdateCheckService.js +21 -5
  58. package/build/services/OtaUpdateCheckService.js.map +1 -1
  59. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  60. package/build/services/asg/localNetworkTransport.js +13 -12
  61. package/build/services/asg/localNetworkTransport.js.map +1 -1
  62. package/build/services/glassesClockSync.js +1 -1
  63. package/build/services/glassesClockSync.js.map +1 -1
  64. package/build/services/otaReleaseVersion.d.ts +10 -0
  65. package/build/services/otaReleaseVersion.d.ts.map +1 -0
  66. package/build/services/otaReleaseVersion.js +14 -0
  67. package/build/services/otaReleaseVersion.js.map +1 -0
  68. package/build/stores/appVisibility.d.ts +3 -0
  69. package/build/stores/appVisibility.d.ts.map +1 -0
  70. package/build/stores/appVisibility.js +3 -0
  71. package/build/stores/appVisibility.js.map +1 -0
  72. package/build/stores/apps.d.ts.map +1 -1
  73. package/build/stores/apps.js +7 -1
  74. package/build/stores/apps.js.map +1 -1
  75. package/build/stores/core.d.ts +3 -3
  76. package/build/stores/core.d.ts.map +1 -1
  77. package/build/stores/core.js.map +1 -1
  78. package/build/stores/glasses.d.ts +10 -4
  79. package/build/stores/glasses.d.ts.map +1 -1
  80. package/build/stores/glasses.js.map +1 -1
  81. package/package.json +12 -6
  82. package/src/bluetooth-sdk/ota-transport.ts +1 -0
  83. package/src/facades/ota.ts +16 -5
  84. package/src/facades/pairing.ts +88 -12
  85. package/src/generated/releaseMetadata.ts +5 -5
  86. package/src/index.ts +1 -0
  87. package/src/react/MentraLiveOtaFlow.tsx +178 -465
  88. package/src/react/index.ts +14 -0
  89. package/src/react/useMentraLiveOta.ts +707 -0
  90. package/src/services/BlobStore.ts +57 -10
  91. package/src/services/GlassesStatusProjection.ts +7 -6
  92. package/src/services/HotspotOtaTransport.ts +8 -7
  93. package/src/services/OtaAutoChain.ts +26 -2
  94. package/src/services/OtaInstallCoordinator.ts +16 -13
  95. package/src/services/OtaService.ts +1 -2
  96. package/src/services/OtaUpdateCheckService.ts +26 -8
  97. package/src/services/asg/localNetworkTransport.ts +20 -19
  98. package/src/services/glassesClockSync.ts +1 -1
  99. package/src/services/otaReleaseVersion.ts +22 -0
  100. package/src/stores/appVisibility.ts +3 -0
  101. package/src/stores/apps.ts +7 -1
  102. package/src/stores/core.ts +4 -4
  103. package/src/stores/glasses.ts +13 -6
@@ -1 +1 @@
1
- {"version":3,"file":"BlobStore.d.ts","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,OAAO,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAA;AAMhD,gCAAgC;AAChC,eAAO,MAAM,gBAAgB,QAAqB,CAAA;AASlD,KAAK,UAAU,GAAG,CAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,EAAE,EAAE,OAAO,EACX,MAAM,CAAC,EAAE,OAAO,EAChB,KAAK,CAAC,EAAE;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,KAC9C,IAAI,CAAA;AAET,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,oEAAoE;IACpE,SAAS,EAAE,MAAM,MAAM,CAAA;CACxB;AAmCD,qBAAa,SAAS;IAMR,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4B;gBAE1B,KAAK,EAAE,cAAc;IAIlD,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,OAAO;IAIf,2DAA2D;IAC3D,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,MAAM;IAMd,2EAA2E;IAC3E,OAAO,CAAC,SAAS;IAKjB,mGAAmG;IACnG,OAAO,CAAC,UAAU;IA8ClB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IA+B7F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IA8C5F,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IA4B7F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAYtF,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuD1G,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+C5G,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAW1F,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ5F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAS7F,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAM7F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAmB7F,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAqB/F,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAgC3F,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAgB1F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuD3G,uFAAuF;IACvF,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIpC,oFAAoF;IACpF,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,IAAI;CAOb"}
1
+ {"version":3,"file":"BlobStore.d.ts","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"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"}
@@ -36,8 +36,15 @@ const LOG_TAG = "LocalMiniappRuntime";
36
36
  /** Per-app blob quota: 1 GB. */
37
37
  export const BLOB_QUOTA_BYTES = 1024 * 1024 * 1024;
38
38
  const ROOT_DIR_NAME = "mentra_blobs";
39
- /** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one unique subdir per share). */
39
+ /** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one subdir per stored blob). */
40
40
  const SHARE_DIR_NAME = "mentra_blob_share";
41
+ /**
42
+ * Keep prepared share handoffs alive long enough for recipient apps to read
43
+ * them asynchronously (for example, an email composer attaching the file).
44
+ * Stale copies are collected before the next share; the OS may evict cache
45
+ * files sooner under storage pressure.
46
+ */
47
+ const SHARE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
41
48
  const META_KEY_ROOT = "mentraos_blobmeta_";
42
49
  /** Cap md5 computation so we don't block the JS thread hashing a huge file. */
43
50
  const MD5_MAX_BYTES = 50 * 1024 * 1024;
@@ -511,6 +518,36 @@ export class BlobStore {
511
518
  this.hooks.sendResult(packageName, requestId, true);
512
519
  }
513
520
  // ---- share ---------------------------------------------------------------
521
+ /** Best-effort GC for prepared share handoffs retained in the OS cache. */
522
+ cleanupShareCache(now = Date.now()) {
523
+ try {
524
+ const root = new Directory(Paths.cache, SHARE_DIR_NAME);
525
+ if (!root.exists)
526
+ return;
527
+ const cutoff = now - SHARE_CACHE_MAX_AGE_MS;
528
+ for (const entry of root.list()) {
529
+ if (!(entry instanceof Directory))
530
+ continue;
531
+ try {
532
+ const info = entry.info();
533
+ // modificationTime is available on both platforms; creationTime can
534
+ // be absent on Android versions before API 26. The directory id
535
+ // begins with Date.now().toString(36), which is a final fallback.
536
+ const encoded = entry.name.split("-", 1)[0];
537
+ const encodedTime = /^[0-9a-z]+$/.test(encoded) ? Number.parseInt(encoded, 36) : Number.NaN;
538
+ const timestamp = info.modificationTime ?? info.creationTime ?? encodedTime;
539
+ if (Number.isFinite(timestamp) && timestamp < cutoff)
540
+ entry.delete();
541
+ }
542
+ catch {
543
+ // Keep scanning if one cache entry disappears or is unreadable.
544
+ }
545
+ }
546
+ }
547
+ catch {
548
+ // Cache cleanup must never prevent a share.
549
+ }
550
+ }
514
551
  async handleShare(packageName, payload, requestId) {
515
552
  const key = payload.key;
516
553
  const meta = this.readMeta(packageName, key);
@@ -528,23 +565,35 @@ export class BlobStore {
528
565
  // applies to base64/data payloads, not file:// URLs), so sharing `file.uri`
529
566
  // directly would hand the recipient a machine-named file. Copy to a temp
530
567
  // file that carries the real display name + extension, share that, then
531
- // clean it up.
568
+ // clean it up after the recipient has had time to consume it.
532
569
  const shareName = shareFileName(meta);
533
- // Copy into a per-share unique subdir so two concurrent shares that resolve
534
- // to the same display name can't clobber each other's temp file while
535
- // Share.open still references it. The file keeps `shareName` as its basename
536
- // so the OS share sheet shows the right name + extension.
570
+ // Key the cache directory by the immutable stored filename. Repeated or
571
+ // concurrent shares of the same blob can safely reference the same bytes,
572
+ // avoiding an extra full-sized cache copy for every share. Different blobs
573
+ // still get isolated directories even when they use the same display name.
574
+ // The file keeps `shareName` as its basename so the OS share sheet shows the
575
+ // right name + extension.
537
576
  let tempDir = null;
577
+ let cacheReady = false;
538
578
  try {
539
- tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, this.makeId());
540
- tempDir.create({ intermediates: true });
579
+ this.cleanupShareCache();
580
+ tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, sanitizeSegment(meta.fileName));
581
+ if (!tempDir.exists)
582
+ tempDir.create({ intermediates: true });
541
583
  const temp = new File(tempDir, shareName);
542
- file.copy(temp);
584
+ if (!temp.exists || temp.size !== meta.bytes) {
585
+ if (temp.exists)
586
+ temp.delete();
587
+ file.copy(temp);
588
+ }
589
+ cacheReady = true;
543
590
  await Share.open({
544
591
  url: temp.uri,
545
592
  type: meta.mimeType || OCTET,
546
593
  filename: shareName,
547
594
  });
595
+ // Share.open resolves once the OS accepts the handoff. Recipient apps may
596
+ // still read the URI asynchronously, so retain this cache copy for GC.
548
597
  this.hooks.sendResult(packageName, requestId, true, { success: true });
549
598
  }
550
599
  catch (error) {
@@ -557,7 +606,11 @@ export class BlobStore {
557
606
  }
558
607
  }
559
608
  finally {
560
- if (tempDir) {
609
+ // Once a reusable cache entry is ready, retain it even if this particular
610
+ // sheet is cancelled or fails: another concurrent/recent share may still
611
+ // be reading the same file. Only remove an entry whose preparation did
612
+ // not finish; ready entries are collected after the retention window.
613
+ if (tempDir && !cacheReady) {
561
614
  try {
562
615
  if (tempDir.exists)
563
616
  tempDir.delete();
@@ -1 +1 @@
1
- {"version":3,"file":"BlobStore.js","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * BlobStore\n *\n * Host-side implementation of `session.blob` — persistent, per-(userId,\n * packageName) BINARY storage for local miniapps, keyed like SimpleStorage (its\n * small-string sibling). Extracted from LocalMiniappRuntime the same way\n * NavigationHandlers is, to keep the runtime file small.\n *\n * Generic byte store — it knows nothing about any specific format. Highlights:\n * - chunked write/read (BLOB_CREATE/WRITE/COMMIT, BLOB_OPEN_READ/READ) so no\n * single bridge message is large;\n * - `setFromUrl` downloads a URL straight to disk (File.downloadFileAsync) and\n * `importFile` runs the OS picker (File.pickFileAsync) — both keep bytes\n * entirely host-side;\n * - `share` hands a stored file to the OS share sheet.\n *\n * Layout (mirrors SimpleStorage's scoping):\n * bytes: Paths.document/mentra_blobs/{userId}/{packageName}/{fileName}\n * metadata: MMKV key mentraos_blobmeta_{userId}_{packageName}_{key} → StoredBlobMeta JSON\n *\n * The on-disk file name is a generated id plus a mime-derived extension (iOS\n * AVPlayer keys a local file's format off the path extension, so blobs played\n * via `session.speaker.play` must carry one); the logical `key` (caller-chosen)\n * is the MMKV key. This decouples the filesystem from arbitrary caller keys.\n *\n * expo-file-system's new API is synchronous (create/writeBytes/md5/size), so\n * streaming chunk writes do no async work.\n */\n\nimport {Buffer} from \"buffer\"\nimport Share from \"react-native-share\"\nimport {Directory, File, Paths, type FileHandle} from \"expo-file-system\"\n\nimport {MiniappErrorCode} from \"@mentra/miniapp\"\nimport {storage as mmkvStorage} from \"../utils/storage/storage\"\nimport {sanitizeSegment, shareFileName, withDiskExt} from \"./blobPaths\"\n\nconst LOG_TAG = \"LocalMiniappRuntime\"\n\n/** Per-app blob quota: 1 GB. */\nexport const BLOB_QUOTA_BYTES = 1024 * 1024 * 1024\nconst ROOT_DIR_NAME = \"mentra_blobs\"\n/** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one unique subdir per share). */\nconst SHARE_DIR_NAME = \"mentra_blob_share\"\nconst META_KEY_ROOT = \"mentraos_blobmeta_\"\n/** Cap md5 computation so we don't block the JS thread hashing a huge file. */\nconst MD5_MAX_BYTES = 50 * 1024 * 1024\n\n// Matches LocalMiniappRuntime.sendResult (see NavigationHandlers).\ntype SendResult = (\n packageName: string,\n requestId: string | undefined,\n ok: boolean,\n result?: unknown,\n error?: {code: MiniappErrorCode; message: string},\n) => void\n\nexport interface BlobStoreHooks {\n sendResult: SendResult\n /** Resolve the current user id (same source SimpleStorage uses). */\n getUserId: () => string\n}\n\ninterface StoredBlobMeta {\n key: string\n fileName: string\n name?: string\n mimeType: string\n bytes: number\n createdAt: number\n updatedAt: number\n md5?: string\n meta?: Record<string, string | number | boolean>\n}\n\ninterface ActiveUpload {\n packageName: string\n key: string\n fileName: string\n partFile: File\n handle: FileHandle\n bytes: number\n /** Bytes already committed under this key (overwrite target) — excluded from the quota check. */\n priorBytes: number\n name?: string\n mimeType: string\n meta?: Record<string, string | number | boolean>\n createdAt: number\n}\n\ninterface ActiveReader {\n packageName: string\n handle: FileHandle\n remaining: number\n}\n\nexport class BlobStore {\n private readonly uploads = new Map<string, ActiveUpload>() // key: `${pkg} ${key}`\n private readonly readers = new Map<string, ActiveReader>() // key: host handle id\n /** user + package → committed usage bytes, cached; invalidated on commit/delete/clear. */\n private readonly usageCache = new Map<string, number>()\n\n constructor(private readonly hooks: BlobStoreHooks) {}\n\n // ---- path / meta helpers -------------------------------------------------\n\n private currentIdentity(packageName: string): {userId: string; scope: string} {\n const userId = this.hooks.getUserId().trim()\n if (!userId) throw new Error(\"Mentra user identity is unavailable\")\n const scope = `${userId}\\0${packageName}`\n return {userId, scope}\n }\n\n private dirFor(packageName: string): Directory {\n const dir = new Directory(\n Paths.document,\n ROOT_DIR_NAME,\n sanitizeSegment(this.currentIdentity(packageName).userId),\n sanitizeSegment(packageName),\n )\n if (!dir.exists) dir.create({intermediates: true})\n return dir\n }\n\n private metaPrefix(packageName: string): string {\n return `${META_KEY_ROOT}${sanitizeSegment(this.currentIdentity(packageName).userId)}_${packageName}_`\n }\n\n private metaKey(packageName: string, key: string): string {\n return this.metaPrefix(packageName) + key\n }\n\n private uploadKey(packageName: string, key: string): string {\n return `${packageName} ${key}`\n }\n\n private readMeta(packageName: string, key: string): StoredBlobMeta | null {\n const r = mmkvStorage.load<StoredBlobMeta>(this.metaKey(packageName, key))\n return r.is_ok() ? r.value : null\n }\n\n private writeMeta(packageName: string, meta: StoredBlobMeta): void {\n mmkvStorage.save(this.metaKey(packageName, meta.key), meta)\n }\n\n private allMeta(packageName: string): StoredBlobMeta[] {\n const prefix = this.metaPrefix(packageName)\n const out: StoredBlobMeta[] = []\n for (const k of mmkvStorage.getAllKeys()) {\n if (!k.startsWith(prefix)) continue\n const r = mmkvStorage.load<StoredBlobMeta>(k)\n if (r.is_ok() && r.value) out.push(r.value)\n }\n return out\n }\n\n private fileFor(packageName: string, fileName: string): File {\n return new File(this.dirFor(packageName), fileName)\n }\n\n /** Public API shape: stored meta + derived file:// uri. */\n private toBlobMeta(packageName: string, m: StoredBlobMeta) {\n return {\n key: m.key,\n name: m.name,\n mimeType: m.mimeType,\n bytes: m.bytes,\n createdAt: m.createdAt,\n updatedAt: m.updatedAt,\n md5: m.md5,\n uri: this.fileFor(packageName, m.fileName).uri,\n meta: m.meta,\n }\n }\n\n private committedUsage(packageName: string): number {\n const {scope} = this.currentIdentity(packageName)\n const cached = this.usageCache.get(scope)\n if (cached !== undefined) return cached\n let total = 0\n for (const m of this.allMeta(packageName)) total += m.bytes || 0\n this.usageCache.set(scope, total)\n return total\n }\n\n private bumpUsage(packageName: string, delta: number): void {\n const {scope} = this.currentIdentity(packageName)\n const cur = this.usageCache.get(scope)\n if (cur !== undefined) this.usageCache.set(scope, Math.max(0, cur + delta))\n }\n\n private safeMd5(file: File, bytes: number): string | undefined {\n if (bytes > MD5_MAX_BYTES) return undefined\n try {\n return file.md5 ?? undefined\n } catch {\n return undefined\n }\n }\n\n private makeId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}${Math.random()\n .toString(36)\n .slice(2, 6)}`\n }\n\n /** Would storing `incoming` bytes under `key` exceed the per-app quota? */\n private overQuota(packageName: string, key: string, incoming: number): boolean {\n const priorBytes = this.readMeta(packageName, key)?.bytes ?? 0\n return this.committedUsage(packageName) - priorBytes + incoming > BLOB_QUOTA_BYTES\n }\n\n /** Publish a file already sitting at `fileName` as the blob for `key`; returns its public meta. */\n private commitFile(\n packageName: string,\n key: string,\n fileName: string,\n mimeType: string,\n name: string | undefined,\n extraMeta: Record<string, string | number | boolean> | undefined,\n ) {\n // Attach a mime-derived extension to the on-disk name. Consumers that key\n // format off the path — iOS AVPlayer under `session.speaker.play` — cannot\n // open an extension-less file, even though the bytes are valid.\n const named = withDiskExt(fileName, mimeType)\n if (named !== fileName) {\n try {\n this.fileFor(packageName, fileName).rename(named)\n fileName = named\n } catch {\n /* keep the extension-less name — the blob still stores and shares fine */\n }\n }\n const prior = this.readMeta(packageName, key)\n if (prior && prior.fileName !== fileName) {\n this.deleteFileQuiet(packageName, prior.fileName)\n this.bumpUsage(packageName, -(prior.bytes || 0))\n }\n const finalFile = this.fileFor(packageName, fileName)\n const bytes = finalFile.size ?? 0\n const now = Date.now()\n const meta: StoredBlobMeta = {\n key,\n fileName,\n name,\n mimeType,\n bytes,\n createdAt: prior?.createdAt ?? now,\n updatedAt: now,\n md5: this.safeMd5(finalFile, bytes),\n meta: extraMeta,\n }\n this.writeMeta(packageName, meta)\n this.bumpUsage(packageName, bytes)\n return this.toBlobMeta(packageName, meta)\n }\n\n // ---- streaming write -----------------------------------------------------\n\n handleCreate(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n try {\n const key = (typeof payload.key === \"string\" && payload.key) || this.makeId()\n const mapKey = this.uploadKey(packageName, key)\n const existing = this.uploads.get(mapKey)\n if (existing) this.discardUpload(existing)\n\n const fileName = this.makeId()\n const partFile = this.fileFor(packageName, fileName + \".part\")\n partFile.create({intermediates: true, overwrite: true})\n const handle = partFile.open()\n\n this.uploads.set(mapKey, {\n packageName,\n key,\n fileName,\n partFile,\n handle,\n bytes: 0,\n priorBytes: this.readMeta(packageName, key)?.bytes ?? 0,\n name: typeof payload.name === \"string\" ? payload.name : undefined,\n mimeType: typeof payload.mimeType === \"string\" ? payload.mimeType : \"application/octet-stream\",\n meta: (payload.meta as Record<string, string | number | boolean>) ?? undefined,\n createdAt: Date.now(),\n })\n this.hooks.sendResult(packageName, requestId, true, {key})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleWrite(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const up = this.uploads.get(this.uploadKey(packageName, key))\n if (!up) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: `No open blob writer for key ${key}`,\n })\n return\n }\n try {\n const chunk = Buffer.from((payload.base64 as string) ?? \"\", \"base64\")\n const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)\n // A seek write (offset present) patches existing bytes in place — used to\n // rewrite a container header on finalize; it does not grow the blob.\n if (typeof payload.offset === \"number\") {\n if (payload.offset < 0 || payload.offset + bytes.length > up.bytes) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_WRITE_FAILED,\n message: \"writeAt offset out of range (must stay within written bytes)\",\n })\n return\n }\n up.handle.offset = payload.offset\n up.handle.writeBytes(bytes)\n up.handle.offset = up.bytes // restore append position\n this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})\n return\n }\n // Exclude the prior blob under this key — it's freed on commit (overwrite).\n if (this.committedUsage(packageName) - up.priorBytes + up.bytes + bytes.length > BLOB_QUOTA_BYTES) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n up.handle.offset = up.bytes\n up.handle.writeBytes(bytes)\n up.bytes += bytes.length\n this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleCommit(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const mapKey = this.uploadKey(packageName, key)\n const up = this.uploads.get(mapKey)\n if (!up) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: `No open blob writer for key ${key}`,\n })\n return\n }\n try {\n up.handle.close()\n up.partFile.rename(up.fileName) // drop \".part\"\n const extraMeta = {\n ...(up.meta ?? {}),\n ...((payload.meta as Record<string, string | number | boolean>) ?? {}),\n }\n const meta = this.commitFile(packageName, key, up.fileName, up.mimeType, up.name, extraMeta)\n this.uploads.delete(mapKey)\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n this.discardUpload(up)\n this.uploads.delete(mapKey)\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleAbort(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const mapKey = this.uploadKey(packageName, payload.key as string)\n const up = this.uploads.get(mapKey)\n if (up) {\n this.discardUpload(up)\n this.uploads.delete(mapKey)\n }\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n // ---- download / import (host-side; no bytes cross the bridge) -------------\n\n async handleSetFromUrl(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = payload.key as string\n const url = payload.url as string\n if (!key || !url) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, new Error(\"setFromUrl requires key + url\"))\n return\n }\n if (url.startsWith(\"file:\") || url.startsWith(\"javascript:\")) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_DOWNLOAD_FAILED,\n message: \"Unsupported URL scheme\",\n })\n return\n }\n const headers = payload.headers as Record<string, string> | undefined\n const fileName = this.makeId()\n const partFile = this.fileFor(packageName, fileName + \".part\")\n try {\n const dl = await File.downloadFileAsync(url, partFile, {idempotent: true, ...(headers ? {headers} : {})})\n const size = dl.size ?? 0\n if (this.overQuota(packageName, key, size)) {\n try {\n dl.delete()\n } catch {\n /* ignore */\n }\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n partFile.rename(fileName)\n const mimeType =\n (payload.mimeType as string) || inferMime(basename(url)) || this.fileFor(packageName, fileName).type || OCTET\n const name = (payload.name as string) || basename(url) || key\n const meta = this.commitFile(\n packageName,\n key,\n fileName,\n mimeType,\n name,\n payload.meta as Record<string, string | number | boolean> | undefined,\n )\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n try {\n if (partFile.exists) partFile.delete()\n } catch {\n /* ignore */\n }\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_DOWNLOAD_FAILED, err)\n }\n }\n\n async handleImport(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = (typeof payload.key === \"string\" && payload.key) || this.makeId()\n const mimeFilter = payload.mimeType as string | undefined\n\n let picked: File | File[] | null = null\n try {\n picked = await File.pickFileAsync(undefined, mimeFilter)\n } catch (err) {\n // Picker dismissed / cancelled (or unavailable) → resolve to null, not an error.\n console.log(`${LOG_TAG}: blob import picker dismissed`, err)\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n const file = Array.isArray(picked) ? picked[0] : picked\n if (!file) {\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n try {\n const size = file.size ?? 0\n if (this.overQuota(packageName, key, size)) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n const fileName = this.makeId()\n file.copy(this.fileFor(packageName, fileName))\n const name = basename(file.uri) || key\n const mimeType = mimeFilter || file.type || inferMime(name) || OCTET\n const meta = this.commitFile(\n packageName,\n key,\n fileName,\n mimeType,\n name,\n payload.meta as Record<string, string | number | boolean> | undefined,\n )\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_IMPORT_FAILED, err)\n }\n }\n\n // ---- read ----------------------------------------------------------------\n\n handleGet(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n if (!meta || !this.fileFor(packageName, meta.fileName).exists) {\n if (meta) this.dropMeta(packageName, meta) // reconcile dangling meta\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n this.hooks.sendResult(packageName, requestId, true, this.toBlobMeta(packageName, meta))\n }\n\n handleList(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n const blobs = this.allMeta(packageName)\n .filter((m) => this.fileFor(packageName, m.fileName).exists)\n .sort((a, b) => b.createdAt - a.createdAt)\n .map((m) => this.toBlobMeta(packageName, m))\n this.hooks.sendResult(packageName, requestId, true, {blobs})\n }\n\n handleUsage(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n const metas = this.allMeta(packageName)\n this.hooks.sendResult(packageName, requestId, true, {\n bytes: metas.reduce((s, m) => s + (m.bytes || 0), 0),\n count: metas.length,\n quotaBytes: BLOB_QUOTA_BYTES,\n })\n }\n\n handleDelete(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const meta = this.readMeta(packageName, payload.key as string)\n if (meta) this.dropMeta(packageName, meta)\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n handleClear(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n try {\n // Abort any in-flight uploads / open readers first so nothing keeps writing\n // to a .part file in the directory we're about to delete.\n this.closeAppHandles(packageName)\n for (const m of this.allMeta(packageName)) mmkvStorage.remove(this.metaKey(packageName, m.key))\n const dir = this.dirFor(packageName)\n try {\n if (dir.exists) dir.delete()\n } catch {\n /* best-effort */\n }\n this.usageCache.set(this.currentIdentity(packageName).scope, 0)\n this.hooks.sendResult(packageName, requestId, true)\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleOpenRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n const file = meta ? this.fileFor(packageName, meta.fileName) : null\n if (!meta || !file || !file.exists) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_NOT_FOUND,\n message: `Blob ${key} not found`,\n })\n return\n }\n try {\n const handle = file.open()\n const handleId = `r_${this.makeId()}`\n this.readers.set(handleId, {packageName, handle, remaining: meta.bytes})\n this.hooks.sendResult(packageName, requestId, true, {handle: handleId, meta: this.toBlobMeta(packageName, meta)})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const handleId = payload.handle as string\n const reader = this.readers.get(handleId)\n if (!reader || reader.packageName !== packageName) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: \"Unknown blob read handle\",\n })\n return\n }\n try {\n const want = Math.max(0, Math.min(Number(payload.maxBytes) || 1024 * 1024, reader.remaining))\n const bytes = want > 0 ? reader.handle.readBytes(want) : new Uint8Array(0)\n reader.remaining -= bytes.length\n const done = reader.remaining <= 0\n const base64 = bytes.length\n ? Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(\"base64\")\n : \"\"\n if (done) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(handleId)\n }\n this.hooks.sendResult(packageName, requestId, true, {base64, done})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleCloseRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const handleId = payload.handle as string\n const reader = this.readers.get(handleId)\n if (reader && reader.packageName === packageName) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(handleId)\n }\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n // ---- share ---------------------------------------------------------------\n\n async handleShare(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n const file = meta ? this.fileFor(packageName, meta.fileName) : null\n if (!meta || !file || !file.exists) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_NOT_FOUND,\n message: `Blob ${key} not found`,\n })\n return\n }\n // The on-disk blob name is a generated id (mime extension attached at\n // commit; legacy blobs may lack it). react-native-share derives the shared\n // file's name + extension from the URL's path (its `filename` option only\n // applies to base64/data payloads, not file:// URLs), so sharing `file.uri`\n // directly would hand the recipient a machine-named file. Copy to a temp\n // file that carries the real display name + extension, share that, then\n // clean it up.\n const shareName = shareFileName(meta)\n // Copy into a per-share unique subdir so two concurrent shares that resolve\n // to the same display name can't clobber each other's temp file while\n // Share.open still references it. The file keeps `shareName` as its basename\n // so the OS share sheet shows the right name + extension.\n let tempDir: Directory | null = null\n try {\n tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, this.makeId())\n tempDir.create({intermediates: true})\n const temp = new File(tempDir, shareName)\n file.copy(temp)\n await Share.open({\n url: temp.uri,\n type: meta.mimeType || OCTET,\n filename: shareName,\n })\n this.hooks.sendResult(packageName, requestId, true, {success: true})\n } catch (error: any) {\n if (error?.message?.includes(\"User did not share\")) {\n this.hooks.sendResult(packageName, requestId, true, {success: false, cancelled: true})\n } else {\n console.error(`${LOG_TAG}: blob share error:`, error)\n this.hooks.sendResult(packageName, requestId, true, {success: false})\n }\n } finally {\n if (tempDir) {\n try {\n if (tempDir.exists) tempDir.delete()\n } catch {\n /* ignore */\n }\n }\n }\n }\n\n // ---- lifecycle / cleanup -------------------------------------------------\n\n /** Called from LocalMiniappRuntime.unregisterApp — drop this app's in-flight state. */\n onAppGone(packageName: string): void {\n this.closeAppHandles(packageName)\n }\n\n /** Abort in-flight uploads + close open read handles for an app (no-op if none). */\n private closeAppHandles(packageName: string): void {\n for (const [k, up] of this.uploads) {\n if (up.packageName === packageName) {\n this.discardUpload(up)\n this.uploads.delete(k)\n }\n }\n for (const [k, reader] of this.readers) {\n if (reader.packageName === packageName) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(k)\n }\n }\n }\n\n private discardUpload(up: ActiveUpload): void {\n try {\n up.handle.close()\n } catch {\n /* ignore */\n }\n try {\n if (up.partFile.exists) up.partFile.delete()\n } catch {\n /* ignore */\n }\n }\n\n private dropMeta(packageName: string, meta: StoredBlobMeta): void {\n this.deleteFileQuiet(packageName, meta.fileName)\n mmkvStorage.remove(this.metaKey(packageName, meta.key))\n this.bumpUsage(packageName, -(meta.bytes || 0))\n }\n\n private deleteFileQuiet(packageName: string, fileName: string): void {\n try {\n const f = this.fileFor(packageName, fileName)\n if (f.exists) f.delete()\n } catch {\n /* ignore */\n }\n }\n\n private fail(packageName: string, requestId: string | undefined, code: MiniappErrorCode, err: unknown): void {\n console.error(`${LOG_TAG}: blob error [${code}]:`, err)\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code,\n message: err instanceof Error ? err.message : \"Blob error\",\n })\n }\n}\n\nconst OCTET = \"application/octet-stream\"\n\n/** Last path segment of a URL/URI, query stripped + percent-decoded. */\nfunction basename(uri: string): string {\n const noQuery = uri.split(\"?\")[0].split(\"#\")[0]\n const seg = noQuery.substring(noQuery.lastIndexOf(\"/\") + 1)\n try {\n return decodeURIComponent(seg)\n } catch {\n return seg\n }\n}\n\n/** Best-effort MIME from a filename extension. Returns undefined when unknown. */\nfunction inferMime(name: string): string | undefined {\n const dot = name.lastIndexOf(\".\")\n if (dot < 0) return undefined\n const ext = name.slice(dot + 1).toLowerCase()\n const map: Record<string, string> = {\n pdf: \"application/pdf\",\n epub: \"application/epub+zip\",\n zip: \"application/zip\",\n json: \"application/json\",\n txt: \"text/plain\",\n csv: \"text/csv\",\n html: \"text/html\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n webm: \"video/webm\",\n mp3: \"audio/mpeg\",\n m4a: \"audio/mp4\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n }\n return map[ext]\n}\n"]}
1
+ {"version":3,"file":"BlobStore.js","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * BlobStore\n *\n * Host-side implementation of `session.blob` — persistent, per-(userId,\n * packageName) BINARY storage for local miniapps, keyed like SimpleStorage (its\n * small-string sibling). Extracted from LocalMiniappRuntime the same way\n * NavigationHandlers is, to keep the runtime file small.\n *\n * Generic byte store — it knows nothing about any specific format. Highlights:\n * - chunked write/read (BLOB_CREATE/WRITE/COMMIT, BLOB_OPEN_READ/READ) so no\n * single bridge message is large;\n * - `setFromUrl` downloads a URL straight to disk (File.downloadFileAsync) and\n * `importFile` runs the OS picker (File.pickFileAsync) — both keep bytes\n * entirely host-side;\n * - `share` hands a stored file to the OS share sheet.\n *\n * Layout (mirrors SimpleStorage's scoping):\n * bytes: Paths.document/mentra_blobs/{userId}/{packageName}/{fileName}\n * metadata: MMKV key mentraos_blobmeta_{userId}_{packageName}_{key} → StoredBlobMeta JSON\n *\n * The on-disk file name is a generated id plus a mime-derived extension (iOS\n * AVPlayer keys a local file's format off the path extension, so blobs played\n * via `session.speaker.play` must carry one); the logical `key` (caller-chosen)\n * is the MMKV key. This decouples the filesystem from arbitrary caller keys.\n *\n * expo-file-system's new API is synchronous (create/writeBytes/md5/size), so\n * streaming chunk writes do no async work.\n */\n\nimport {Buffer} from \"buffer\"\nimport Share from \"react-native-share\"\nimport {Directory, File, Paths, type FileHandle} from \"expo-file-system\"\n\nimport {MiniappErrorCode} from \"@mentra/miniapp\"\nimport {storage as mmkvStorage} from \"../utils/storage/storage\"\nimport {sanitizeSegment, shareFileName, withDiskExt} from \"./blobPaths\"\n\nconst LOG_TAG = \"LocalMiniappRuntime\"\n\n/** Per-app blob quota: 1 GB. */\nexport const BLOB_QUOTA_BYTES = 1024 * 1024 * 1024\nconst ROOT_DIR_NAME = \"mentra_blobs\"\n/** Cache dir root holding short-lived, properly-named copies handed to the OS share sheet (one subdir per stored blob). */\nconst SHARE_DIR_NAME = \"mentra_blob_share\"\n/**\n * Keep prepared share handoffs alive long enough for recipient apps to read\n * them asynchronously (for example, an email composer attaching the file).\n * Stale copies are collected before the next share; the OS may evict cache\n * files sooner under storage pressure.\n */\nconst SHARE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000\nconst META_KEY_ROOT = \"mentraos_blobmeta_\"\n/** Cap md5 computation so we don't block the JS thread hashing a huge file. */\nconst MD5_MAX_BYTES = 50 * 1024 * 1024\n\n// Matches LocalMiniappRuntime.sendResult (see NavigationHandlers).\ntype SendResult = (\n packageName: string,\n requestId: string | undefined,\n ok: boolean,\n result?: unknown,\n error?: {code: MiniappErrorCode; message: string},\n) => void\n\nexport interface BlobStoreHooks {\n sendResult: SendResult\n /** Resolve the current user id (same source SimpleStorage uses). */\n getUserId: () => string\n}\n\ninterface StoredBlobMeta {\n key: string\n fileName: string\n name?: string\n mimeType: string\n bytes: number\n createdAt: number\n updatedAt: number\n md5?: string\n meta?: Record<string, string | number | boolean>\n}\n\ninterface ActiveUpload {\n packageName: string\n key: string\n fileName: string\n partFile: File\n handle: FileHandle\n bytes: number\n /** Bytes already committed under this key (overwrite target) — excluded from the quota check. */\n priorBytes: number\n name?: string\n mimeType: string\n meta?: Record<string, string | number | boolean>\n createdAt: number\n}\n\ninterface ActiveReader {\n packageName: string\n handle: FileHandle\n remaining: number\n}\n\nexport class BlobStore {\n private readonly uploads = new Map<string, ActiveUpload>() // key: `${pkg} ${key}`\n private readonly readers = new Map<string, ActiveReader>() // key: host handle id\n /** user + package → committed usage bytes, cached; invalidated on commit/delete/clear. */\n private readonly usageCache = new Map<string, number>()\n\n constructor(private readonly hooks: BlobStoreHooks) {}\n\n // ---- path / meta helpers -------------------------------------------------\n\n private currentIdentity(packageName: string): {userId: string; scope: string} {\n const userId = this.hooks.getUserId().trim()\n if (!userId) throw new Error(\"Mentra user identity is unavailable\")\n const scope = `${userId}\\0${packageName}`\n return {userId, scope}\n }\n\n private dirFor(packageName: string): Directory {\n const dir = new Directory(\n Paths.document,\n ROOT_DIR_NAME,\n sanitizeSegment(this.currentIdentity(packageName).userId),\n sanitizeSegment(packageName),\n )\n if (!dir.exists) dir.create({intermediates: true})\n return dir\n }\n\n private metaPrefix(packageName: string): string {\n return `${META_KEY_ROOT}${sanitizeSegment(this.currentIdentity(packageName).userId)}_${packageName}_`\n }\n\n private metaKey(packageName: string, key: string): string {\n return this.metaPrefix(packageName) + key\n }\n\n private uploadKey(packageName: string, key: string): string {\n return `${packageName} ${key}`\n }\n\n private readMeta(packageName: string, key: string): StoredBlobMeta | null {\n const r = mmkvStorage.load<StoredBlobMeta>(this.metaKey(packageName, key))\n return r.is_ok() ? r.value : null\n }\n\n private writeMeta(packageName: string, meta: StoredBlobMeta): void {\n mmkvStorage.save(this.metaKey(packageName, meta.key), meta)\n }\n\n private allMeta(packageName: string): StoredBlobMeta[] {\n const prefix = this.metaPrefix(packageName)\n const out: StoredBlobMeta[] = []\n for (const k of mmkvStorage.getAllKeys()) {\n if (!k.startsWith(prefix)) continue\n const r = mmkvStorage.load<StoredBlobMeta>(k)\n if (r.is_ok() && r.value) out.push(r.value)\n }\n return out\n }\n\n private fileFor(packageName: string, fileName: string): File {\n return new File(this.dirFor(packageName), fileName)\n }\n\n /** Public API shape: stored meta + derived file:// uri. */\n private toBlobMeta(packageName: string, m: StoredBlobMeta) {\n return {\n key: m.key,\n name: m.name,\n mimeType: m.mimeType,\n bytes: m.bytes,\n createdAt: m.createdAt,\n updatedAt: m.updatedAt,\n md5: m.md5,\n uri: this.fileFor(packageName, m.fileName).uri,\n meta: m.meta,\n }\n }\n\n private committedUsage(packageName: string): number {\n const {scope} = this.currentIdentity(packageName)\n const cached = this.usageCache.get(scope)\n if (cached !== undefined) return cached\n let total = 0\n for (const m of this.allMeta(packageName)) total += m.bytes || 0\n this.usageCache.set(scope, total)\n return total\n }\n\n private bumpUsage(packageName: string, delta: number): void {\n const {scope} = this.currentIdentity(packageName)\n const cur = this.usageCache.get(scope)\n if (cur !== undefined) this.usageCache.set(scope, Math.max(0, cur + delta))\n }\n\n private safeMd5(file: File, bytes: number): string | undefined {\n if (bytes > MD5_MAX_BYTES) return undefined\n try {\n return file.md5 ?? undefined\n } catch {\n return undefined\n }\n }\n\n private makeId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}${Math.random()\n .toString(36)\n .slice(2, 6)}`\n }\n\n /** Would storing `incoming` bytes under `key` exceed the per-app quota? */\n private overQuota(packageName: string, key: string, incoming: number): boolean {\n const priorBytes = this.readMeta(packageName, key)?.bytes ?? 0\n return this.committedUsage(packageName) - priorBytes + incoming > BLOB_QUOTA_BYTES\n }\n\n /** Publish a file already sitting at `fileName` as the blob for `key`; returns its public meta. */\n private commitFile(\n packageName: string,\n key: string,\n fileName: string,\n mimeType: string,\n name: string | undefined,\n extraMeta: Record<string, string | number | boolean> | undefined,\n ) {\n // Attach a mime-derived extension to the on-disk name. Consumers that key\n // format off the path — iOS AVPlayer under `session.speaker.play` — cannot\n // open an extension-less file, even though the bytes are valid.\n const named = withDiskExt(fileName, mimeType)\n if (named !== fileName) {\n try {\n this.fileFor(packageName, fileName).rename(named)\n fileName = named\n } catch {\n /* keep the extension-less name — the blob still stores and shares fine */\n }\n }\n const prior = this.readMeta(packageName, key)\n if (prior && prior.fileName !== fileName) {\n this.deleteFileQuiet(packageName, prior.fileName)\n this.bumpUsage(packageName, -(prior.bytes || 0))\n }\n const finalFile = this.fileFor(packageName, fileName)\n const bytes = finalFile.size ?? 0\n const now = Date.now()\n const meta: StoredBlobMeta = {\n key,\n fileName,\n name,\n mimeType,\n bytes,\n createdAt: prior?.createdAt ?? now,\n updatedAt: now,\n md5: this.safeMd5(finalFile, bytes),\n meta: extraMeta,\n }\n this.writeMeta(packageName, meta)\n this.bumpUsage(packageName, bytes)\n return this.toBlobMeta(packageName, meta)\n }\n\n // ---- streaming write -----------------------------------------------------\n\n handleCreate(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n try {\n const key = (typeof payload.key === \"string\" && payload.key) || this.makeId()\n const mapKey = this.uploadKey(packageName, key)\n const existing = this.uploads.get(mapKey)\n if (existing) this.discardUpload(existing)\n\n const fileName = this.makeId()\n const partFile = this.fileFor(packageName, fileName + \".part\")\n partFile.create({intermediates: true, overwrite: true})\n const handle = partFile.open()\n\n this.uploads.set(mapKey, {\n packageName,\n key,\n fileName,\n partFile,\n handle,\n bytes: 0,\n priorBytes: this.readMeta(packageName, key)?.bytes ?? 0,\n name: typeof payload.name === \"string\" ? payload.name : undefined,\n mimeType: typeof payload.mimeType === \"string\" ? payload.mimeType : \"application/octet-stream\",\n meta: (payload.meta as Record<string, string | number | boolean>) ?? undefined,\n createdAt: Date.now(),\n })\n this.hooks.sendResult(packageName, requestId, true, {key})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleWrite(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const up = this.uploads.get(this.uploadKey(packageName, key))\n if (!up) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: `No open blob writer for key ${key}`,\n })\n return\n }\n try {\n const chunk = Buffer.from((payload.base64 as string) ?? \"\", \"base64\")\n const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)\n // A seek write (offset present) patches existing bytes in place — used to\n // rewrite a container header on finalize; it does not grow the blob.\n if (typeof payload.offset === \"number\") {\n if (payload.offset < 0 || payload.offset + bytes.length > up.bytes) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_WRITE_FAILED,\n message: \"writeAt offset out of range (must stay within written bytes)\",\n })\n return\n }\n up.handle.offset = payload.offset\n up.handle.writeBytes(bytes)\n up.handle.offset = up.bytes // restore append position\n this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})\n return\n }\n // Exclude the prior blob under this key — it's freed on commit (overwrite).\n if (this.committedUsage(packageName) - up.priorBytes + up.bytes + bytes.length > BLOB_QUOTA_BYTES) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n up.handle.offset = up.bytes\n up.handle.writeBytes(bytes)\n up.bytes += bytes.length\n this.hooks.sendResult(packageName, requestId, true, {bytesWritten: up.bytes})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleCommit(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const mapKey = this.uploadKey(packageName, key)\n const up = this.uploads.get(mapKey)\n if (!up) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: `No open blob writer for key ${key}`,\n })\n return\n }\n try {\n up.handle.close()\n up.partFile.rename(up.fileName) // drop \".part\"\n const extraMeta = {\n ...(up.meta ?? {}),\n ...((payload.meta as Record<string, string | number | boolean>) ?? {}),\n }\n const meta = this.commitFile(packageName, key, up.fileName, up.mimeType, up.name, extraMeta)\n this.uploads.delete(mapKey)\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n this.discardUpload(up)\n this.uploads.delete(mapKey)\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_WRITE_FAILED, err)\n }\n }\n\n handleAbort(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const mapKey = this.uploadKey(packageName, payload.key as string)\n const up = this.uploads.get(mapKey)\n if (up) {\n this.discardUpload(up)\n this.uploads.delete(mapKey)\n }\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n // ---- download / import (host-side; no bytes cross the bridge) -------------\n\n async handleSetFromUrl(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = payload.key as string\n const url = payload.url as string\n if (!key || !url) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, new Error(\"setFromUrl requires key + url\"))\n return\n }\n if (url.startsWith(\"file:\") || url.startsWith(\"javascript:\")) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_DOWNLOAD_FAILED,\n message: \"Unsupported URL scheme\",\n })\n return\n }\n const headers = payload.headers as Record<string, string> | undefined\n const fileName = this.makeId()\n const partFile = this.fileFor(packageName, fileName + \".part\")\n try {\n const dl = await File.downloadFileAsync(url, partFile, {idempotent: true, ...(headers ? {headers} : {})})\n const size = dl.size ?? 0\n if (this.overQuota(packageName, key, size)) {\n try {\n dl.delete()\n } catch {\n /* ignore */\n }\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n partFile.rename(fileName)\n const mimeType =\n (payload.mimeType as string) || inferMime(basename(url)) || this.fileFor(packageName, fileName).type || OCTET\n const name = (payload.name as string) || basename(url) || key\n const meta = this.commitFile(\n packageName,\n key,\n fileName,\n mimeType,\n name,\n payload.meta as Record<string, string | number | boolean> | undefined,\n )\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n try {\n if (partFile.exists) partFile.delete()\n } catch {\n /* ignore */\n }\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_DOWNLOAD_FAILED, err)\n }\n }\n\n async handleImport(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = (typeof payload.key === \"string\" && payload.key) || this.makeId()\n const mimeFilter = payload.mimeType as string | undefined\n\n let picked: File | File[] | null = null\n try {\n picked = await File.pickFileAsync(undefined, mimeFilter)\n } catch (err) {\n // Picker dismissed / cancelled (or unavailable) → resolve to null, not an error.\n console.log(`${LOG_TAG}: blob import picker dismissed`, err)\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n const file = Array.isArray(picked) ? picked[0] : picked\n if (!file) {\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n try {\n const size = file.size ?? 0\n if (this.overQuota(packageName, key, size)) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_QUOTA_EXCEEDED,\n message: `Blob quota (${BLOB_QUOTA_BYTES} bytes) exceeded`,\n })\n return\n }\n const fileName = this.makeId()\n file.copy(this.fileFor(packageName, fileName))\n const name = basename(file.uri) || key\n const mimeType = mimeFilter || file.type || inferMime(name) || OCTET\n const meta = this.commitFile(\n packageName,\n key,\n fileName,\n mimeType,\n name,\n payload.meta as Record<string, string | number | boolean> | undefined,\n )\n this.hooks.sendResult(packageName, requestId, true, meta)\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.BLOB_IMPORT_FAILED, err)\n }\n }\n\n // ---- read ----------------------------------------------------------------\n\n handleGet(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n if (!meta || !this.fileFor(packageName, meta.fileName).exists) {\n if (meta) this.dropMeta(packageName, meta) // reconcile dangling meta\n this.hooks.sendResult(packageName, requestId, true, null)\n return\n }\n this.hooks.sendResult(packageName, requestId, true, this.toBlobMeta(packageName, meta))\n }\n\n handleList(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n const blobs = this.allMeta(packageName)\n .filter((m) => this.fileFor(packageName, m.fileName).exists)\n .sort((a, b) => b.createdAt - a.createdAt)\n .map((m) => this.toBlobMeta(packageName, m))\n this.hooks.sendResult(packageName, requestId, true, {blobs})\n }\n\n handleUsage(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n const metas = this.allMeta(packageName)\n this.hooks.sendResult(packageName, requestId, true, {\n bytes: metas.reduce((s, m) => s + (m.bytes || 0), 0),\n count: metas.length,\n quotaBytes: BLOB_QUOTA_BYTES,\n })\n }\n\n handleDelete(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const meta = this.readMeta(packageName, payload.key as string)\n if (meta) this.dropMeta(packageName, meta)\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n handleClear(packageName: string, _payload: Record<string, unknown>, requestId?: string): void {\n try {\n // Abort any in-flight uploads / open readers first so nothing keeps writing\n // to a .part file in the directory we're about to delete.\n this.closeAppHandles(packageName)\n for (const m of this.allMeta(packageName)) mmkvStorage.remove(this.metaKey(packageName, m.key))\n const dir = this.dirFor(packageName)\n try {\n if (dir.exists) dir.delete()\n } catch {\n /* best-effort */\n }\n this.usageCache.set(this.currentIdentity(packageName).scope, 0)\n this.hooks.sendResult(packageName, requestId, true)\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleOpenRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n const file = meta ? this.fileFor(packageName, meta.fileName) : null\n if (!meta || !file || !file.exists) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_NOT_FOUND,\n message: `Blob ${key} not found`,\n })\n return\n }\n try {\n const handle = file.open()\n const handleId = `r_${this.makeId()}`\n this.readers.set(handleId, {packageName, handle, remaining: meta.bytes})\n this.hooks.sendResult(packageName, requestId, true, {handle: handleId, meta: this.toBlobMeta(packageName, meta)})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const handleId = payload.handle as string\n const reader = this.readers.get(handleId)\n if (!reader || reader.packageName !== packageName) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_HANDLE_INVALID,\n message: \"Unknown blob read handle\",\n })\n return\n }\n try {\n const want = Math.max(0, Math.min(Number(payload.maxBytes) || 1024 * 1024, reader.remaining))\n const bytes = want > 0 ? reader.handle.readBytes(want) : new Uint8Array(0)\n reader.remaining -= bytes.length\n const done = reader.remaining <= 0\n const base64 = bytes.length\n ? Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(\"base64\")\n : \"\"\n if (done) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(handleId)\n }\n this.hooks.sendResult(packageName, requestId, true, {base64, done})\n } catch (err) {\n this.fail(packageName, requestId, MiniappErrorCode.INTERNAL, err)\n }\n }\n\n handleCloseRead(packageName: string, payload: Record<string, unknown>, requestId?: string): void {\n const handleId = payload.handle as string\n const reader = this.readers.get(handleId)\n if (reader && reader.packageName === packageName) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(handleId)\n }\n this.hooks.sendResult(packageName, requestId, true)\n }\n\n // ---- share ---------------------------------------------------------------\n\n /** Best-effort GC for prepared share handoffs retained in the OS cache. */\n private cleanupShareCache(now = Date.now()): void {\n try {\n const root = new Directory(Paths.cache, SHARE_DIR_NAME)\n if (!root.exists) return\n const cutoff = now - SHARE_CACHE_MAX_AGE_MS\n for (const entry of root.list()) {\n if (!(entry instanceof Directory)) continue\n try {\n const info = entry.info()\n // modificationTime is available on both platforms; creationTime can\n // be absent on Android versions before API 26. The directory id\n // begins with Date.now().toString(36), which is a final fallback.\n const encoded = entry.name.split(\"-\", 1)[0]\n const encodedTime = /^[0-9a-z]+$/.test(encoded) ? Number.parseInt(encoded, 36) : Number.NaN\n const timestamp = info.modificationTime ?? info.creationTime ?? encodedTime\n if (Number.isFinite(timestamp) && timestamp < cutoff) entry.delete()\n } catch {\n // Keep scanning if one cache entry disappears or is unreadable.\n }\n }\n } catch {\n // Cache cleanup must never prevent a share.\n }\n }\n\n async handleShare(packageName: string, payload: Record<string, unknown>, requestId?: string): Promise<void> {\n const key = payload.key as string\n const meta = this.readMeta(packageName, key)\n const file = meta ? this.fileFor(packageName, meta.fileName) : null\n if (!meta || !file || !file.exists) {\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code: MiniappErrorCode.BLOB_NOT_FOUND,\n message: `Blob ${key} not found`,\n })\n return\n }\n // The on-disk blob name is a generated id (mime extension attached at\n // commit; legacy blobs may lack it). react-native-share derives the shared\n // file's name + extension from the URL's path (its `filename` option only\n // applies to base64/data payloads, not file:// URLs), so sharing `file.uri`\n // directly would hand the recipient a machine-named file. Copy to a temp\n // file that carries the real display name + extension, share that, then\n // clean it up after the recipient has had time to consume it.\n const shareName = shareFileName(meta)\n // Key the cache directory by the immutable stored filename. Repeated or\n // concurrent shares of the same blob can safely reference the same bytes,\n // avoiding an extra full-sized cache copy for every share. Different blobs\n // still get isolated directories even when they use the same display name.\n // The file keeps `shareName` as its basename so the OS share sheet shows the\n // right name + extension.\n let tempDir: Directory | null = null\n let cacheReady = false\n try {\n this.cleanupShareCache()\n tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, sanitizeSegment(meta.fileName))\n if (!tempDir.exists) tempDir.create({intermediates: true})\n const temp = new File(tempDir, shareName)\n if (!temp.exists || temp.size !== meta.bytes) {\n if (temp.exists) temp.delete()\n file.copy(temp)\n }\n cacheReady = true\n await Share.open({\n url: temp.uri,\n type: meta.mimeType || OCTET,\n filename: shareName,\n })\n // Share.open resolves once the OS accepts the handoff. Recipient apps may\n // still read the URI asynchronously, so retain this cache copy for GC.\n this.hooks.sendResult(packageName, requestId, true, {success: true})\n } catch (error: any) {\n if (error?.message?.includes(\"User did not share\")) {\n this.hooks.sendResult(packageName, requestId, true, {success: false, cancelled: true})\n } else {\n console.error(`${LOG_TAG}: blob share error:`, error)\n this.hooks.sendResult(packageName, requestId, true, {success: false})\n }\n } finally {\n // Once a reusable cache entry is ready, retain it even if this particular\n // sheet is cancelled or fails: another concurrent/recent share may still\n // be reading the same file. Only remove an entry whose preparation did\n // not finish; ready entries are collected after the retention window.\n if (tempDir && !cacheReady) {\n try {\n if (tempDir.exists) tempDir.delete()\n } catch {\n /* ignore */\n }\n }\n }\n }\n\n // ---- lifecycle / cleanup -------------------------------------------------\n\n /** Called from LocalMiniappRuntime.unregisterApp — drop this app's in-flight state. */\n onAppGone(packageName: string): void {\n this.closeAppHandles(packageName)\n }\n\n /** Abort in-flight uploads + close open read handles for an app (no-op if none). */\n private closeAppHandles(packageName: string): void {\n for (const [k, up] of this.uploads) {\n if (up.packageName === packageName) {\n this.discardUpload(up)\n this.uploads.delete(k)\n }\n }\n for (const [k, reader] of this.readers) {\n if (reader.packageName === packageName) {\n try {\n reader.handle.close()\n } catch {\n /* ignore */\n }\n this.readers.delete(k)\n }\n }\n }\n\n private discardUpload(up: ActiveUpload): void {\n try {\n up.handle.close()\n } catch {\n /* ignore */\n }\n try {\n if (up.partFile.exists) up.partFile.delete()\n } catch {\n /* ignore */\n }\n }\n\n private dropMeta(packageName: string, meta: StoredBlobMeta): void {\n this.deleteFileQuiet(packageName, meta.fileName)\n mmkvStorage.remove(this.metaKey(packageName, meta.key))\n this.bumpUsage(packageName, -(meta.bytes || 0))\n }\n\n private deleteFileQuiet(packageName: string, fileName: string): void {\n try {\n const f = this.fileFor(packageName, fileName)\n if (f.exists) f.delete()\n } catch {\n /* ignore */\n }\n }\n\n private fail(packageName: string, requestId: string | undefined, code: MiniappErrorCode, err: unknown): void {\n console.error(`${LOG_TAG}: blob error [${code}]:`, err)\n this.hooks.sendResult(packageName, requestId, false, undefined, {\n code,\n message: err instanceof Error ? err.message : \"Blob error\",\n })\n }\n}\n\nconst OCTET = \"application/octet-stream\"\n\n/** Last path segment of a URL/URI, query stripped + percent-decoded. */\nfunction basename(uri: string): string {\n const noQuery = uri.split(\"?\")[0].split(\"#\")[0]\n const seg = noQuery.substring(noQuery.lastIndexOf(\"/\") + 1)\n try {\n return decodeURIComponent(seg)\n } catch {\n return seg\n }\n}\n\n/** Best-effort MIME from a filename extension. Returns undefined when unknown. */\nfunction inferMime(name: string): string | undefined {\n const dot = name.lastIndexOf(\".\")\n if (dot < 0) return undefined\n const ext = name.slice(dot + 1).toLowerCase()\n const map: Record<string, string> = {\n pdf: \"application/pdf\",\n epub: \"application/epub+zip\",\n zip: \"application/zip\",\n json: \"application/json\",\n txt: \"text/plain\",\n csv: \"text/csv\",\n html: \"text/html\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n webm: \"video/webm\",\n mp3: \"audio/mpeg\",\n m4a: \"audio/mp4\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n }\n return map[ext]\n}\n"]}
@@ -1,4 +1,17 @@
1
- import type { GlassesStatus } from "@mentra/bluetooth-sdk/internal";
2
- export declare function startGlassesStatusProjection(forwarder?: (status: Partial<GlassesStatus>) => void): Promise<void>;
1
+ /**
2
+ * Glasses status projection engine-owned. Subscribes to the native bluetooth-sdk
3
+ * status events and projects them into the engine device stores, so engine's own
4
+ * services (which react to those stores) work for ANY host — not just the first-party
5
+ * Mentra app, where this used to live in MantleManager.
6
+ *
7
+ * This is the INBOUND mirror of GlassesSettingsSync: that pushes the settings store
8
+ * OUT to the device; this pulls device status IN to the stores. It's the feed that
9
+ * the rest of the engine runtime depends on (MicStateCoordinator, the settings-sync
10
+ * on-connect trigger, the facade read-models all read these stores).
11
+ *
12
+ * Started by `engine.start()`. Idempotent.
13
+ */
14
+ import { type PublicGlassesStatus } from "@mentra/bluetooth-sdk";
15
+ export declare function startGlassesStatusProjection(forwarder?: (status: Partial<PublicGlassesStatus>) => void): Promise<void>;
3
16
  export declare function stopGlassesStatusProjection(): void;
4
17
  //# sourceMappingURL=GlassesStatusProjection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesStatusProjection.d.ts","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gCAAgC,CAAA;AASjE,wBAAgB,4BAA4B,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDhH;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD"}
1
+ {"version":3,"file":"GlassesStatusProjection.d.ts","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAqB,EAAC,KAAK,mBAAmB,EAAC,MAAM,uBAAuB,CAAA;AAS5E,wBAAgB,4BAA4B,CAC1C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,KAAK,IAAI,GACzD,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD"}
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * Started by `engine.start()`. Idempotent.
13
13
  */
14
- import BluetoothSdk from "@mentra/bluetooth-sdk/internal";
14
+ import BluetoothSdk from "@mentra/bluetooth-sdk";
15
15
  import { useCoreStore } from "../stores/core";
16
16
  import { useGlassesStore } from "../stores/glasses";
17
17
  let unsubs = [];
@@ -45,13 +45,13 @@ export function startGlassesStatusProjection(forwarder) {
45
45
  console.warn("GlassesStatusProjection: getGlassesStatus failed", error);
46
46
  });
47
47
  // Bluetooth-adapter status -> core store.
48
- unsubs.push(BluetoothSdk.onBluetoothStatus((changed) => {
48
+ unsubs.push(BluetoothSdk.subscribeBluetoothStatus((changed) => {
49
49
  bluetoothEventSeen = true;
50
50
  useCoreStore.getState().setCoreInfo(changed);
51
51
  }));
52
52
  // Glasses status -> glasses store (+ optional full-runtime forwarding to local
53
53
  // miniapps; clear any stale OTA-available flag on disconnect).
54
- unsubs.push(BluetoothSdk.onGlassesStatus((changed) => {
54
+ unsubs.push(BluetoothSdk.subscribeGlassesStatus((changed) => {
55
55
  glassesEventSeen = true;
56
56
  useGlassesStore.getState().setGlassesInfo(changed);
57
57
  glassesStatusForwarder?.(changed);
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesStatusProjection.js","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Glasses status projection — engine-owned. Subscribes to the native bluetooth-sdk\n * status events and projects them into the engine device stores, so engine's own\n * services (which react to those stores) work for ANY host — not just the first-party\n * Mentra app, where this used to live in MantleManager.\n *\n * This is the INBOUND mirror of GlassesSettingsSync: that pushes the settings store\n * OUT to the device; this pulls device status IN to the stores. It's the feed that\n * the rest of the engine runtime depends on (MicStateCoordinator, the settings-sync\n * on-connect trigger, the facade read-models all read these stores).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {GlassesStatus} from \"@mentra/bluetooth-sdk/internal\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\n\nlet unsubs: Array<() => void> = []\nlet projectionRunId = 0\nlet glassesStatusForwarder: ((status: Partial<GlassesStatus>) => void) | null = null\nlet hydrationPromise: Promise<void> | null = null\n\nexport function startGlassesStatusProjection(forwarder?: (status: Partial<GlassesStatus>) => void): Promise<void> {\n if (forwarder) glassesStatusForwarder = forwarder\n if (unsubs.length) return hydrationPromise ?? Promise.resolve()\n\n const runId = ++projectionRunId\n let bluetoothEventSeen = false\n let glassesEventSeen = false\n\n const bluetoothHydration = BluetoothSdk.getBluetoothStatus()\n .then((status) => {\n if (runId !== projectionRunId || bluetoothEventSeen) return\n useCoreStore.getState().setCoreInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getBluetoothStatus failed\", error)\n })\n\n const glassesHydration = BluetoothSdk.getGlassesStatus()\n .then((status) => {\n if (runId !== projectionRunId || glassesEventSeen) return\n useGlassesStore.getState().setGlassesInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getGlassesStatus failed\", error)\n })\n\n // Bluetooth-adapter status -> core store.\n unsubs.push(\n BluetoothSdk.onBluetoothStatus((changed) => {\n bluetoothEventSeen = true\n useCoreStore.getState().setCoreInfo(changed)\n }),\n )\n\n // Glasses status -> glasses store (+ optional full-runtime forwarding to local\n // miniapps; clear any stale OTA-available flag on disconnect).\n unsubs.push(\n BluetoothSdk.onGlassesStatus((changed) => {\n glassesEventSeen = true\n useGlassesStore.getState().setGlassesInfo(changed)\n glassesStatusForwarder?.(changed)\n if (changed.connection?.state === \"disconnected\") {\n useGlassesStore.getState().setOtaUpdateAvailable(null)\n }\n }),\n )\n\n hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)\n return hydrationPromise\n}\n\nexport function stopGlassesStatusProjection(): void {\n projectionRunId++\n unsubs.forEach((unsub) => unsub())\n unsubs = []\n glassesStatusForwarder = null\n hydrationPromise = null\n}\n"]}
1
+ {"version":3,"file":"GlassesStatusProjection.js","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Glasses status projection — engine-owned. Subscribes to the native bluetooth-sdk\n * status events and projects them into the engine device stores, so engine's own\n * services (which react to those stores) work for ANY host — not just the first-party\n * Mentra app, where this used to live in MantleManager.\n *\n * This is the INBOUND mirror of GlassesSettingsSync: that pushes the settings store\n * OUT to the device; this pulls device status IN to the stores. It's the feed that\n * the rest of the engine runtime depends on (MicStateCoordinator, the settings-sync\n * on-connect trigger, the facade read-models all read these stores).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk, {type PublicGlassesStatus} from \"@mentra/bluetooth-sdk\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\n\nlet unsubs: Array<() => void> = []\nlet projectionRunId = 0\nlet glassesStatusForwarder: ((status: Partial<PublicGlassesStatus>) => void) | null = null\nlet hydrationPromise: Promise<void> | null = null\n\nexport function startGlassesStatusProjection(\n forwarder?: (status: Partial<PublicGlassesStatus>) => void,\n): Promise<void> {\n if (forwarder) glassesStatusForwarder = forwarder\n if (unsubs.length) return hydrationPromise ?? Promise.resolve()\n\n const runId = ++projectionRunId\n let bluetoothEventSeen = false\n let glassesEventSeen = false\n\n const bluetoothHydration = BluetoothSdk.getBluetoothStatus()\n .then((status) => {\n if (runId !== projectionRunId || bluetoothEventSeen) return\n useCoreStore.getState().setCoreInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getBluetoothStatus failed\", error)\n })\n\n const glassesHydration = BluetoothSdk.getGlassesStatus()\n .then((status) => {\n if (runId !== projectionRunId || glassesEventSeen) return\n useGlassesStore.getState().setGlassesInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getGlassesStatus failed\", error)\n })\n\n // Bluetooth-adapter status -> core store.\n unsubs.push(\n BluetoothSdk.subscribeBluetoothStatus((changed) => {\n bluetoothEventSeen = true\n useCoreStore.getState().setCoreInfo(changed)\n }),\n )\n\n // Glasses status -> glasses store (+ optional full-runtime forwarding to local\n // miniapps; clear any stale OTA-available flag on disconnect).\n unsubs.push(\n BluetoothSdk.subscribeGlassesStatus((changed) => {\n glassesEventSeen = true\n useGlassesStore.getState().setGlassesInfo(changed)\n glassesStatusForwarder?.(changed)\n if (changed.connection?.state === \"disconnected\") {\n useGlassesStore.getState().setOtaUpdateAvailable(null)\n }\n }),\n )\n\n hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)\n return hydrationPromise\n}\n\nexport function stopGlassesStatusProjection(): void {\n projectionRunId++\n unsubs.forEach((unsub) => unsub())\n unsubs = []\n glassesStatusForwarder = null\n hydrationPromise = null\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"HotspotOtaTransport.d.ts","sourceRoot":"","sources":["../../src/services/HotspotOtaTransport.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAML,KAAK,2BAA2B,EAEjC,MAAM,yBAAyB,CAAA;AAIhC,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,SAAS,CAAA;AAEzG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,CAAC,EAAE,2BAA2B,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAC3B,gCAAgC,GAChC,sBAAsB,GACtB,qBAAqB,GACrB,uBAAuB,CAAA;AAE3B,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,IAAI,EAAE,mBAAmB;gBAAzB,IAAI,EAAE,mBAAmB,EACzC,OAAO,EAAE,MAAM;CAKlB;AAED,yFAAyF;AACzF,cAAM,mBAAmB;IACvB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,eAAe,CAA6B;IAE9C,OAAO,CACX,WAAW,EAAE,4BAA4B,EACzC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAClD,OAAO,CAAC,MAAM,CAAC;IAuElB,gGAAgG;YAClF,2BAA2B;IAczC,OAAO,CAAC,mBAAmB,CAa1B;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA4BhC;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAA"}
1
+ {"version":3,"file":"HotspotOtaTransport.d.ts","sourceRoot":"","sources":["../../src/services/HotspotOtaTransport.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAML,KAAK,2BAA2B,EAEjC,MAAM,yBAAyB,CAAA;AAIhC,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,SAAS,CAAA;AAEzG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,CAAC,EAAE,2BAA2B,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAC3B,gCAAgC,GAChC,sBAAsB,GACtB,qBAAqB,GACrB,uBAAuB,CAAA;AAE3B,qBAAa,wBAAyB,SAAQ,KAAK;aAE/B,IAAI,EAAE,mBAAmB;gBAAzB,IAAI,EAAE,mBAAmB,EACzC,OAAO,EAAE,MAAM;CAKlB;AAED,yFAAyF;AACzF,cAAM,mBAAmB;IACvB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,eAAe,CAA6B;IAE9C,OAAO,CACX,WAAW,EAAE,4BAA4B,EACzC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAClD,OAAO,CAAC,MAAM,CAAC;IAuElB,gGAAgG;YAClF,2BAA2B;IAczC,OAAO,CAAC,mBAAmB,CAa1B;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA4BhC;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAA"}
@@ -1,4 +1,5 @@
1
- import BluetoothSdk, { isEnabledHotspotStatus, MentraOtaServer } from "@mentra/bluetooth-sdk/internal";
1
+ import BluetoothSdk, { isEnabledHotspotStatus } from "@mentra/bluetooth-sdk";
2
+ import { otaServer } from "@mentra/bluetooth-sdk/ota-transport";
2
3
  // One coordinator selects the native Android or iOS staging implementation at runtime.
3
4
  // eslint-disable-next-line react-native/split-platform-components
4
5
  import { PermissionsAndroid, Platform } from "react-native";
@@ -49,7 +50,7 @@ class HotspotOtaTransport {
49
50
  let localAddress = scopedAddress;
50
51
  if (Platform.OS === "ios") {
51
52
  try {
52
- localAddress = await MentraOtaServer.waitForWifiAddress(hotspot.localIp, 15_000);
53
+ localAddress = await otaServer.waitForWifiAddress(hotspot.localIp, 15_000);
53
54
  }
54
55
  catch (error) {
55
56
  throw new HotspotOtaTransportError("hotspot_join_failed", error instanceof Error ? error.message : String(error));
@@ -58,10 +59,10 @@ class HotspotOtaTransport {
58
59
  const artifactPaths = Object.fromEntries(this.prepared.map((artifact) => [artifact.sha256, artifact.filePath]));
59
60
  // Start exactly one native listener to learn its selected port, then atomically replace
60
61
  // the placeholder with the immutable rewritten manifest before ota_start is sent.
61
- const server = await MentraOtaServer.startOtaServer("{}", artifactPaths, localAddress);
62
+ const server = await otaServer.start("{}", artifactPaths, localAddress);
62
63
  this.serverStarted = true;
63
64
  const manifest = rewriteManifestForLocalServer(checkResult.manifestBody, this.prepared, server.baseUrl);
64
- const published = await MentraOtaServer.startOtaServer(manifest, artifactPaths, server.host);
65
+ const published = await otaServer.start(manifest, artifactPaths, server.host);
65
66
  if (published.manifestUrl !== server.manifestUrl) {
66
67
  throw new Error("Local OTA server endpoint changed while publishing the manifest");
67
68
  }
@@ -98,12 +99,12 @@ class HotspotOtaTransport {
98
99
  }
99
100
  }
100
101
  downloadIosArtifact = async (entry, destination, onProgress) => {
101
- const subscription = MentraOtaServer.addListener("artifactDownloadProgress", (event) => {
102
+ const subscription = otaServer.onArtifactDownloadProgress((event) => {
102
103
  if (event.destination === destination)
103
104
  onProgress?.(event.bytesWritten, event.contentLength);
104
105
  });
105
106
  try {
106
- return await MentraOtaServer.downloadArtifact(entry.url, destination);
107
+ return await otaServer.downloadArtifact(entry.url, destination);
107
108
  }
108
109
  finally {
109
110
  subscription.remove();
@@ -124,7 +125,7 @@ class HotspotOtaTransport {
124
125
  }
125
126
  }
126
127
  if (this.serverStarted)
127
- await MentraOtaServer.stopOtaServer().catch(() => { });
128
+ await otaServer.stop().catch(() => { });
128
129
  if (this.localNetworkConnected)
129
130
  await localNetworkTransport.disconnect().catch(() => { });
130
131
  await cleanupArtifacts().catch(() => { });
@@ -1 +1 @@
1
- {"version":3,"file":"HotspotOtaTransport.js","sourceRoot":"","sources":["../../src/services/HotspotOtaTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk, {isEnabledHotspotStatus, MentraOtaServer} from \"@mentra/bluetooth-sdk/internal\"\n// One coordinator selects the native Android or iOS staging implementation at runtime.\n// eslint-disable-next-line react-native/split-platform-components\nimport {PermissionsAndroid, Platform} from \"react-native\"\nimport type {OtaCheckCurrentGlassesResult} from \"./OtaUpdateCheckService\"\nimport {\n cleanupArtifacts,\n OtaArtifactError,\n planArtifacts,\n prepareArtifacts,\n rewriteManifestForLocalServer,\n type OtaArtifactDownloadProgress,\n type PreparedOtaArtifact,\n} from \"./OtaArtifactDownloader\"\nimport {disableHotspotWithRetry} from \"./HotspotShutdown\"\nimport {localNetworkTransport} from \"./asg/localNetworkTransport\"\n\nexport type HotspotOtaPhase = \"idle\" | \"downloading\" | \"starting_hotspot\" | \"joining_hotspot\" | \"serving\"\n\nexport type HotspotOtaProgress = {\n phase: HotspotOtaPhase\n artifact?: OtaArtifactDownloadProgress\n}\n\nexport type HotspotOtaErrorCode =\n | \"hotspot_wifi_permission_denied\"\n | \"hotspot_start_failed\"\n | \"hotspot_join_failed\"\n | \"hotspot_server_failed\"\n\nexport class HotspotOtaTransportError extends Error {\n constructor(\n public readonly code: HotspotOtaErrorCode,\n message: string,\n ) {\n super(message)\n this.name = \"HotspotOtaTransportError\"\n }\n}\n\n/** Owns the single phone-side endpoint and network lease for one hotspot OTA attempt. */\nclass HotspotOtaTransport {\n private active = false\n private prepared: PreparedOtaArtifact[] = []\n private hotspotRequested = false\n private localNetworkConnected = false\n private serverStarted = false\n private teardownPromise: Promise<void> | null = null\n\n async prepare(\n checkResult: OtaCheckCurrentGlassesResult,\n onProgress?: (progress: HotspotOtaProgress) => void,\n ): Promise<string> {\n if (this.teardownPromise) await this.teardownPromise\n if (this.active) {\n throw new Error(\"A hotspot OTA transport is already active\")\n }\n if (!checkResult.manifestBody) {\n throw new Error(\"The selected OTA check has no manifest body\")\n }\n\n let phase: HotspotOtaPhase = \"downloading\"\n try {\n await this.ensureAndroidWifiPermission()\n onProgress?.({phase: \"downloading\"})\n this.prepared = await prepareArtifacts(\n planArtifacts(checkResult),\n (artifact) => onProgress?.({phase: \"downloading\", artifact}),\n Platform.OS === \"ios\" ? this.downloadIosArtifact : undefined,\n )\n\n phase = \"starting_hotspot\"\n onProgress?.({phase: \"starting_hotspot\"})\n this.hotspotRequested = true\n const hotspot = await BluetoothSdk.setHotspotState(true)\n if (!isEnabledHotspotStatus(hotspot)) {\n throw new HotspotOtaTransportError(\"hotspot_start_failed\", \"Mentra Live did not return hotspot credentials\")\n }\n phase = \"joining_hotspot\"\n onProgress?.({phase: \"joining_hotspot\"})\n const scopedAddress = await localNetworkTransport.connect(hotspot.ssid, hotspot.password)\n this.localNetworkConnected = true\n let localAddress = scopedAddress\n if (Platform.OS === \"ios\") {\n try {\n localAddress = await MentraOtaServer.waitForWifiAddress(hotspot.localIp, 15_000)\n } catch (error) {\n throw new HotspotOtaTransportError(\n \"hotspot_join_failed\",\n error instanceof Error ? error.message : String(error),\n )\n }\n }\n const artifactPaths = Object.fromEntries(this.prepared.map((artifact) => [artifact.sha256, artifact.filePath]))\n // Start exactly one native listener to learn its selected port, then atomically replace\n // the placeholder with the immutable rewritten manifest before ota_start is sent.\n const server = await MentraOtaServer.startOtaServer(\"{}\", artifactPaths, localAddress)\n this.serverStarted = true\n const manifest = rewriteManifestForLocalServer(checkResult.manifestBody, this.prepared, server.baseUrl)\n const published = await MentraOtaServer.startOtaServer(manifest, artifactPaths, server.host)\n if (published.manifestUrl !== server.manifestUrl) {\n throw new Error(\"Local OTA server endpoint changed while publishing the manifest\")\n }\n this.active = true\n onProgress?.({phase: \"serving\"})\n return published.manifestUrl\n } catch (error) {\n const joined = this.localNetworkConnected\n await this.teardown()\n if (error instanceof OtaArtifactError || error instanceof HotspotOtaTransportError) {\n throw error\n }\n const message = error instanceof Error ? error.message : String(error)\n if (phase === \"starting_hotspot\") {\n throw new HotspotOtaTransportError(\"hotspot_start_failed\", message)\n }\n if (phase === \"joining_hotspot\" && !joined) {\n throw new HotspotOtaTransportError(\"hotspot_join_failed\", message)\n }\n throw new HotspotOtaTransportError(\"hotspot_server_failed\", message)\n }\n }\n\n /** Android 13+ requires the nearby-WiFi runtime grant before WifiNetworkSpecifier is usable. */\n private async ensureAndroidWifiPermission(): Promise<void> {\n if (Platform.OS !== \"android\") return\n\n const permission = PermissionsAndroid.PERMISSIONS.NEARBY_WIFI_DEVICES\n if (await PermissionsAndroid.check(permission)) return\n const result = await PermissionsAndroid.request(permission)\n if (result !== PermissionsAndroid.RESULTS.GRANTED) {\n throw new HotspotOtaTransportError(\n \"hotspot_wifi_permission_denied\",\n \"Nearby devices permission is required to connect to the glasses hotspot\",\n )\n }\n }\n\n private downloadIosArtifact = async (\n entry: {url: string},\n destination: string,\n onProgress?: (bytesWritten: number, contentLength: number) => void,\n ): Promise<{statusCode: number}> => {\n const subscription = MentraOtaServer.addListener(\"artifactDownloadProgress\", (event) => {\n if (event.destination === destination) onProgress?.(event.bytesWritten, event.contentLength)\n })\n try {\n return await MentraOtaServer.downloadArtifact(entry.url, destination)\n } finally {\n subscription.remove()\n }\n }\n\n async teardown(): Promise<void> {\n if (this.teardownPromise) return this.teardownPromise\n this.teardownPromise = (async () => {\n if (this.hotspotRequested) {\n const hotspotStopped = await disableHotspotWithRetry(() => BluetoothSdk.setHotspotState(false), {\n // A completed APK step has just replaced ASG. Let the new command path\n // settle before asking it to tear down the SystemUI-owned access point.\n initialDelayMs: this.serverStarted ? 750 : 0,\n })\n if (!hotspotStopped) {\n console.warn(\"[OTA_PROGRESS] glasses hotspot shutdown was not confirmed after bounded retries\")\n }\n }\n if (this.serverStarted) await MentraOtaServer.stopOtaServer().catch(() => {})\n if (this.localNetworkConnected) await localNetworkTransport.disconnect().catch(() => {})\n await cleanupArtifacts().catch(() => {})\n this.prepared = []\n this.active = false\n this.serverStarted = false\n this.localNetworkConnected = false\n this.hotspotRequested = false\n })()\n try {\n await this.teardownPromise\n } finally {\n this.teardownPromise = null\n }\n }\n}\n\nexport const hotspotOtaTransport = new HotspotOtaTransport()\n"]}
1
+ {"version":3,"file":"HotspotOtaTransport.js","sourceRoot":"","sources":["../../src/services/HotspotOtaTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk, {isEnabledHotspotStatus} from \"@mentra/bluetooth-sdk\"\nimport {otaServer} from \"@mentra/bluetooth-sdk/ota-transport\"\n// One coordinator selects the native Android or iOS staging implementation at runtime.\n// eslint-disable-next-line react-native/split-platform-components\nimport {PermissionsAndroid, Platform} from \"react-native\"\nimport type {OtaCheckCurrentGlassesResult} from \"./OtaUpdateCheckService\"\nimport {\n cleanupArtifacts,\n OtaArtifactError,\n planArtifacts,\n prepareArtifacts,\n rewriteManifestForLocalServer,\n type OtaArtifactDownloadProgress,\n type PreparedOtaArtifact,\n} from \"./OtaArtifactDownloader\"\nimport {disableHotspotWithRetry} from \"./HotspotShutdown\"\nimport {localNetworkTransport} from \"./asg/localNetworkTransport\"\n\nexport type HotspotOtaPhase = \"idle\" | \"downloading\" | \"starting_hotspot\" | \"joining_hotspot\" | \"serving\"\n\nexport type HotspotOtaProgress = {\n phase: HotspotOtaPhase\n artifact?: OtaArtifactDownloadProgress\n}\n\nexport type HotspotOtaErrorCode =\n | \"hotspot_wifi_permission_denied\"\n | \"hotspot_start_failed\"\n | \"hotspot_join_failed\"\n | \"hotspot_server_failed\"\n\nexport class HotspotOtaTransportError extends Error {\n constructor(\n public readonly code: HotspotOtaErrorCode,\n message: string,\n ) {\n super(message)\n this.name = \"HotspotOtaTransportError\"\n }\n}\n\n/** Owns the single phone-side endpoint and network lease for one hotspot OTA attempt. */\nclass HotspotOtaTransport {\n private active = false\n private prepared: PreparedOtaArtifact[] = []\n private hotspotRequested = false\n private localNetworkConnected = false\n private serverStarted = false\n private teardownPromise: Promise<void> | null = null\n\n async prepare(\n checkResult: OtaCheckCurrentGlassesResult,\n onProgress?: (progress: HotspotOtaProgress) => void,\n ): Promise<string> {\n if (this.teardownPromise) await this.teardownPromise\n if (this.active) {\n throw new Error(\"A hotspot OTA transport is already active\")\n }\n if (!checkResult.manifestBody) {\n throw new Error(\"The selected OTA check has no manifest body\")\n }\n\n let phase: HotspotOtaPhase = \"downloading\"\n try {\n await this.ensureAndroidWifiPermission()\n onProgress?.({phase: \"downloading\"})\n this.prepared = await prepareArtifacts(\n planArtifacts(checkResult),\n (artifact) => onProgress?.({phase: \"downloading\", artifact}),\n Platform.OS === \"ios\" ? this.downloadIosArtifact : undefined,\n )\n\n phase = \"starting_hotspot\"\n onProgress?.({phase: \"starting_hotspot\"})\n this.hotspotRequested = true\n const hotspot = await BluetoothSdk.setHotspotState(true)\n if (!isEnabledHotspotStatus(hotspot)) {\n throw new HotspotOtaTransportError(\"hotspot_start_failed\", \"Mentra Live did not return hotspot credentials\")\n }\n phase = \"joining_hotspot\"\n onProgress?.({phase: \"joining_hotspot\"})\n const scopedAddress = await localNetworkTransport.connect(hotspot.ssid, hotspot.password)\n this.localNetworkConnected = true\n let localAddress = scopedAddress\n if (Platform.OS === \"ios\") {\n try {\n localAddress = await otaServer.waitForWifiAddress(hotspot.localIp, 15_000)\n } catch (error) {\n throw new HotspotOtaTransportError(\n \"hotspot_join_failed\",\n error instanceof Error ? error.message : String(error),\n )\n }\n }\n const artifactPaths = Object.fromEntries(this.prepared.map((artifact) => [artifact.sha256, artifact.filePath]))\n // Start exactly one native listener to learn its selected port, then atomically replace\n // the placeholder with the immutable rewritten manifest before ota_start is sent.\n const server = await otaServer.start(\"{}\", artifactPaths, localAddress)\n this.serverStarted = true\n const manifest = rewriteManifestForLocalServer(checkResult.manifestBody, this.prepared, server.baseUrl)\n const published = await otaServer.start(manifest, artifactPaths, server.host)\n if (published.manifestUrl !== server.manifestUrl) {\n throw new Error(\"Local OTA server endpoint changed while publishing the manifest\")\n }\n this.active = true\n onProgress?.({phase: \"serving\"})\n return published.manifestUrl\n } catch (error) {\n const joined = this.localNetworkConnected\n await this.teardown()\n if (error instanceof OtaArtifactError || error instanceof HotspotOtaTransportError) {\n throw error\n }\n const message = error instanceof Error ? error.message : String(error)\n if (phase === \"starting_hotspot\") {\n throw new HotspotOtaTransportError(\"hotspot_start_failed\", message)\n }\n if (phase === \"joining_hotspot\" && !joined) {\n throw new HotspotOtaTransportError(\"hotspot_join_failed\", message)\n }\n throw new HotspotOtaTransportError(\"hotspot_server_failed\", message)\n }\n }\n\n /** Android 13+ requires the nearby-WiFi runtime grant before WifiNetworkSpecifier is usable. */\n private async ensureAndroidWifiPermission(): Promise<void> {\n if (Platform.OS !== \"android\") return\n\n const permission = PermissionsAndroid.PERMISSIONS.NEARBY_WIFI_DEVICES\n if (await PermissionsAndroid.check(permission)) return\n const result = await PermissionsAndroid.request(permission)\n if (result !== PermissionsAndroid.RESULTS.GRANTED) {\n throw new HotspotOtaTransportError(\n \"hotspot_wifi_permission_denied\",\n \"Nearby devices permission is required to connect to the glasses hotspot\",\n )\n }\n }\n\n private downloadIosArtifact = async (\n entry: {url: string},\n destination: string,\n onProgress?: (bytesWritten: number, contentLength: number) => void,\n ): Promise<{statusCode: number}> => {\n const subscription = otaServer.onArtifactDownloadProgress((event) => {\n if (event.destination === destination) onProgress?.(event.bytesWritten, event.contentLength)\n })\n try {\n return await otaServer.downloadArtifact(entry.url, destination)\n } finally {\n subscription.remove()\n }\n }\n\n async teardown(): Promise<void> {\n if (this.teardownPromise) return this.teardownPromise\n this.teardownPromise = (async () => {\n if (this.hotspotRequested) {\n const hotspotStopped = await disableHotspotWithRetry(() => BluetoothSdk.setHotspotState(false), {\n // A completed APK step has just replaced ASG. Let the new command path\n // settle before asking it to tear down the SystemUI-owned access point.\n initialDelayMs: this.serverStarted ? 750 : 0,\n })\n if (!hotspotStopped) {\n console.warn(\"[OTA_PROGRESS] glasses hotspot shutdown was not confirmed after bounded retries\")\n }\n }\n if (this.serverStarted) await otaServer.stop().catch(() => {})\n if (this.localNetworkConnected) await localNetworkTransport.disconnect().catch(() => {})\n await cleanupArtifacts().catch(() => {})\n this.prepared = []\n this.active = false\n this.serverStarted = false\n this.localNetworkConnected = false\n this.hotspotRequested = false\n })()\n try {\n await this.teardownPromise\n } finally {\n this.teardownPromise = null\n }\n }\n}\n\nexport const hotspotOtaTransport = new HotspotOtaTransport()\n"]}