@mentra/engine 3.2.0-dev.168 → 3.2.0-dev.177

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.
@@ -1,10 +1,10 @@
1
1
  export const ENGINE_RELEASE_METADATA = Object.freeze({
2
2
  "schemaVersion": 1,
3
3
  "familyBaseVersion": "3.2.0",
4
- "releaseIdentity": "3.2.0-dev.168",
5
- "releaseSetId": "mentra-3.2.0-dev.168",
6
- "sourceCommit": "9a492a485727a0ebdd15ba9bb02fb92f3f102906",
7
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.168.json",
8
- "otaManifestSha256": "79e6f5d746e0dcd2a0690ddd762d261cfb3448ccae4b39e44be79ad4bae8105c"
4
+ "releaseIdentity": "3.2.0-dev.177",
5
+ "releaseSetId": "mentra-3.2.0-dev.177",
6
+ "sourceCommit": "333e6c36f33855ce6db6c192d677c36bd6316c3f",
7
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.177.json",
8
+ "otaManifestSha256": "2bde3826947a1416081eeab114f207cb182d1a1e31f43c2a6212eb1865e5ba0c"
9
9
  });
10
10
  //# sourceMappingURL=releaseMetadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.168\",\n \"releaseSetId\": \"mentra-3.2.0-dev.168\",\n \"sourceCommit\": \"9a492a485727a0ebdd15ba9bb02fb92f3f102906\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.168.json\",\n \"otaManifestSha256\": \"79e6f5d746e0dcd2a0690ddd762d261cfb3448ccae4b39e44be79ad4bae8105c\"\n})\n"]}
1
+ {"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface EngineReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.177\",\n \"releaseSetId\": \"mentra-3.2.0-dev.177\",\n \"sourceCommit\": \"333e6c36f33855ce6db6c192d677c36bd6316c3f\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.177.json\",\n \"otaManifestSha256\": \"2bde3826947a1416081eeab114f207cb182d1a1e31f43c2a6212eb1865e5ba0c\"\n})\n"]}
@@ -41,6 +41,7 @@ export interface BlobStoreHooks {
41
41
  export declare class BlobStore {
42
42
  private readonly hooks;
43
43
  private readonly uploads;
44
+ private readonly activeShareDirs;
44
45
  private readonly readers;
45
46
  /** user + package → committed usage bytes, cached; invalidated on commit/delete/clear. */
46
47
  private readonly usageCache;
@@ -1 +1 @@
1
- {"version":3,"file":"BlobStore.d.ts","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BlobStore.d.ts","sourceRoot":"","sources":["../../src/services/BlobStore.ts"],"names":[],"mappings":"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"}
@@ -45,12 +45,14 @@ const SHARE_DIR_NAME = "mentra_blob_share";
45
45
  * files sooner under storage pressure.
46
46
  */
47
47
  const SHARE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
48
+ const SHARE_LAST_USED_FILE = ".last-shared";
48
49
  const META_KEY_ROOT = "mentraos_blobmeta_";
49
50
  /** Cap md5 computation so we don't block the JS thread hashing a huge file. */
50
51
  const MD5_MAX_BYTES = 50 * 1024 * 1024;
51
52
  export class BlobStore {
52
53
  hooks;
53
54
  uploads = new Map(); // key: `${pkg} ${key}`
55
+ activeShareDirs = new Map();
54
56
  readers = new Map(); // key: host handle id
55
57
  /** user + package → committed usage bytes, cached; invalidated on commit/delete/clear. */
56
58
  usageCache = new Map();
@@ -526,16 +528,19 @@ export class BlobStore {
526
528
  return;
527
529
  const cutoff = now - SHARE_CACHE_MAX_AGE_MS;
528
530
  for (const entry of root.list()) {
529
- if (!(entry instanceof Directory))
531
+ if (!(entry instanceof Directory) || this.activeShareDirs.has(entry.uri))
530
532
  continue;
531
533
  try {
534
+ const lastUsed = new File(entry, SHARE_LAST_USED_FILE);
532
535
  const info = entry.info();
533
536
  // modificationTime is available on both platforms; creationTime can
534
537
  // be absent on Android versions before API 26. The directory id
535
538
  // begins with Date.now().toString(36), which is a final fallback.
536
539
  const encoded = entry.name.split("-", 1)[0];
537
540
  const encodedTime = /^[0-9a-z]+$/.test(encoded) ? Number.parseInt(encoded, 36) : Number.NaN;
538
- const timestamp = info.modificationTime ?? info.creationTime ?? encodedTime;
541
+ const timestamp = lastUsed.exists
542
+ ? Number(lastUsed.textSync())
543
+ : info.modificationTime ?? info.creationTime ?? encodedTime;
539
544
  if (Number.isFinite(timestamp) && timestamp < cutoff)
540
545
  entry.delete();
541
546
  }
@@ -575,6 +580,7 @@ export class BlobStore {
575
580
  // right name + extension.
576
581
  let tempDir = null;
577
582
  let cacheReady = false;
583
+ let handoffStarted = false;
578
584
  try {
579
585
  this.cleanupShareCache();
580
586
  tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, sanitizeSegment(meta.fileName));
@@ -587,6 +593,11 @@ export class BlobStore {
587
593
  file.copy(temp);
588
594
  }
589
595
  cacheReady = true;
596
+ // Persist each handoff's age, even when reusing old bytes. Directory
597
+ // modification time does not change when an existing file is shared.
598
+ new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()));
599
+ this.activeShareDirs.set(tempDir.uri, (this.activeShareDirs.get(tempDir.uri) ?? 0) + 1);
600
+ handoffStarted = true;
590
601
  await Share.open({
591
602
  url: temp.uri,
592
603
  type: meta.mimeType || OCTET,
@@ -606,6 +617,21 @@ export class BlobStore {
606
617
  }
607
618
  }
608
619
  finally {
620
+ if (tempDir && handoffStarted) {
621
+ // Give async readers a full window after the chooser returns, even
622
+ // if it stayed open longer than the retention period.
623
+ try {
624
+ new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()));
625
+ }
626
+ catch (error) {
627
+ console.warn(`${LOG_TAG}: could not refresh share retention`, error);
628
+ }
629
+ const remaining = (this.activeShareDirs.get(tempDir.uri) ?? 1) - 1;
630
+ if (remaining > 0)
631
+ this.activeShareDirs.set(tempDir.uri, remaining);
632
+ else
633
+ this.activeShareDirs.delete(tempDir.uri);
634
+ }
609
635
  // Once a reusable cache entry is ready, retain it even if this particular
610
636
  // sheet is cancelled or fails: another concurrent/recent share may still
611
637
  // be reading the same file. Only remove an entry whose preparation did
@@ -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 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
+ {"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 SHARE_LAST_USED_FILE = \".last-shared\"\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 activeShareDirs = new Map<string, number>()\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) || this.activeShareDirs.has(entry.uri)) continue\n try {\n const lastUsed = new File(entry, SHARE_LAST_USED_FILE)\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 = lastUsed.exists\n ? Number(lastUsed.textSync())\n : 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 let handoffStarted = 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 // Persist each handoff's age, even when reusing old bytes. Directory\n // modification time does not change when an existing file is shared.\n new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()))\n this.activeShareDirs.set(tempDir.uri, (this.activeShareDirs.get(tempDir.uri) ?? 0) + 1)\n handoffStarted = 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 if (tempDir && handoffStarted) {\n // Give async readers a full window after the chooser returns, even\n // if it stayed open longer than the retention period.\n try {\n new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()))\n } catch (error) {\n console.warn(`${LOG_TAG}: could not refresh share retention`, error)\n }\n const remaining = (this.activeShareDirs.get(tempDir.uri) ?? 1) - 1\n if (remaining > 0) this.activeShareDirs.set(tempDir.uri, remaining)\n else this.activeShareDirs.delete(tempDir.uri)\n }\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,4 @@
1
- import { DisplayProfile } from "./types";
1
+ import type { DisplayProfile } from "./types";
2
2
  export declare const G2_PROFILE: DisplayProfile;
3
3
  /**
4
4
  * Get the hyphen width for G2 in rendered pixels.
@@ -1 +1 @@
1
- {"version":3,"file":"g2.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/profiles/g2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAA;AActC,eAAO,MAAM,UAAU,EAAE,cAYxB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA"}
1
+ {"version":3,"file":"g2.d.ts","sourceRoot":"","sources":["../../../../src/utils/display/profiles/g2.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAA;AAsF3C,eAAO,MAAM,UAAU,EAAE,cAwBxB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA"}
@@ -1,13 +1,86 @@
1
+ import { G1_PROFILE } from "./g1";
2
+ /**
3
+ * Even Realities G2 display profile. Reuse G1's baseline metrics with
4
+ * G2-specific line limits and proportional Russian Cyrillic advances.
5
+ */
1
6
  /**
2
- * Even Realities G2 Smart Glasses Display Profile
7
+ * Russian Cyrillic advance widths in rendered pixels. Independently reproduced
8
+ * with @evenrealities/evenhub-simulator 0.9.5: render each glyph 8 and 16 times,
9
+ * subtract the alpha-bounds widths, then divide by 8 to cancel glyph bearings
10
+ * and container padding. Simulator font rendering can differ from hardware.
11
+ * Other Cyrillic characters retain the inherited 18px fallback.
3
12
  *
4
- * G2 uses the same display hardware as G1 (green monochrome, ~640x200).
5
- * Glyph widths and rendering formula are identical to G1.
6
- * The protocol differs (EvenHub protobuf vs G1 binary) but display
7
- * characteristics are the same.
13
+ * Values below are converted to raw glyph units for (g + 1) * 2. Half-unit
14
+ * raw values preserve odd rendered advances exactly.
8
15
  */
9
- // G2 uses the same glyph widths as G1 (same display hardware/font)
10
- import { G1_PROFILE } from "./g1";
16
+ const G2_CYRILLIC_RENDERED_PX = {
17
+ а: 12,
18
+ б: 12,
19
+ в: 12,
20
+ г: 9,
21
+ д: 12,
22
+ е: 11,
23
+ ё: 11,
24
+ ж: 13,
25
+ з: 11,
26
+ и: 12,
27
+ й: 12,
28
+ к: 10,
29
+ л: 12,
30
+ м: 14,
31
+ н: 12,
32
+ о: 11,
33
+ п: 11,
34
+ р: 12,
35
+ с: 11,
36
+ т: 11,
37
+ у: 11,
38
+ ф: 13,
39
+ х: 11,
40
+ ц: 12,
41
+ ч: 12,
42
+ ш: 14,
43
+ щ: 15,
44
+ ъ: 13,
45
+ ы: 14,
46
+ ь: 12,
47
+ э: 11,
48
+ ю: 14,
49
+ я: 11,
50
+ А: 13,
51
+ Б: 13,
52
+ В: 13,
53
+ Г: 10,
54
+ Д: 15,
55
+ Е: 12,
56
+ Ё: 12,
57
+ Ж: 17,
58
+ З: 12,
59
+ И: 14,
60
+ Й: 14,
61
+ К: 13,
62
+ Л: 14,
63
+ М: 16,
64
+ Н: 13,
65
+ О: 14,
66
+ П: 13,
67
+ Р: 13,
68
+ С: 13,
69
+ Т: 12,
70
+ У: 13,
71
+ Ф: 16,
72
+ Х: 13,
73
+ Ц: 13,
74
+ Ч: 12,
75
+ Ш: 16,
76
+ Щ: 17,
77
+ Ъ: 15,
78
+ Ы: 16,
79
+ Ь: 13,
80
+ Э: 13,
81
+ Ю: 17,
82
+ Я: 13,
83
+ };
11
84
  export const G2_PROFILE = {
12
85
  ...G1_PROFILE,
13
86
  id: "even-realities-g2",
@@ -20,6 +93,16 @@ export const G2_PROFILE = {
20
93
  // clean. With this set, the scene pipeline height-clips text so a box is
21
94
  // never handed more lines than fit.
22
95
  lineHeightPx: 40,
96
+ fontMetrics: {
97
+ ...G1_PROFILE.fontMetrics,
98
+ // Glyph map takes priority over uniformScripts in TextMeasurer, so the
99
+ // Cyrillic table overrides the uniform width for G2 only —
100
+ // G1 keeps its verified uniform 18px untouched.
101
+ glyphWidths: new Map([
102
+ ...G1_PROFILE.fontMetrics.glyphWidths,
103
+ ...Object.entries(G2_CYRILLIC_RENDERED_PX).map(([char, renderedPx]) => [char, renderedPx / 2 - 1]),
104
+ ]),
105
+ },
23
106
  };
24
107
  /**
25
108
  * Get the hyphen width for G2 in rendered pixels.
@@ -1 +1 @@
1
- {"version":3,"file":"g2.js","sourceRoot":"","sources":["../../../../src/utils/display/profiles/g2.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,mEAAmE;AACnE,OAAO,EAAC,UAAU,EAAC,MAAM,MAAM,CAAA;AAE/B,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,GAAG,UAAU;IACb,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,mBAAmB;IACzB,sEAAsE;IACtE,mDAAmD;IACnD,QAAQ,EAAE,CAAC;IACX,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,oCAAoC;IACpC,YAAY,EAAE,EAAE;CACjB,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAEpC;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAA","sourcesContent":["import {DisplayProfile} from \"./types\"\n\n/**\n * Even Realities G2 Smart Glasses Display Profile\n *\n * G2 uses the same display hardware as G1 (green monochrome, ~640x200).\n * Glyph widths and rendering formula are identical to G1.\n * The protocol differs (EvenHub protobuf vs G1 binary) but display\n * characteristics are the same.\n */\n\n// G2 uses the same glyph widths as G1 (same display hardware/font)\nimport {G1_PROFILE} from \"./g1\"\n\nexport const G2_PROFILE: DisplayProfile = {\n ...G1_PROFILE,\n id: \"even-realities-g2\",\n name: \"Even Realities G2\",\n // G2 fits more vertical text than G1 — allow up to 8 lines before the\n // wrapper truncates (G1 stays at its inherited 5).\n maxLines: 8,\n // Hardware-calibrated 2026-07-03: a 28px container overflowed one line of\n // the firmware font (triggering the fw's overflow indicator tick); 40px is\n // clean. With this set, the scene pipeline height-clips text so a box is\n // never handed more lines than fit.\n lineHeightPx: 40,\n}\n\n/**\n * Get the hyphen width for G2 in rendered pixels.\n * Same as G1: Hyphen glyph = 4px → rendered = (4+1)*2 = 10px\n */\nexport const G2_HYPHEN_WIDTH_PX = 10\n\n/**\n * Get the space width for G2 in rendered pixels.\n * Same as G1: Space glyph = 2px → rendered = (2+1)*2 = 6px\n */\nexport const G2_SPACE_WIDTH_PX = 6\n"]}
1
+ {"version":3,"file":"g2.js","sourceRoot":"","sources":["../../../../src/utils/display/profiles/g2.ts"],"names":[],"mappings":"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","sourcesContent":["import {G1_PROFILE} from \"./g1\"\nimport type {DisplayProfile} from \"./types\"\n\n/**\n * Even Realities G2 display profile. Reuse G1's baseline metrics with\n * G2-specific line limits and proportional Russian Cyrillic advances.\n */\n\n/**\n * Russian Cyrillic advance widths in rendered pixels. Independently reproduced\n * with @evenrealities/evenhub-simulator 0.9.5: render each glyph 8 and 16 times,\n * subtract the alpha-bounds widths, then divide by 8 to cancel glyph bearings\n * and container padding. Simulator font rendering can differ from hardware.\n * Other Cyrillic characters retain the inherited 18px fallback.\n *\n * Values below are converted to raw glyph units for (g + 1) * 2. Half-unit\n * raw values preserve odd rendered advances exactly.\n */\nconst G2_CYRILLIC_RENDERED_PX: Record<string, number> = {\n а: 12,\n б: 12,\n в: 12,\n г: 9,\n д: 12,\n е: 11,\n ё: 11,\n ж: 13,\n з: 11,\n и: 12,\n й: 12,\n к: 10,\n л: 12,\n м: 14,\n н: 12,\n о: 11,\n п: 11,\n р: 12,\n с: 11,\n т: 11,\n у: 11,\n ф: 13,\n х: 11,\n ц: 12,\n ч: 12,\n ш: 14,\n щ: 15,\n ъ: 13,\n ы: 14,\n ь: 12,\n э: 11,\n ю: 14,\n я: 11,\n А: 13,\n Б: 13,\n В: 13,\n Г: 10,\n Д: 15,\n Е: 12,\n Ё: 12,\n Ж: 17,\n З: 12,\n И: 14,\n Й: 14,\n К: 13,\n Л: 14,\n М: 16,\n Н: 13,\n О: 14,\n П: 13,\n Р: 13,\n С: 13,\n Т: 12,\n У: 13,\n Ф: 16,\n Х: 13,\n Ц: 13,\n Ч: 12,\n Ш: 16,\n Щ: 17,\n Ъ: 15,\n Ы: 16,\n Ь: 13,\n Э: 13,\n Ю: 17,\n Я: 13,\n}\n\nexport const G2_PROFILE: DisplayProfile = {\n ...G1_PROFILE,\n id: \"even-realities-g2\",\n name: \"Even Realities G2\",\n // G2 fits more vertical text than G1 — allow up to 8 lines before the\n // wrapper truncates (G1 stays at its inherited 5).\n maxLines: 8,\n // Hardware-calibrated 2026-07-03: a 28px container overflowed one line of\n // the firmware font (triggering the fw's overflow indicator tick); 40px is\n // clean. With this set, the scene pipeline height-clips text so a box is\n // never handed more lines than fit.\n lineHeightPx: 40,\n fontMetrics: {\n ...G1_PROFILE.fontMetrics,\n // Glyph map takes priority over uniformScripts in TextMeasurer, so the\n // Cyrillic table overrides the uniform width for G2 only —\n // G1 keeps its verified uniform 18px untouched.\n glyphWidths: new Map<string, number>([\n ...G1_PROFILE.fontMetrics.glyphWidths,\n ...Object.entries(G2_CYRILLIC_RENDERED_PX).map(\n ([char, renderedPx]) => [char, renderedPx / 2 - 1] as [string, number],\n ),\n ]),\n },\n}\n\n/**\n * Get the hyphen width for G2 in rendered pixels.\n * Same as G1: Hyphen glyph = 4px → rendered = (4+1)*2 = 10px\n */\nexport const G2_HYPHEN_WIDTH_PX = 10\n\n/**\n * Get the space width for G2 in rendered pixels.\n * Same as G1: Space glyph = 2px → rendered = (2+1)*2 = 6px\n */\nexport const G2_SPACE_WIDTH_PX = 6\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/engine",
3
- "version": "3.2.0-dev.168",
3
+ "version": "3.2.0-dev.177",
4
4
  "description": "Mentra Engine — miniapp registry, WebView bridge, and miniapp store",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -97,12 +97,12 @@
97
97
  "registry": "https://registry.npmjs.org/"
98
98
  },
99
99
  "dependencies": {
100
- "@mentra/acs-meeting": "3.2.0-dev.168",
101
- "@mentra/bluetooth-sdk": "3.2.0-dev.168",
102
- "@mentra/cloud-client": "3.2.0-dev.168",
103
- "@mentra/cloud-protocol": "3.2.0-dev.168",
104
- "@mentra/crust": "3.2.0-dev.168",
105
- "@mentra/miniapp": "3.2.0-dev.168",
100
+ "@mentra/acs-meeting": "3.2.0-dev.177",
101
+ "@mentra/bluetooth-sdk": "3.2.0-dev.177",
102
+ "@mentra/cloud-client": "3.2.0-dev.177",
103
+ "@mentra/cloud-protocol": "3.2.0-dev.177",
104
+ "@mentra/crust": "3.2.0-dev.177",
105
+ "@mentra/miniapp": "3.2.0-dev.177",
106
106
  "buffer": "^6.0.3",
107
107
  "events": "^3.3.0",
108
108
  "react-native-marked": "8.1.1",
@@ -12,9 +12,9 @@ export interface EngineReleaseMetadata {
12
12
  export const ENGINE_RELEASE_METADATA: Readonly<EngineReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
14
  "familyBaseVersion": "3.2.0",
15
- "releaseIdentity": "3.2.0-dev.168",
16
- "releaseSetId": "mentra-3.2.0-dev.168",
17
- "sourceCommit": "9a492a485727a0ebdd15ba9bb02fb92f3f102906",
18
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.168.json",
19
- "otaManifestSha256": "79e6f5d746e0dcd2a0690ddd762d261cfb3448ccae4b39e44be79ad4bae8105c"
15
+ "releaseIdentity": "3.2.0-dev.177",
16
+ "releaseSetId": "mentra-3.2.0-dev.177",
17
+ "sourceCommit": "333e6c36f33855ce6db6c192d677c36bd6316c3f",
18
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.177.json",
19
+ "otaManifestSha256": "2bde3826947a1416081eeab114f207cb182d1a1e31f43c2a6212eb1865e5ba0c"
20
20
  })
@@ -49,6 +49,7 @@ const SHARE_DIR_NAME = "mentra_blob_share"
49
49
  * files sooner under storage pressure.
50
50
  */
51
51
  const SHARE_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000
52
+ const SHARE_LAST_USED_FILE = ".last-shared"
52
53
  const META_KEY_ROOT = "mentraos_blobmeta_"
53
54
  /** Cap md5 computation so we don't block the JS thread hashing a huge file. */
54
55
  const MD5_MAX_BYTES = 50 * 1024 * 1024
@@ -103,6 +104,7 @@ interface ActiveReader {
103
104
 
104
105
  export class BlobStore {
105
106
  private readonly uploads = new Map<string, ActiveUpload>() // key: `${pkg} ${key}`
107
+ private readonly activeShareDirs = new Map<string, number>()
106
108
  private readonly readers = new Map<string, ActiveReader>() // key: host handle id
107
109
  /** user + package → committed usage bytes, cached; invalidated on commit/delete/clear. */
108
110
  private readonly usageCache = new Map<string, number>()
@@ -612,15 +614,18 @@ export class BlobStore {
612
614
  if (!root.exists) return
613
615
  const cutoff = now - SHARE_CACHE_MAX_AGE_MS
614
616
  for (const entry of root.list()) {
615
- if (!(entry instanceof Directory)) continue
617
+ if (!(entry instanceof Directory) || this.activeShareDirs.has(entry.uri)) continue
616
618
  try {
619
+ const lastUsed = new File(entry, SHARE_LAST_USED_FILE)
617
620
  const info = entry.info()
618
621
  // modificationTime is available on both platforms; creationTime can
619
622
  // be absent on Android versions before API 26. The directory id
620
623
  // begins with Date.now().toString(36), which is a final fallback.
621
624
  const encoded = entry.name.split("-", 1)[0]
622
625
  const encodedTime = /^[0-9a-z]+$/.test(encoded) ? Number.parseInt(encoded, 36) : Number.NaN
623
- const timestamp = info.modificationTime ?? info.creationTime ?? encodedTime
626
+ const timestamp = lastUsed.exists
627
+ ? Number(lastUsed.textSync())
628
+ : info.modificationTime ?? info.creationTime ?? encodedTime
624
629
  if (Number.isFinite(timestamp) && timestamp < cutoff) entry.delete()
625
630
  } catch {
626
631
  // Keep scanning if one cache entry disappears or is unreadable.
@@ -658,6 +663,7 @@ export class BlobStore {
658
663
  // right name + extension.
659
664
  let tempDir: Directory | null = null
660
665
  let cacheReady = false
666
+ let handoffStarted = false
661
667
  try {
662
668
  this.cleanupShareCache()
663
669
  tempDir = new Directory(Paths.cache, SHARE_DIR_NAME, sanitizeSegment(meta.fileName))
@@ -668,6 +674,11 @@ export class BlobStore {
668
674
  file.copy(temp)
669
675
  }
670
676
  cacheReady = true
677
+ // Persist each handoff's age, even when reusing old bytes. Directory
678
+ // modification time does not change when an existing file is shared.
679
+ new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()))
680
+ this.activeShareDirs.set(tempDir.uri, (this.activeShareDirs.get(tempDir.uri) ?? 0) + 1)
681
+ handoffStarted = true
671
682
  await Share.open({
672
683
  url: temp.uri,
673
684
  type: meta.mimeType || OCTET,
@@ -684,6 +695,18 @@ export class BlobStore {
684
695
  this.hooks.sendResult(packageName, requestId, true, {success: false})
685
696
  }
686
697
  } finally {
698
+ if (tempDir && handoffStarted) {
699
+ // Give async readers a full window after the chooser returns, even
700
+ // if it stayed open longer than the retention period.
701
+ try {
702
+ new File(tempDir, SHARE_LAST_USED_FILE).write(String(Date.now()))
703
+ } catch (error) {
704
+ console.warn(`${LOG_TAG}: could not refresh share retention`, error)
705
+ }
706
+ const remaining = (this.activeShareDirs.get(tempDir.uri) ?? 1) - 1
707
+ if (remaining > 0) this.activeShareDirs.set(tempDir.uri, remaining)
708
+ else this.activeShareDirs.delete(tempDir.uri)
709
+ }
687
710
  // Once a reusable cache entry is ready, retain it even if this particular
688
711
  // sheet is cancelled or fails: another concurrent/recent share may still
689
712
  // be reading the same file. Only remove an entry whose preparation did
@@ -1,16 +1,89 @@
1
- import {DisplayProfile} from "./types"
1
+ import {G1_PROFILE} from "./g1"
2
+ import type {DisplayProfile} from "./types"
2
3
 
3
4
  /**
4
- * Even Realities G2 Smart Glasses Display Profile
5
- *
6
- * G2 uses the same display hardware as G1 (green monochrome, ~640x200).
7
- * Glyph widths and rendering formula are identical to G1.
8
- * The protocol differs (EvenHub protobuf vs G1 binary) but display
9
- * characteristics are the same.
5
+ * Even Realities G2 display profile. Reuse G1's baseline metrics with
6
+ * G2-specific line limits and proportional Russian Cyrillic advances.
10
7
  */
11
8
 
12
- // G2 uses the same glyph widths as G1 (same display hardware/font)
13
- import {G1_PROFILE} from "./g1"
9
+ /**
10
+ * Russian Cyrillic advance widths in rendered pixels. Independently reproduced
11
+ * with @evenrealities/evenhub-simulator 0.9.5: render each glyph 8 and 16 times,
12
+ * subtract the alpha-bounds widths, then divide by 8 to cancel glyph bearings
13
+ * and container padding. Simulator font rendering can differ from hardware.
14
+ * Other Cyrillic characters retain the inherited 18px fallback.
15
+ *
16
+ * Values below are converted to raw glyph units for (g + 1) * 2. Half-unit
17
+ * raw values preserve odd rendered advances exactly.
18
+ */
19
+ const G2_CYRILLIC_RENDERED_PX: Record<string, number> = {
20
+ а: 12,
21
+ б: 12,
22
+ в: 12,
23
+ г: 9,
24
+ д: 12,
25
+ е: 11,
26
+ ё: 11,
27
+ ж: 13,
28
+ з: 11,
29
+ и: 12,
30
+ й: 12,
31
+ к: 10,
32
+ л: 12,
33
+ м: 14,
34
+ н: 12,
35
+ о: 11,
36
+ п: 11,
37
+ р: 12,
38
+ с: 11,
39
+ т: 11,
40
+ у: 11,
41
+ ф: 13,
42
+ х: 11,
43
+ ц: 12,
44
+ ч: 12,
45
+ ш: 14,
46
+ щ: 15,
47
+ ъ: 13,
48
+ ы: 14,
49
+ ь: 12,
50
+ э: 11,
51
+ ю: 14,
52
+ я: 11,
53
+ А: 13,
54
+ Б: 13,
55
+ В: 13,
56
+ Г: 10,
57
+ Д: 15,
58
+ Е: 12,
59
+ Ё: 12,
60
+ Ж: 17,
61
+ З: 12,
62
+ И: 14,
63
+ Й: 14,
64
+ К: 13,
65
+ Л: 14,
66
+ М: 16,
67
+ Н: 13,
68
+ О: 14,
69
+ П: 13,
70
+ Р: 13,
71
+ С: 13,
72
+ Т: 12,
73
+ У: 13,
74
+ Ф: 16,
75
+ Х: 13,
76
+ Ц: 13,
77
+ Ч: 12,
78
+ Ш: 16,
79
+ Щ: 17,
80
+ Ъ: 15,
81
+ Ы: 16,
82
+ Ь: 13,
83
+ Э: 13,
84
+ Ю: 17,
85
+ Я: 13,
86
+ }
14
87
 
15
88
  export const G2_PROFILE: DisplayProfile = {
16
89
  ...G1_PROFILE,
@@ -24,6 +97,18 @@ export const G2_PROFILE: DisplayProfile = {
24
97
  // clean. With this set, the scene pipeline height-clips text so a box is
25
98
  // never handed more lines than fit.
26
99
  lineHeightPx: 40,
100
+ fontMetrics: {
101
+ ...G1_PROFILE.fontMetrics,
102
+ // Glyph map takes priority over uniformScripts in TextMeasurer, so the
103
+ // Cyrillic table overrides the uniform width for G2 only —
104
+ // G1 keeps its verified uniform 18px untouched.
105
+ glyphWidths: new Map<string, number>([
106
+ ...G1_PROFILE.fontMetrics.glyphWidths,
107
+ ...Object.entries(G2_CYRILLIC_RENDERED_PX).map(
108
+ ([char, renderedPx]) => [char, renderedPx / 2 - 1] as [string, number],
109
+ ),
110
+ ]),
111
+ },
27
112
  }
28
113
 
29
114
  /**