@glassly/miniapp 0.3.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/README.md +275 -0
  2. package/dist/background/index.d.ts +64 -0
  3. package/dist/background/index.d.ts.map +1 -0
  4. package/dist/background/index.js +26 -0
  5. package/dist/background/index.js.map +1 -0
  6. package/dist/background/register.d.ts +39 -0
  7. package/dist/background/register.d.ts.map +1 -0
  8. package/dist/background/register.js +72 -0
  9. package/dist/background/register.js.map +1 -0
  10. package/dist/dev-reload.d.ts +21 -0
  11. package/dist/dev-reload.d.ts.map +1 -0
  12. package/dist/dev-reload.js +60 -0
  13. package/dist/dev-reload.js.map +1 -0
  14. package/dist/envelope.d.ts +23 -0
  15. package/dist/envelope.d.ts.map +1 -0
  16. package/dist/envelope.js +47 -0
  17. package/dist/envelope.js.map +1 -0
  18. package/dist/globals.d.ts +54 -0
  19. package/dist/globals.d.ts.map +1 -0
  20. package/dist/globals.js +12 -0
  21. package/dist/globals.js.map +1 -0
  22. package/dist/hardware.d.ts +38 -0
  23. package/dist/hardware.d.ts.map +1 -0
  24. package/dist/hardware.js +31 -0
  25. package/dist/hardware.js.map +1 -0
  26. package/dist/index.d.ts +65 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +34 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/modules/actions.d.ts +81 -0
  31. package/dist/modules/actions.d.ts.map +1 -0
  32. package/dist/modules/actions.js +137 -0
  33. package/dist/modules/actions.js.map +1 -0
  34. package/dist/modules/ai.d.ts +211 -0
  35. package/dist/modules/ai.d.ts.map +1 -0
  36. package/dist/modules/ai.js +117 -0
  37. package/dist/modules/ai.js.map +1 -0
  38. package/dist/modules/auth.d.ts +25 -0
  39. package/dist/modules/auth.d.ts.map +1 -0
  40. package/dist/modules/auth.js +52 -0
  41. package/dist/modules/auth.js.map +1 -0
  42. package/dist/modules/base64.d.ts +16 -0
  43. package/dist/modules/base64.d.ts.map +1 -0
  44. package/dist/modules/base64.js +92 -0
  45. package/dist/modules/base64.js.map +1 -0
  46. package/dist/modules/blob.d.ts +161 -0
  47. package/dist/modules/blob.d.ts.map +1 -0
  48. package/dist/modules/blob.js +277 -0
  49. package/dist/modules/blob.js.map +1 -0
  50. package/dist/modules/camera.d.ts +183 -0
  51. package/dist/modules/camera.d.ts.map +1 -0
  52. package/dist/modules/camera.js +119 -0
  53. package/dist/modules/camera.js.map +1 -0
  54. package/dist/modules/cloud.d.ts +34 -0
  55. package/dist/modules/cloud.d.ts.map +1 -0
  56. package/dist/modules/cloud.js +70 -0
  57. package/dist/modules/cloud.js.map +1 -0
  58. package/dist/modules/dashboard.d.ts +24 -0
  59. package/dist/modules/dashboard.d.ts.map +1 -0
  60. package/dist/modules/dashboard.js +39 -0
  61. package/dist/modules/dashboard.js.map +1 -0
  62. package/dist/modules/display.d.ts +167 -0
  63. package/dist/modules/display.d.ts.map +1 -0
  64. package/dist/modules/display.js +83 -0
  65. package/dist/modules/display.js.map +1 -0
  66. package/dist/modules/events.d.ts +178 -0
  67. package/dist/modules/events.d.ts.map +1 -0
  68. package/dist/modules/events.js +168 -0
  69. package/dist/modules/events.js.map +1 -0
  70. package/dist/modules/glasses.d.ts +36 -0
  71. package/dist/modules/glasses.d.ts.map +1 -0
  72. package/dist/modules/glasses.js +52 -0
  73. package/dist/modules/glasses.js.map +1 -0
  74. package/dist/modules/heading.d.ts +16 -0
  75. package/dist/modules/heading.d.ts.map +1 -0
  76. package/dist/modules/heading.js +20 -0
  77. package/dist/modules/heading.js.map +1 -0
  78. package/dist/modules/imu.d.ts +31 -0
  79. package/dist/modules/imu.d.ts.map +1 -0
  80. package/dist/modules/imu.js +40 -0
  81. package/dist/modules/imu.js.map +1 -0
  82. package/dist/modules/input.d.ts +34 -0
  83. package/dist/modules/input.d.ts.map +1 -0
  84. package/dist/modules/input.js +50 -0
  85. package/dist/modules/input.js.map +1 -0
  86. package/dist/modules/keys.d.ts +30 -0
  87. package/dist/modules/keys.d.ts.map +1 -0
  88. package/dist/modules/keys.js +37 -0
  89. package/dist/modules/keys.js.map +1 -0
  90. package/dist/modules/languages.d.ts +42 -0
  91. package/dist/modules/languages.d.ts.map +1 -0
  92. package/dist/modules/languages.js +57 -0
  93. package/dist/modules/languages.js.map +1 -0
  94. package/dist/modules/led.d.ts +35 -0
  95. package/dist/modules/led.d.ts.map +1 -0
  96. package/dist/modules/led.js +39 -0
  97. package/dist/modules/led.js.map +1 -0
  98. package/dist/modules/location.d.ts +27 -0
  99. package/dist/modules/location.d.ts.map +1 -0
  100. package/dist/modules/location.js +35 -0
  101. package/dist/modules/location.js.map +1 -0
  102. package/dist/modules/memory.d.ts +126 -0
  103. package/dist/modules/memory.d.ts.map +1 -0
  104. package/dist/modules/memory.js +141 -0
  105. package/dist/modules/memory.js.map +1 -0
  106. package/dist/modules/mic.d.ts +61 -0
  107. package/dist/modules/mic.d.ts.map +1 -0
  108. package/dist/modules/mic.js +94 -0
  109. package/dist/modules/mic.js.map +1 -0
  110. package/dist/modules/miniapps.d.ts +82 -0
  111. package/dist/modules/miniapps.d.ts.map +1 -0
  112. package/dist/modules/miniapps.js +58 -0
  113. package/dist/modules/miniapps.js.map +1 -0
  114. package/dist/modules/navigation.d.ts +332 -0
  115. package/dist/modules/navigation.d.ts.map +1 -0
  116. package/dist/modules/navigation.js +387 -0
  117. package/dist/modules/navigation.js.map +1 -0
  118. package/dist/modules/permissions.d.ts +58 -0
  119. package/dist/modules/permissions.d.ts.map +1 -0
  120. package/dist/modules/permissions.js +66 -0
  121. package/dist/modules/permissions.js.map +1 -0
  122. package/dist/modules/phone.d.ts +95 -0
  123. package/dist/modules/phone.d.ts.map +1 -0
  124. package/dist/modules/phone.js +110 -0
  125. package/dist/modules/phone.js.map +1 -0
  126. package/dist/modules/pivots/engine.d.ts +169 -0
  127. package/dist/modules/pivots/engine.d.ts.map +1 -0
  128. package/dist/modules/pivots/engine.js +778 -0
  129. package/dist/modules/pivots/engine.js.map +1 -0
  130. package/dist/modules/pivots/geometry.d.ts +84 -0
  131. package/dist/modules/pivots/geometry.d.ts.map +1 -0
  132. package/dist/modules/pivots/geometry.js +371 -0
  133. package/dist/modules/pivots/geometry.js.map +1 -0
  134. package/dist/modules/pivots/instructions.d.ts +139 -0
  135. package/dist/modules/pivots/instructions.d.ts.map +1 -0
  136. package/dist/modules/pivots/instructions.js +281 -0
  137. package/dist/modules/pivots/instructions.js.map +1 -0
  138. package/dist/modules/speaker.d.ts +175 -0
  139. package/dist/modules/speaker.d.ts.map +1 -0
  140. package/dist/modules/speaker.js +249 -0
  141. package/dist/modules/speaker.js.map +1 -0
  142. package/dist/modules/speakers.d.ts +29 -0
  143. package/dist/modules/speakers.d.ts.map +1 -0
  144. package/dist/modules/speakers.js +38 -0
  145. package/dist/modules/speakers.js.map +1 -0
  146. package/dist/modules/storage.d.ts +50 -0
  147. package/dist/modules/storage.d.ts.map +1 -0
  148. package/dist/modules/storage.js +99 -0
  149. package/dist/modules/storage.js.map +1 -0
  150. package/dist/modules/stream.d.ts +133 -0
  151. package/dist/modules/stream.d.ts.map +1 -0
  152. package/dist/modules/stream.js +53 -0
  153. package/dist/modules/stream.js.map +1 -0
  154. package/dist/modules/system.d.ts +71 -0
  155. package/dist/modules/system.d.ts.map +1 -0
  156. package/dist/modules/system.js +65 -0
  157. package/dist/modules/system.js.map +1 -0
  158. package/dist/modules/transcription.d.ts +99 -0
  159. package/dist/modules/transcription.d.ts.map +1 -0
  160. package/dist/modules/transcription.js +126 -0
  161. package/dist/modules/transcription.js.map +1 -0
  162. package/dist/modules/translation.d.ts +65 -0
  163. package/dist/modules/translation.d.ts.map +1 -0
  164. package/dist/modules/translation.js +121 -0
  165. package/dist/modules/translation.js.map +1 -0
  166. package/dist/modules/ui.d.ts +186 -0
  167. package/dist/modules/ui.d.ts.map +1 -0
  168. package/dist/modules/ui.js +289 -0
  169. package/dist/modules/ui.js.map +1 -0
  170. package/dist/protocol.d.ts +384 -0
  171. package/dist/protocol.d.ts.map +1 -0
  172. package/dist/protocol.js +413 -0
  173. package/dist/protocol.js.map +1 -0
  174. package/dist/react/MiniappHeader.d.ts +44 -0
  175. package/dist/react/MiniappHeader.d.ts.map +1 -0
  176. package/dist/react/MiniappHeader.js +32 -0
  177. package/dist/react/MiniappHeader.js.map +1 -0
  178. package/dist/react/MiniappProvider.d.ts +33 -0
  179. package/dist/react/MiniappProvider.d.ts.map +1 -0
  180. package/dist/react/MiniappProvider.js +15 -0
  181. package/dist/react/MiniappProvider.js.map +1 -0
  182. package/dist/react/capsuleHeaderLayout.d.ts +3 -0
  183. package/dist/react/capsuleHeaderLayout.d.ts.map +1 -0
  184. package/dist/react/capsuleHeaderLayout.js +5 -0
  185. package/dist/react/capsuleHeaderLayout.js.map +1 -0
  186. package/dist/react/index.d.ts +13 -0
  187. package/dist/react/index.d.ts.map +1 -0
  188. package/dist/react/index.js +9 -0
  189. package/dist/react/index.js.map +1 -0
  190. package/dist/react/useCapabilities.d.ts +26 -0
  191. package/dist/react/useCapabilities.d.ts.map +1 -0
  192. package/dist/react/useCapabilities.js +27 -0
  193. package/dist/react/useCapabilities.js.map +1 -0
  194. package/dist/react/useCapsuleHeaderStyle.d.ts +27 -0
  195. package/dist/react/useCapsuleHeaderStyle.d.ts.map +1 -0
  196. package/dist/react/useCapsuleHeaderStyle.js +39 -0
  197. package/dist/react/useCapsuleHeaderStyle.js.map +1 -0
  198. package/dist/react/useColorScheme.d.ts +18 -0
  199. package/dist/react/useColorScheme.d.ts.map +1 -0
  200. package/dist/react/useColorScheme.js +24 -0
  201. package/dist/react/useColorScheme.js.map +1 -0
  202. package/dist/react/useConnected.d.ts +17 -0
  203. package/dist/react/useConnected.d.ts.map +1 -0
  204. package/dist/react/useConnected.js +39 -0
  205. package/dist/react/useConnected.js.map +1 -0
  206. package/dist/react/useRpc.d.ts +29 -0
  207. package/dist/react/useRpc.d.ts.map +1 -0
  208. package/dist/react/useRpc.js +82 -0
  209. package/dist/react/useRpc.js.map +1 -0
  210. package/dist/react/useSafeArea.d.ts +21 -0
  211. package/dist/react/useSafeArea.d.ts.map +1 -0
  212. package/dist/react/useSafeArea.js +22 -0
  213. package/dist/react/useSafeArea.js.map +1 -0
  214. package/dist/session.d.ts +355 -0
  215. package/dist/session.d.ts.map +1 -0
  216. package/dist/session.js +688 -0
  217. package/dist/session.js.map +1 -0
  218. package/dist/sub-path-types.test-d.d.ts +40 -0
  219. package/dist/sub-path-types.test-d.d.ts.map +1 -0
  220. package/dist/sub-path-types.test-d.js +37 -0
  221. package/dist/sub-path-types.test-d.js.map +1 -0
  222. package/dist/transport/auto.d.ts +35 -0
  223. package/dist/transport/auto.d.ts.map +1 -0
  224. package/dist/transport/auto.js +124 -0
  225. package/dist/transport/auto.js.map +1 -0
  226. package/dist/transport/dispatch.d.ts +43 -0
  227. package/dist/transport/dispatch.d.ts.map +1 -0
  228. package/dist/transport/dispatch.js +91 -0
  229. package/dist/transport/dispatch.js.map +1 -0
  230. package/dist/transport/local-socket.d.ts +28 -0
  231. package/dist/transport/local-socket.d.ts.map +1 -0
  232. package/dist/transport/local-socket.js +80 -0
  233. package/dist/transport/local-socket.js.map +1 -0
  234. package/dist/transport/mock.d.ts +54 -0
  235. package/dist/transport/mock.d.ts.map +1 -0
  236. package/dist/transport/mock.js +210 -0
  237. package/dist/transport/mock.js.map +1 -0
  238. package/dist/transport/postmessage.d.ts +36 -0
  239. package/dist/transport/postmessage.d.ts.map +1 -0
  240. package/dist/transport/postmessage.js +78 -0
  241. package/dist/transport/postmessage.js.map +1 -0
  242. package/dist/transport/types.d.ts +25 -0
  243. package/dist/transport/types.d.ts.map +1 -0
  244. package/dist/transport/types.js +11 -0
  245. package/dist/transport/types.js.map +1 -0
  246. package/dist/ui/index.d.ts +107 -0
  247. package/dist/ui/index.d.ts.map +1 -0
  248. package/dist/ui/index.js +32 -0
  249. package/dist/ui/index.js.map +1 -0
  250. package/package.json +84 -0
  251. package/src/background/index.ts +177 -0
  252. package/src/background/register.ts +89 -0
  253. package/src/dev-reload.ts +59 -0
  254. package/src/envelope.ts +54 -0
  255. package/src/globals.ts +62 -0
  256. package/src/hardware.ts +40 -0
  257. package/src/index.ts +216 -0
  258. package/src/modules/actions.ts +189 -0
  259. package/src/modules/ai.ts +281 -0
  260. package/src/modules/auth.ts +68 -0
  261. package/src/modules/base64.ts +87 -0
  262. package/src/modules/blob.ts +341 -0
  263. package/src/modules/camera.ts +258 -0
  264. package/src/modules/cloud.ts +94 -0
  265. package/src/modules/dashboard.ts +51 -0
  266. package/src/modules/display.ts +207 -0
  267. package/src/modules/events.ts +318 -0
  268. package/src/modules/glasses.ts +58 -0
  269. package/src/modules/heading.ts +23 -0
  270. package/src/modules/imu.ts +44 -0
  271. package/src/modules/input.ts +66 -0
  272. package/src/modules/keys.ts +46 -0
  273. package/src/modules/languages.ts +72 -0
  274. package/src/modules/led.ts +61 -0
  275. package/src/modules/location.ts +39 -0
  276. package/src/modules/memory.ts +224 -0
  277. package/src/modules/mic.ts +103 -0
  278. package/src/modules/miniapps.ts +103 -0
  279. package/src/modules/navigation.ts +674 -0
  280. package/src/modules/permissions.ts +83 -0
  281. package/src/modules/phone.ts +155 -0
  282. package/src/modules/pivots/engine.ts +863 -0
  283. package/src/modules/pivots/geometry.ts +409 -0
  284. package/src/modules/pivots/instructions.ts +341 -0
  285. package/src/modules/speaker.ts +347 -0
  286. package/src/modules/speakers.ts +40 -0
  287. package/src/modules/storage.ts +109 -0
  288. package/src/modules/stream.ts +168 -0
  289. package/src/modules/system.ts +113 -0
  290. package/src/modules/transcription.ts +180 -0
  291. package/src/modules/translation.ts +156 -0
  292. package/src/modules/ui.ts +481 -0
  293. package/src/protocol.ts +475 -0
  294. package/src/react/MiniappHeader.tsx +114 -0
  295. package/src/react/MiniappProvider.tsx +50 -0
  296. package/src/react/capsuleHeaderLayout.ts +11 -0
  297. package/src/react/index.ts +12 -0
  298. package/src/react/useCapabilities.ts +29 -0
  299. package/src/react/useCapsuleHeaderStyle.ts +55 -0
  300. package/src/react/useColorScheme.ts +26 -0
  301. package/src/react/useConnected.ts +47 -0
  302. package/src/react/useRpc.ts +103 -0
  303. package/src/react/useSafeArea.ts +36 -0
  304. package/src/session.ts +969 -0
  305. package/src/sub-path-types.test-d.ts +60 -0
  306. package/src/transport/auto.ts +143 -0
  307. package/src/transport/dispatch.ts +106 -0
  308. package/src/transport/local-socket.ts +91 -0
  309. package/src/transport/mock.ts +255 -0
  310. package/src/transport/postmessage.ts +95 -0
  311. package/src/transport/types.ts +26 -0
  312. package/src/ui/index.ts +129 -0
@@ -0,0 +1,688 @@
1
+ /**
2
+ * @fileoverview MiniappSession — central session object for a local miniapp.
3
+ *
4
+ * Owns the transport, the request/response correlation map, the readiness queue,
5
+ * the PONG auto-reply, the visibility state, and all per-module instances.
6
+ *
7
+ * Lifecycle:
8
+ * const session = new MiniappSession()
9
+ * await session.connect() // sends CONNECT, resolves on CONNECT_ACK
10
+ * session.display.render([...])
11
+ * ...
12
+ * session.disconnect()
13
+ */
14
+ import { EventEmitter } from "eventemitter3";
15
+ import { makeRequestId, parseEnvelope, serializeEnvelope } from "./envelope";
16
+ import { getGlasslyGlobals } from "./globals";
17
+ import { MiniappErrorCode, MiniappRequestType, MiniappResponseType } from "./protocol";
18
+ import { createTransport } from "./transport/auto";
19
+ import { CameraModule } from "./modules/camera";
20
+ import { AuthModule } from "./modules/auth";
21
+ import { CloudModule } from "./modules/cloud";
22
+ import { DashboardAPI } from "./modules/dashboard";
23
+ import { DisplayManager } from "./modules/display";
24
+ import { EventManager } from "./modules/events";
25
+ import { GlassesModule } from "./modules/glasses";
26
+ import { HeadingModule } from "./modules/heading";
27
+ import { AiModule } from "./modules/ai";
28
+ import { ImuModule } from "./modules/imu";
29
+ import { InputModule } from "./modules/input";
30
+ import { KeysModule } from "./modules/keys";
31
+ import { LedModule } from "./modules/led";
32
+ import { LocationModule } from "./modules/location";
33
+ import { MemoryModule } from "./modules/memory";
34
+ import { SpeakersModule } from "./modules/speakers";
35
+ import { MicModule } from "./modules/mic";
36
+ import { NavigationModule } from "./modules/navigation";
37
+ import { PermissionsModule } from "./modules/permissions";
38
+ import { PhoneModule } from "./modules/phone";
39
+ import { TranscriptionModule } from "./modules/transcription";
40
+ import { TranslationModule } from "./modules/translation";
41
+ import { UIModuleImpl } from "./modules/ui";
42
+ import { SimpleStorage } from "./modules/storage";
43
+ import { SpeakerModule } from "./modules/speaker";
44
+ import { StreamModule } from "./modules/stream";
45
+ import { SystemModule } from "./modules/system";
46
+ import { MiniappsModule } from "./modules/miniapps";
47
+ import { ActionsModule } from "./modules/actions";
48
+ import { BlobModule } from "./modules/blob";
49
+ const ALL_PERMISSION_TYPES = [
50
+ "location",
51
+ "microphone",
52
+ "camera",
53
+ "notifications",
54
+ "calendar",
55
+ "apiKeys",
56
+ "memory",
57
+ "ai",
58
+ "speakers",
59
+ ];
60
+ export class NotConnectedError extends Error {
61
+ constructor(message = "MiniappSession is not connected") {
62
+ super(message);
63
+ this.code = MiniappErrorCode.NOT_CONNECTED;
64
+ this.name = "NotConnectedError";
65
+ }
66
+ }
67
+ /**
68
+ * What a request promise actually rejects with: a real Error (so `${err}`,
69
+ * `err.message`, and stack traces behave) that still carries the host's
70
+ * `code` for callers matching on MiniappRequestError's shape. Before this
71
+ * class, requests rejected with the bare `{code, message}` wire object and
72
+ * miniapp logs printed "[object Object]".
73
+ */
74
+ export class MiniappRequestFailure extends Error {
75
+ constructor(code, message) {
76
+ super(message);
77
+ this.code = code;
78
+ this.name = "MiniappRequestFailure";
79
+ }
80
+ }
81
+ const DEFAULT_CONNECT_TIMEOUT_MS = 10000;
82
+ // Hard ceiling on how long a single bridge request waits for the host's
83
+ // REQUEST_RESULT. Without it, a host that never responds (a hung cloud call, a
84
+ // GPS fix that never arrives, a native handler that stalls) leaves the request
85
+ // promise pending FOREVER — which is what stalled navigation at "Starting…"
86
+ // (the controller's `starting` guard never reset because start() never settled).
87
+ // 60s is generous: it covers a slow route computation while still guaranteeing
88
+ // the promise eventually rejects so callers can roll back / surface an error.
89
+ const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
90
+ // The default preserves the pre-channel-registry behavior for code that creates
91
+ // or accepts a bare MiniappSession. `registerMiniapp<Channels>` supplies the
92
+ // concrete mapping for scaffolded miniapps.
93
+ export class MiniappSession {
94
+ constructor(options = {}) {
95
+ /** Phone-declared glasses capabilities. Null until CONNECT_ACK arrives. */
96
+ this.capabilities = null;
97
+ this.userId = "";
98
+ this.packageName = "";
99
+ this.visibility = "foreground";
100
+ /** Host color scheme. Seeded from window.Glassly, updated via session events. */
101
+ this.colorScheme = "light";
102
+ /** True after CONNECT_ACK. Observe with waitForReady() or the "ready" event. */
103
+ this.ready = false;
104
+ this.emitter = new EventEmitter();
105
+ this.authState = null;
106
+ this.authWaiters = new Set();
107
+ this.authRefreshPromise = null;
108
+ /**
109
+ * Outbound queue for anything sent before CONNECT_ACK. Flushed in FIFO order
110
+ * once the phone responds with CONNECT_ACK.
111
+ */
112
+ this.outboundQueue = [];
113
+ this.pendingRequests = new Map();
114
+ this.connectPromise = null;
115
+ this.disposed = false;
116
+ /** Manifest-declared permission cache. Updated on CONNECT_ACK / PERMISSIONS_UPDATE. */
117
+ this._permissions = {
118
+ location: false,
119
+ microphone: false,
120
+ camera: false,
121
+ notifications: false,
122
+ calendar: false,
123
+ apiKeys: false,
124
+ memory: false,
125
+ ai: false,
126
+ speakers: false,
127
+ };
128
+ this.transport = createTransport(options);
129
+ this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
130
+ const injected = getGlasslyGlobals();
131
+ this.packageName = options.packageName ?? injected.packageName ?? "";
132
+ if (injected.colorScheme === "light" || injected.colorScheme === "dark") {
133
+ this.colorScheme = injected.colorScheme;
134
+ }
135
+ this.auth = new AuthModule(this);
136
+ this.events = new EventManager(this);
137
+ this.speaker = new SpeakerModule(this);
138
+ this.camera = new CameraModule(this);
139
+ this.cloud = new CloudModule(this);
140
+ this.dashboard = new DashboardAPI(this);
141
+ this.display = new DisplayManager(this);
142
+ this.glasses = new GlassesModule(this);
143
+ this.heading = new HeadingModule(this);
144
+ this.imu = new ImuModule(this);
145
+ this.input = new InputModule(this);
146
+ this.ai = new AiModule(this);
147
+ this.keys = new KeysModule(this);
148
+ this.led = new LedModule(this);
149
+ this.location = new LocationModule(this);
150
+ this.memory = new MemoryModule(this);
151
+ this.speakers = new SpeakersModule(this);
152
+ this.mic = new MicModule(this);
153
+ this.navigation = new NavigationModule(this);
154
+ this.permissions = new PermissionsModule(this);
155
+ this.phone = new PhoneModule(this);
156
+ this.storage = new SimpleStorage(this);
157
+ this.blob = new BlobModule(this);
158
+ this.stream = new StreamModule(this);
159
+ this.system = new SystemModule(this);
160
+ this.transcription = new TranscriptionModule(this);
161
+ this.translation = new TranslationModule(this);
162
+ this.ui = new UIModuleImpl(this);
163
+ this.miniapps = new MiniappsModule(this);
164
+ this.actions = new ActionsModule(this);
165
+ }
166
+ /**
167
+ * @internal — synchronous lookup against the cached manifest-declared
168
+ * permission record from CONNECT_ACK / PERMISSIONS_UPDATE. Domain modules
169
+ * use this to expose their `hasPermission` getters without going to the
170
+ * wire. Returns false until CONNECT_ACK arrives.
171
+ *
172
+ * `manifestKey` is the manifest's UPPER_CASE permission name
173
+ * (MICROPHONE, CAMERA, LOCATION, READ_NOTIFICATIONS, etc.). Maps to v3's
174
+ * lowercase canonical keys internally.
175
+ */
176
+ _hasManifestPermission(manifestKey) {
177
+ const canonical = manifestKeyToCanonical(manifestKey);
178
+ if (!canonical)
179
+ return false;
180
+ return this._permissions[canonical] === true;
181
+ }
182
+ /**
183
+ * @internal — read the current manifest-declared permission record.
184
+ * Powers session.permissions.getAll(). Returns a fresh shallow copy so
185
+ * callers can't mutate internal state.
186
+ */
187
+ _getPermissions() {
188
+ return { ...this._permissions };
189
+ }
190
+ /** @internal — current miniapp-scoped backend auth, if the host provided one. */
191
+ _getAuth() {
192
+ return this.authState ? { ...this.authState } : null;
193
+ }
194
+ /**
195
+ * @internal — wait for a scoped miniapp token. Used by session.auth; not part
196
+ * of the public SDK surface because authors should never manage wire events.
197
+ */
198
+ _waitForAuth(minTtlMs, timeoutMs = 10000) {
199
+ const current = this.authState;
200
+ if (current && this.authHasTtl(current, minTtlMs)) {
201
+ return Promise.resolve({ ...current });
202
+ }
203
+ void this.requestAuthRefresh(minTtlMs);
204
+ return new Promise((resolve, reject) => {
205
+ const waiter = {
206
+ minTtlMs,
207
+ resolve,
208
+ reject,
209
+ timer: setTimeout(() => {
210
+ this.authWaiters.delete(waiter);
211
+ reject(new NotConnectedError("Miniapp auth token is not available"));
212
+ }, timeoutMs),
213
+ };
214
+ this.authWaiters.add(waiter);
215
+ });
216
+ }
217
+ /**
218
+ * @internal — subscribe to a raw stream type. Domain modules call this; it
219
+ * delegates to the EventManager registry. Underscore prefix signals "not
220
+ * part of the public SDK surface — use session.mic.onAudioChunk /
221
+ * session.transcription.on(...)
222
+ * etc. instead."
223
+ */
224
+ _subscribe(streamType, handler, options = {}) {
225
+ return this.events.subscribe(streamType, handler, options);
226
+ }
227
+ // -------------------------------------------------------------------------
228
+ // Connection lifecycle
229
+ // -------------------------------------------------------------------------
230
+ /**
231
+ * Connect to LocalMiniappRuntime. Idempotent — calling multiple times
232
+ * returns the same Promise.
233
+ */
234
+ connect() {
235
+ if (this.disposed) {
236
+ return Promise.reject(new NotConnectedError("MiniappSession was disposed"));
237
+ }
238
+ if (this.connectPromise)
239
+ return this.connectPromise;
240
+ // Register the readiness listener BEFORE any awaits so that a synchronous
241
+ // CONNECT_ACK delivery in tests (or the phone) can't race the subscription.
242
+ const readyPromise = new Promise((resolve, reject) => {
243
+ const timer = setTimeout(() => {
244
+ const err = new Error("MiniappSession: CONNECT_ACK timeout");
245
+ this.failAllPending(new MiniappRequestFailure(MiniappErrorCode.NOT_CONNECTED, err.message));
246
+ this.emitter.emit("error", err);
247
+ reject(err);
248
+ }, this.connectTimeoutMs);
249
+ this.emitter.once("ready", () => {
250
+ clearTimeout(timer);
251
+ resolve();
252
+ });
253
+ this.emitter.once("error", (err) => {
254
+ clearTimeout(timer);
255
+ reject(err);
256
+ });
257
+ });
258
+ this.connectPromise = (async () => {
259
+ this.transport.onMessage((raw) => this.handleIncoming(raw));
260
+ this.transport.onDisconnect((reason) => this.handleTransportDisconnect(reason));
261
+ await this.transport.open();
262
+ const requestId = makeRequestId();
263
+ const connectPayload = {
264
+ type: MiniappRequestType.CONNECT,
265
+ packageName: this.packageName,
266
+ };
267
+ this.transport.send(serializeEnvelope({ payload: connectPayload, requestId }));
268
+ await readyPromise;
269
+ })();
270
+ return this.connectPromise;
271
+ }
272
+ /** Resolves when `ready` becomes true, or rejects if connect failed. */
273
+ waitForReady() {
274
+ if (this.ready)
275
+ return Promise.resolve();
276
+ return this.connect();
277
+ }
278
+ isConnected() {
279
+ return this.ready && this.transport.isOpen();
280
+ }
281
+ disconnect() {
282
+ if (this.disposed)
283
+ return;
284
+ this.disposed = true;
285
+ // Give listeners one synchronous chance to flush final messages
286
+ // (e.g. display.render([])) before we tear down the transport.
287
+ try {
288
+ this.emitter.emit("beforeDisconnect", "disconnect called");
289
+ }
290
+ catch (err) {
291
+ // A throwing handler must not block teardown.
292
+ console.warn("[MiniappSession] beforeDisconnect handler threw:", err);
293
+ }
294
+ this.failAllPending(new MiniappRequestFailure(MiniappErrorCode.REQUEST_ABORTED, "Session disconnected"));
295
+ try {
296
+ this.transport.close();
297
+ }
298
+ catch {
299
+ // ignore
300
+ }
301
+ this.ready = false;
302
+ this.emitter.emit("disconnect", "disconnect called");
303
+ }
304
+ // -------------------------------------------------------------------------
305
+ // Outbound traffic — modules call these
306
+ // -------------------------------------------------------------------------
307
+ /** Send a fire-and-forget request that does not need a response. */
308
+ sendOneShot(payload) {
309
+ const envelope = { payload };
310
+ this.enqueueOrSend(serializeEnvelope(envelope));
311
+ }
312
+ /**
313
+ * Send a request and get a Promise that resolves with the REQUEST_RESULT payload.
314
+ * Rejects with a MiniappRequestError if the phone returns an error result.
315
+ *
316
+ * `opts.timeoutMs` overrides the default request timeout. Pass `0` to disable
317
+ * it entirely for inherently long-running requests whose duration is unbounded
318
+ * (e.g. audio playback that resolves only when the clip finishes) — those still
319
+ * settle via REQUEST_RESULT or `failAllPending` on disconnect, so they can't
320
+ * leak. Most requests should keep the default ceiling.
321
+ */
322
+ sendRequest(payload, opts) {
323
+ if (this.disposed) {
324
+ return Promise.reject(new NotConnectedError());
325
+ }
326
+ const requestId = makeRequestId();
327
+ const envelope = { payload, requestId };
328
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
329
+ return new Promise((resolve, reject) => {
330
+ // Reject (and drop) the request if the host never sends a REQUEST_RESULT,
331
+ // so the promise can't hang forever. The REQUEST_RESULT / failAllPending
332
+ // paths clear this timer before settling. A non-positive timeout opts out
333
+ // (long-running requests rely on the host result / disconnect to settle).
334
+ const timer = timeoutMs > 0
335
+ ? setTimeout(() => {
336
+ const pending = this.pendingRequests.get(requestId);
337
+ if (!pending)
338
+ return;
339
+ this.pendingRequests.delete(requestId);
340
+ pending.reject(new MiniappRequestFailure(MiniappErrorCode.ACTION_TIMEOUT, "Request timed out waiting for a response from the host"));
341
+ }, timeoutMs)
342
+ : undefined;
343
+ this.pendingRequests.set(requestId, {
344
+ requestId,
345
+ resolve: resolve,
346
+ reject,
347
+ timer,
348
+ });
349
+ this.enqueueOrSend(serializeEnvelope(envelope));
350
+ });
351
+ }
352
+ // -------------------------------------------------------------------------
353
+ // Event emitter — external API
354
+ // -------------------------------------------------------------------------
355
+ on(event, handler) {
356
+ this.emitter.on(event, handler);
357
+ return () => this.emitter.off(event, handler);
358
+ }
359
+ off(event, handler) {
360
+ this.emitter.off(event, handler);
361
+ }
362
+ /**
363
+ * Last-chance hook before the transport closes. Fires either when the
364
+ * phone notifies the session of an imminent disconnect (~50ms grace
365
+ * window before the socket is torn down) or when this session's
366
+ * `disconnect()` is called locally. Use it to flush final cleanup
367
+ * messages — e.g. `display.render([])` — synchronously. Async work
368
+ * started here will not complete before the socket closes.
369
+ */
370
+ onBeforeDisconnect(handler) {
371
+ return this.on("beforeDisconnect", handler);
372
+ }
373
+ onVisibilityChange(handler) {
374
+ return this.on("visibility", handler);
375
+ }
376
+ onCapabilitiesChange(handler) {
377
+ return this.on("capabilities", handler);
378
+ }
379
+ onColorSchemeChange(handler) {
380
+ return this.on("colorScheme", handler);
381
+ }
382
+ // -------------------------------------------------------------------------
383
+ // Internal — transport glue
384
+ // -------------------------------------------------------------------------
385
+ enqueueOrSend(raw) {
386
+ if (this.ready) {
387
+ try {
388
+ this.transport.send(raw);
389
+ }
390
+ catch (err) {
391
+ // If send fails post-ready, treat as transport error.
392
+ this.emitter.emit("error", err);
393
+ }
394
+ return;
395
+ }
396
+ this.outboundQueue.push(raw);
397
+ }
398
+ flushQueue() {
399
+ const queue = this.outboundQueue.splice(0);
400
+ for (const raw of queue) {
401
+ try {
402
+ this.transport.send(raw);
403
+ }
404
+ catch (err) {
405
+ this.emitter.emit("error", err);
406
+ }
407
+ }
408
+ }
409
+ handleIncoming(raw) {
410
+ const envelope = parseEnvelope(raw);
411
+ if (!envelope)
412
+ return;
413
+ const payload = envelope.payload;
414
+ const type = payload?.type;
415
+ switch (type) {
416
+ case MiniappResponseType.CONNECT_ACK: {
417
+ const ack = payload;
418
+ this.userId = ack.userId ?? "";
419
+ if (ack.packageName)
420
+ this.packageName = ack.packageName;
421
+ this.capabilities = ack.capabilities ?? null;
422
+ if (ack.visibility)
423
+ this.visibility = ack.visibility;
424
+ if (ack.colorScheme === "light" || ack.colorScheme === "dark") {
425
+ this.colorScheme = ack.colorScheme;
426
+ }
427
+ if (ack.auth) {
428
+ this.applyAuth(ack.auth);
429
+ if (!this.userId)
430
+ this.userId = ack.auth.glasslyUserId;
431
+ }
432
+ // Populate the manifest-declared permission cache. Older runtimes
433
+ // that don't send `permissions` leave the all-false default in place
434
+ // — `hasPermission` getters will simply return false.
435
+ if (ack.permissions)
436
+ this.applyPermissions(ack.permissions);
437
+ this.ready = true;
438
+ this.flushQueue();
439
+ this.emitter.emit("ready");
440
+ // Don't resolve request correlation here — CONNECT_ACK has no requestId.
441
+ return;
442
+ }
443
+ case MiniappResponseType.AUTH_UPDATE: {
444
+ const next = payload.auth;
445
+ if (next) {
446
+ this.applyAuth(next);
447
+ if (!this.userId)
448
+ this.userId = next.glasslyUserId;
449
+ }
450
+ return;
451
+ }
452
+ case MiniappResponseType.PERMISSIONS_UPDATE: {
453
+ const next = payload.permissions;
454
+ if (next)
455
+ this.applyPermissions(next);
456
+ return;
457
+ }
458
+ case MiniappResponseType.SPEAKER_STATE: {
459
+ const state = payload.state;
460
+ if (!state)
461
+ return;
462
+ const event = {
463
+ state,
464
+ errorCode: payload.errorCode,
465
+ errorMessage: payload.errorMessage,
466
+ durationMs: payload.durationMs,
467
+ };
468
+ this.speaker._applyState(event);
469
+ this.emitter.emit("speakerState", event);
470
+ return;
471
+ }
472
+ case MiniappRequestType.PING: {
473
+ // Phone → miniapp keepalive ping. Auto-reply with PONG.
474
+ const pong = { type: MiniappResponseType.PONG };
475
+ const env = {
476
+ payload: pong,
477
+ ...(envelope.requestId ? { requestId: envelope.requestId } : {}),
478
+ };
479
+ try {
480
+ this.transport.send(serializeEnvelope(env));
481
+ }
482
+ catch {
483
+ // Ignore; next ping will fail too and runtime will unregister.
484
+ }
485
+ return;
486
+ }
487
+ case MiniappResponseType.EVENT: {
488
+ const streamType = payload.streamType;
489
+ if (!streamType)
490
+ return;
491
+ this.events._forwardEvent(streamType, payload.data, payload.transcriptionRoute);
492
+ return;
493
+ }
494
+ case MiniappResponseType.ACTION_CALL: {
495
+ // Another miniapp invoked one of our declared actions. Route to the
496
+ // registered handler (or buffer briefly for one to register). The SDK
497
+ // replies with an ACTION_RESULT request keyed by callId.
498
+ const callId = payload.callId;
499
+ const actionId = payload.actionId;
500
+ if (!callId || !actionId)
501
+ return;
502
+ const params = payload.params ?? {};
503
+ const callerPackageName = payload.callerPackageName ?? "";
504
+ this.actions._deliver(callId, actionId, params, { callerPackageName });
505
+ return;
506
+ }
507
+ case MiniappResponseType.CAPABILITIES_UPDATE: {
508
+ const cap = payload.capabilities ?? null;
509
+ this.capabilities = cap;
510
+ this.emitter.emit("capabilities", cap);
511
+ return;
512
+ }
513
+ case MiniappResponseType.VISIBILITY_CHANGE: {
514
+ const next = payload.visibility;
515
+ if (next === "foreground" || next === "background") {
516
+ this.visibility = next;
517
+ this.emitter.emit("visibility", next);
518
+ }
519
+ return;
520
+ }
521
+ case MiniappResponseType.COLOR_SCHEME_CHANGE: {
522
+ const next = payload.colorScheme;
523
+ if (next === "light" || next === "dark") {
524
+ this.colorScheme = next;
525
+ this.emitter.emit("colorScheme", next);
526
+ }
527
+ return;
528
+ }
529
+ case MiniappResponseType.REQUEST_RESULT: {
530
+ const requestId = envelope.requestId;
531
+ if (!requestId)
532
+ return;
533
+ const pending = this.pendingRequests.get(requestId);
534
+ if (!pending)
535
+ return;
536
+ this.pendingRequests.delete(requestId);
537
+ if (pending.timer)
538
+ clearTimeout(pending.timer);
539
+ if (payload.ok === false) {
540
+ const wire = payload.error;
541
+ pending.reject(new MiniappRequestFailure(wire?.code ?? MiniappErrorCode.INTERNAL, wire?.message ?? "Unknown error"));
542
+ }
543
+ else {
544
+ pending.resolve(payload.data ?? null);
545
+ }
546
+ return;
547
+ }
548
+ case MiniappResponseType.WILL_DISCONNECT: {
549
+ const reason = payload.reason ?? "phone unregistering";
550
+ try {
551
+ this.emitter.emit("beforeDisconnect", reason);
552
+ }
553
+ catch (err) {
554
+ console.warn("[MiniappSession] beforeDisconnect handler threw:", err);
555
+ }
556
+ return;
557
+ }
558
+ case MiniappResponseType.ERROR: {
559
+ const err = new Error(payload.message ?? "MiniappSession error");
560
+ this.emitter.emit("error", err);
561
+ return;
562
+ }
563
+ default:
564
+ // Unknown type — drop silently. Forward-compat.
565
+ return;
566
+ }
567
+ }
568
+ handleTransportDisconnect(reason) {
569
+ this.ready = false;
570
+ this.failAllPending(new MiniappRequestFailure(MiniappErrorCode.NOT_CONNECTED, `Transport disconnected: ${reason}`));
571
+ this.emitter.emit("disconnect", reason);
572
+ }
573
+ failAllPending(error) {
574
+ for (const pending of this.pendingRequests.values()) {
575
+ if (pending.timer)
576
+ clearTimeout(pending.timer);
577
+ pending.reject(error);
578
+ }
579
+ this.pendingRequests.clear();
580
+ // Auth waiters live outside pendingRequests — they're resolved by an
581
+ // AUTH_UPDATE push, not a correlated REQUEST_RESULT — so a transport drop,
582
+ // dispose, or CONNECT_ACK timeout must reject them here too. Otherwise an
583
+ // in-flight session.auth.getToken()/fetch() hangs until its 10s waiter
584
+ // timeout instead of failing fast with the disconnect error.
585
+ for (const waiter of Array.from(this.authWaiters)) {
586
+ clearTimeout(waiter.timer);
587
+ this.authWaiters.delete(waiter);
588
+ waiter.reject(new NotConnectedError(error.message));
589
+ }
590
+ }
591
+ authHasTtl(auth, minTtlMs) {
592
+ return auth.expiresAt - Date.now() > minTtlMs;
593
+ }
594
+ requestAuthRefresh(minTtlMs) {
595
+ if (this.authRefreshPromise)
596
+ return this.authRefreshPromise;
597
+ if (!this.ready || !this.transport.isOpen())
598
+ return Promise.resolve(null);
599
+ this.authRefreshPromise = this.sendRequest({
600
+ type: MiniappRequestType.AUTH_REFRESH,
601
+ minTtlMs,
602
+ })
603
+ .then((result) => {
604
+ const auth = result?.auth;
605
+ if (auth)
606
+ this.applyAuth(auth);
607
+ return auth ?? null;
608
+ })
609
+ .catch((err) => {
610
+ console.warn("[MiniappSession] auth refresh failed:", err);
611
+ return null;
612
+ })
613
+ .finally(() => {
614
+ this.authRefreshPromise = null;
615
+ });
616
+ return this.authRefreshPromise;
617
+ }
618
+ applyAuth(next) {
619
+ this.authState = { ...next };
620
+ for (const waiter of Array.from(this.authWaiters)) {
621
+ if (!this.authHasTtl(next, waiter.minTtlMs))
622
+ continue;
623
+ clearTimeout(waiter.timer);
624
+ this.authWaiters.delete(waiter);
625
+ waiter.resolve({ ...next });
626
+ }
627
+ this.emitter.emit("auth", { ...next });
628
+ }
629
+ /**
630
+ * Update the cached permission record. Idempotent: emits "permissions"
631
+ * only when the record actually changed. Sanitizes incoming objects to
632
+ * the v3 PermissionType union.
633
+ */
634
+ applyPermissions(next) {
635
+ let changed = false;
636
+ const updated = { ...this._permissions };
637
+ for (const k of ALL_PERMISSION_TYPES) {
638
+ const v = next[k] === true;
639
+ if (updated[k] !== v) {
640
+ updated[k] = v;
641
+ changed = true;
642
+ }
643
+ }
644
+ if (changed) {
645
+ this._permissions = updated;
646
+ this.emitter.emit("permissions", { ...updated });
647
+ }
648
+ }
649
+ }
650
+ /**
651
+ * Map a manifest UPPER_CASE permission name to the lowercase canonical key
652
+ * used by `session.permissions`. Returns null for unknown manifest keys.
653
+ *
654
+ * BACKGROUND_LOCATION + POST_NOTIFICATIONS map onto the same canonical keys
655
+ * as their non-suffixed counterparts (location / notifications) since
656
+ * `has()` is "do I have *any* form of this permission declared".
657
+ */
658
+ function manifestKeyToCanonical(manifestKey) {
659
+ switch (manifestKey.toUpperCase()) {
660
+ case "MICROPHONE":
661
+ return "microphone";
662
+ case "CAMERA":
663
+ return "camera";
664
+ case "LOCATION":
665
+ case "BACKGROUND_LOCATION":
666
+ return "location";
667
+ case "READ_NOTIFICATIONS":
668
+ case "POST_NOTIFICATIONS":
669
+ return "notifications";
670
+ case "CALENDAR":
671
+ return "calendar";
672
+ case "API_KEYS":
673
+ return "apiKeys";
674
+ case "MEMORY":
675
+ return "memory";
676
+ case "AI":
677
+ return "ai";
678
+ case "SPEAKERS":
679
+ return "speakers";
680
+ default:
681
+ return null;
682
+ }
683
+ }
684
+ /** @internal — for the permissions module's onUpdate plumbing. */
685
+ export function _allPermissionTypes() {
686
+ return ALL_PERMISSION_TYPES;
687
+ }
688
+ //# sourceMappingURL=session.js.map