@mentra/miniapp 0.3.0-beta.0 → 0.3.0-beta.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 (50) hide show
  1. package/README.md +4 -2
  2. package/dist/background/index.d.ts +3 -1
  3. package/dist/background/index.d.ts.map +1 -1
  4. package/dist/background/index.js +1 -0
  5. package/dist/background/index.js.map +1 -1
  6. package/dist/background/register.d.ts +3 -2
  7. package/dist/background/register.d.ts.map +1 -1
  8. package/dist/background/register.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +3 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/modules/camera.d.ts +18 -5
  14. package/dist/modules/camera.d.ts.map +1 -1
  15. package/dist/modules/camera.js +5 -2
  16. package/dist/modules/camera.js.map +1 -1
  17. package/dist/modules/languages.d.ts +42 -0
  18. package/dist/modules/languages.d.ts.map +1 -0
  19. package/dist/modules/languages.js +57 -0
  20. package/dist/modules/languages.js.map +1 -0
  21. package/dist/modules/speaker.d.ts +1 -0
  22. package/dist/modules/speaker.d.ts.map +1 -1
  23. package/dist/modules/speaker.js +7 -8
  24. package/dist/modules/speaker.js.map +1 -1
  25. package/dist/modules/transcription.d.ts +26 -7
  26. package/dist/modules/transcription.d.ts.map +1 -1
  27. package/dist/modules/transcription.js +28 -8
  28. package/dist/modules/transcription.js.map +1 -1
  29. package/dist/modules/translation.d.ts +9 -3
  30. package/dist/modules/translation.d.ts.map +1 -1
  31. package/dist/modules/translation.js +5 -3
  32. package/dist/modules/translation.js.map +1 -1
  33. package/dist/session.d.ts +2 -2
  34. package/dist/session.d.ts.map +1 -1
  35. package/dist/session.js +4 -0
  36. package/dist/session.js.map +1 -1
  37. package/dist/sub-path-types.test-d.d.ts +12 -0
  38. package/dist/sub-path-types.test-d.d.ts.map +1 -1
  39. package/dist/sub-path-types.test-d.js.map +1 -1
  40. package/package.json +2 -1
  41. package/src/background/index.ts +8 -1
  42. package/src/background/register.ts +10 -3
  43. package/src/index.ts +10 -0
  44. package/src/modules/camera.ts +22 -6
  45. package/src/modules/languages.ts +72 -0
  46. package/src/modules/speaker.ts +10 -2
  47. package/src/modules/transcription.ts +43 -13
  48. package/src/modules/translation.ts +22 -8
  49. package/src/session.ts +7 -3
  50. package/src/sub-path-types.test-d.ts +12 -1
@@ -21,11 +21,18 @@
21
21
  import {MiniappRequestType, MiniappStreamType} from "../protocol"
22
22
  import {MiniappSession} from "../session"
23
23
  import type {TranscriptionData, UnsubscribeFn} from "./events"
24
+ import {requireLanguageHint, requireTranscriptionLanguage} from "./languages"
25
+ import type {LanguageHint, TranscriptionLanguage} from "./languages"
24
26
 
25
27
  /** Configuration for cloud-side transcription behavior. */
26
28
  export interface TranscriptionConfig {
27
- /** ISO 639-1 language hints to improve detection accuracy (e.g. ["en", "ja"]). */
28
- languageHints?: string[]
29
+ /**
30
+ * Language hints to improve auto-detection accuracy. Bare ISO 639-1 codes
31
+ * from the registry (e.g. ["en", "ja"]); registry BCP-47 tags are accepted
32
+ * and normalized ("en-US" -> "en"). Anything else throws
33
+ * `MiniappValidationError`.
34
+ */
35
+ languageHints?: LanguageHint[]
29
36
  /** Custom vocabulary / boosted terms (e.g. ["MentraOS", "HIPAA"]). */
30
37
  vocabulary?: string[]
31
38
  /** Enable speaker diarisation. Defaults vary by provider. */
@@ -65,15 +72,24 @@ export class TranscriptionModule {
65
72
  * Subscribe to transcription for one or more specific languages. Each call
66
73
  * is independent; multiple can be active simultaneously.
67
74
  *
68
- * @param language - BCP-47 tag(s), e.g. `"en-US"` or `["en-US", "es-ES"]`.
75
+ * Languages are validated against the platform registry
76
+ * (`SUPPORTED_TRANSCRIPTION_LANGUAGES`): registry BCP-47 tags pass through,
77
+ * bare registry codes canonicalize ("fr" -> "fr-FR"), anything else throws
78
+ * `MiniappValidationError` immediately — an invalid language must never
79
+ * become a subscription that silently transcribes the wrong thing
80
+ * (OS-1746 / issue 021).
81
+ *
82
+ * @param language - Registry tag(s), e.g. `"en-US"` or `["en-US", "es-ES"]`.
69
83
  * @param handler - Called for every event in any of the listed languages.
70
84
  */
71
85
  forLanguage(
72
- language: string | string[],
86
+ language: TranscriptionLanguage | TranscriptionLanguage[],
73
87
  handler: (data: TranscriptionData) => void,
74
88
  options: TranscriptionOptions = {},
75
89
  ): UnsubscribeFn {
76
- const langs = Array.isArray(language) ? language : [language]
90
+ const langs = (Array.isArray(language) ? language : [language]).map((lang) =>
91
+ requireTranscriptionLanguage(lang, "transcription.forLanguage(language)"),
92
+ )
77
93
  if (langs.length === 0) return () => {}
78
94
 
79
95
  const unsubs: UnsubscribeFn[] = []
@@ -100,15 +116,29 @@ export class TranscriptionModule {
100
116
 
101
117
  /**
102
118
  * Apply transcription configuration (language hints, custom vocabulary,
103
- * diarisation toggle). Sent to the cloud immediately. Cached locally so we
104
- * could re-send on reconnect (future).
119
+ * diarisation toggle). Hints are validated against the registry and
120
+ * normalized to bare codes ("en-US" -> "en"); unknown values throw
121
+ * `MiniappValidationError` synchronously.
122
+ *
123
+ * Sent to the phone runtime as a REQUEST (not a one-shot) so the returned
124
+ * promise rejects if the runtime cannot apply it — a config that the
125
+ * platform ignores must be loud, not silent (issue 021 S3). Cached locally
126
+ * so the runtime can re-apply on reconnect.
105
127
  */
106
- configure(config: TranscriptionConfig): void {
107
- this.currentConfig = {...config}
108
- this.session.sendOneShot({
109
- type: MiniappRequestType.TRANSCRIPTION_CONFIG,
110
- config: {...config},
111
- })
128
+ configure(config: TranscriptionConfig): Promise<void> {
129
+ const normalized: TranscriptionConfig = {
130
+ ...config,
131
+ languageHints: config.languageHints?.map((hint) =>
132
+ requireLanguageHint(hint, "transcription.configure(languageHints)"),
133
+ ),
134
+ }
135
+ this.currentConfig = {...normalized}
136
+ return this.session
137
+ .sendRequest<void>({
138
+ type: MiniappRequestType.TRANSCRIPTION_CONFIG,
139
+ config: {...normalized},
140
+ })
141
+ .then(() => undefined)
112
142
  }
113
143
 
114
144
  /** Tear down every transcription subscription this module owns. */
@@ -20,9 +20,17 @@
20
20
  import {MiniappStreamType} from "../protocol"
21
21
  import {MiniappSession} from "../session"
22
22
  import type {TranslationData, UnsubscribeFn} from "./events"
23
+ import {requireTranscriptionLanguage} from "./languages"
24
+ import type {TranscriptionLanguage} from "./languages"
23
25
 
24
26
  export type TranslationHandler = (data: TranslationData) => void
25
27
 
28
+ /**
29
+ * Translation source: a registry language, or "auto" to let the cloud detect
30
+ * the spoken language.
31
+ */
32
+ export type TranslationSource = TranscriptionLanguage | "auto"
33
+
26
34
  export class TranslationModule {
27
35
  private readonly unsubs = new Set<UnsubscribeFn>()
28
36
 
@@ -51,8 +59,10 @@ export class TranslationModule {
51
59
  * regardless of source. Useful for "I'm a Spanish speaker, translate
52
60
  * whatever I hear to Spanish."
53
61
  */
54
- to(target: string | string[], handler: TranslationHandler): UnsubscribeFn {
55
- const targets = Array.isArray(target) ? target : [target]
62
+ to(target: TranscriptionLanguage | TranscriptionLanguage[], handler: TranslationHandler): UnsubscribeFn {
63
+ const targets = (Array.isArray(target) ? target : [target]).map((t) =>
64
+ requireTranscriptionLanguage(t, "translation.to(target)"),
65
+ )
56
66
  if (targets.length === 0) return () => {}
57
67
  const unsubs: UnsubscribeFn[] = []
58
68
  for (const t of targets) {
@@ -81,17 +91,21 @@ export class TranslationModule {
81
91
  * from the same source.
82
92
  */
83
93
  fromTo(
84
- source: string,
85
- target: string | string[],
94
+ source: TranslationSource,
95
+ target: TranscriptionLanguage | TranscriptionLanguage[],
86
96
  handler: TranslationHandler,
87
97
  ): UnsubscribeFn {
88
- const targets = Array.isArray(target) ? target : [target]
98
+ const validSource =
99
+ source === "auto" ? "auto" : requireTranscriptionLanguage(source, "translation.fromTo(source)")
100
+ const targets = (Array.isArray(target) ? target : [target]).map((t) =>
101
+ requireTranscriptionLanguage(t, "translation.fromTo(target)"),
102
+ )
89
103
  if (targets.length === 0) return () => {}
90
104
  const unsubs: UnsubscribeFn[] = []
91
105
  for (const t of targets) {
92
106
  unsubs.push(
93
107
  this.session._subscribe(
94
- `${MiniappStreamType.TRANSLATION}:${source}:${t}`,
108
+ `${MiniappStreamType.TRANSLATION}:${validSource}:${t}`,
95
109
  handler as (data: unknown) => void,
96
110
  ),
97
111
  )
@@ -113,8 +127,8 @@ export class TranslationModule {
113
127
  * SDK v3 parity. This alias will be removed in a future release.
114
128
  */
115
129
  forLanguagePair(
116
- fromLang: string,
117
- toLang: string,
130
+ fromLang: TranslationSource,
131
+ toLang: TranscriptionLanguage,
118
132
  handler: TranslationHandler,
119
133
  ): UnsubscribeFn {
120
134
  return this.fromTo(fromLang, toLang, handler)
package/src/session.ts CHANGED
@@ -194,7 +194,11 @@ type SessionEmitterEvents = {
194
194
  auth: (auth: MiniappAuthState) => void
195
195
  }
196
196
 
197
- export class MiniappSession {
197
+ // The default preserves the pre-channel-registry behavior for code that creates
198
+ // or accepts a bare MiniappSession. `registerMiniapp<Channels>` supplies the
199
+ // concrete mapping for scaffolded miniapps.
200
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
201
+ export class MiniappSession<TChannels extends object = any> {
198
202
  public readonly auth: AuthModule
199
203
  public readonly display: DisplayManager
200
204
  /**
@@ -238,7 +242,7 @@ export class MiniappSession {
238
242
  * with inverted buffering policy (background drops when no WebView is
239
243
  * bound; the WebView buffers until ready).
240
244
  */
241
- public readonly ui: UIModule
245
+ public readonly ui: UIModule<TChannels>
242
246
  /**
243
247
  * Inter-miniapp lifecycle + discovery (list / start / stop). SYSTEM-only —
244
248
  * calls reject with NOT_PERMITTED unless this miniapp is a system app.
@@ -324,7 +328,7 @@ export class MiniappSession {
324
328
  this.system = new SystemModule(this)
325
329
  this.transcription = new TranscriptionModule(this)
326
330
  this.translation = new TranslationModule(this)
327
- this.ui = new UIModuleImpl(this)
331
+ this.ui = new UIModuleImpl<TChannels>(this)
328
332
  this.miniapps = new MiniappsModule(this)
329
333
  this.actions = new ActionsModule(this)
330
334
  }
@@ -23,12 +23,23 @@
23
23
  */
24
24
 
25
25
  // ----- @mentra/miniapp/background ---------------------------------------
26
- import type {MiniappSession} from "./background/index"
26
+ import {registerMiniapp} from "./background/index"
27
+ import type {MiniappSession, UIModule} from "./background/index"
27
28
 
28
29
  // ✅ MiniappSession is reachable from the background entry.
29
30
  const _bg: MiniappSession | undefined = undefined
30
31
  void _bg
31
32
 
33
+ // ✅ registerMiniapp<Channels> carries the shared channel registry into
34
+ // session.ui on the background side.
35
+ interface _TestChannels {
36
+ ping: {at: number}
37
+ }
38
+ type _RegisteredHandler = Parameters<typeof registerMiniapp<_TestChannels>>[0]
39
+ type _RegisteredSession = Parameters<_RegisteredHandler>[0]
40
+ type _Assert<T extends true> = T
41
+ export type _TypedBackground = _Assert<_RegisteredSession["ui"] extends UIModule<_TestChannels> ? true : false>
42
+
32
43
  // ----- @mentra/miniapp/ui -----------------------------------------------
33
44
  import type {MentraUiGlobal, MentraTyped} from "./ui/index"
34
45